diff -Nru bluez-4.101/acinclude.m4 bluez-5.23/acinclude.m4 --- bluez-4.101/acinclude.m4 2012-06-22 16:36:49.000000000 +0000 +++ bluez-5.23/acinclude.m4 2013-12-10 06:59:06.000000000 +0000 @@ -21,381 +21,36 @@ with_cflags="$with_cflags -Wredundant-decls" with_cflags="$with_cflags -Wcast-align" with_cflags="$with_cflags -DG_DISABLE_DEPRECATED" + with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28" + with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28" fi - AC_SUBST([WARNING_CFLAGS], $with_cflags) ]) -AC_DEFUN([AC_FUNC_PPOLL], [ - AC_CHECK_FUNC(ppoll, dummy=yes, AC_DEFINE(NEED_PPOLL, 1, - [Define to 1 if you need the ppoll() function.])) -]) - -AC_DEFUN([AC_INIT_BLUEZ], [ - AC_PREFIX_DEFAULT(/usr/local) - - if (test "${prefix}" = "NONE"); then - dnl no prefix and no sysconfdir, so default to /etc - if (test "$sysconfdir" = '${prefix}/etc'); then - AC_SUBST([sysconfdir], ['/etc']) - fi - - dnl no prefix and no localstatedir, so default to /var - if (test "$localstatedir" = '${prefix}/var'); then - AC_SUBST([localstatedir], ['/var']) - fi - - dnl no prefix and no libexecdir, so default to /lib - if (test "$libexecdir" = '${exec_prefix}/libexec'); then - AC_SUBST([libexecdir], ['/lib']) - fi - - dnl no prefix and no mandir, so use ${prefix}/share/man as default - if (test "$mandir" = '${prefix}/man'); then - AC_SUBST([mandir], ['${prefix}/share/man']) +AC_DEFUN([MISC_FLAGS], [ + misc_cflags="" + misc_ldflags="" + AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], + [disable code optimization through compiler]), [ + if (test "${enableval}" = "no"); then + misc_cflags="$misc_cflags -O0" fi - - prefix="${ac_default_prefix}" - fi - - if (test "${libdir}" = '${exec_prefix}/lib'); then - libdir="${prefix}/lib" - fi - - plugindir="${libdir}/bluetooth/plugins" - - if (test "$sysconfdir" = '${prefix}/etc'); then - configdir="${prefix}/etc/bluetooth" - else - configdir="${sysconfdir}/bluetooth" - fi - - if (test "$localstatedir" = '${prefix}/var'); then - storagedir="${prefix}/var/lib/bluetooth" - else - storagedir="${localstatedir}/lib/bluetooth" - fi - - AC_DEFINE_UNQUOTED(CONFIGDIR, "${configdir}", - [Directory for the configuration files]) - AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}", - [Directory for the storage files]) - - AC_SUBST(CONFIGDIR, "${configdir}") - AC_SUBST(STORAGEDIR, "${storagedir}") - - UDEV_DIR="`$PKG_CONFIG --variable=udevdir udev`" - if (test -z "${UDEV_DIR}"); then - UDEV_DIR="/lib/udev" - fi - AC_SUBST(UDEV_DIR) -]) - -AC_DEFUN([AC_PATH_DBUS], [ - PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.4, dummy=yes, - AC_MSG_ERROR(D-Bus >= 1.4 is required)) - AC_SUBST(DBUS_CFLAGS) - AC_SUBST(DBUS_LIBS) -]) - -AC_DEFUN([AC_PATH_GLIB], [ - PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes, - AC_MSG_ERROR(GLib >= 2.28 is required)) - AC_SUBST(GLIB_CFLAGS) - AC_SUBST(GLIB_LIBS) -]) - -AC_DEFUN([AC_PATH_GSTREAMER], [ - PKG_CHECK_MODULES(GSTREAMER, gstreamer-0.10 >= 0.10.30 gstreamer-plugins-base-0.10, gstreamer_found=yes, - AC_MSG_WARN(GStreamer library version 0.10.30 or later is required);gstreamer_found=no) - AC_SUBST(GSTREAMER_CFLAGS) - AC_SUBST(GSTREAMER_LIBS) - GSTREAMER_PLUGINSDIR=`$PKG_CONFIG --variable=pluginsdir gstreamer-0.10` - AC_SUBST(GSTREAMER_PLUGINSDIR) -]) - -AC_DEFUN([AC_PATH_ALSA], [ - PKG_CHECK_MODULES(ALSA, alsa, alsa_found=yes, alsa_found=no) - AC_CHECK_LIB(rt, clock_gettime, ALSA_LIBS="$ALSA_LIBS -lrt", alsa_found=no) - AC_SUBST(ALSA_CFLAGS) - AC_SUBST(ALSA_LIBS) -]) - -AC_DEFUN([AC_PATH_USB], [ - PKG_CHECK_MODULES(USB, libusb, usb_found=yes, usb_found=no) - AC_SUBST(USB_CFLAGS) - AC_SUBST(USB_LIBS) - AC_CHECK_LIB(usb, usb_get_busses, dummy=yes, - AC_DEFINE(NEED_USB_GET_BUSSES, 1, - [Define to 1 if you need the usb_get_busses() function.])) - AC_CHECK_LIB(usb, usb_interrupt_read, dummy=yes, - AC_DEFINE(NEED_USB_INTERRUPT_READ, 1, - [Define to 1 if you need the usb_interrupt_read() function.])) -]) - -AC_DEFUN([AC_PATH_UDEV], [ - PKG_CHECK_MODULES(UDEV, libudev, udev_found=yes, udev_found=no) - AC_SUBST(UDEV_CFLAGS) - AC_SUBST(UDEV_LIBS) -]) - -AC_DEFUN([AC_PATH_SNDFILE], [ - PKG_CHECK_MODULES(SNDFILE, sndfile, sndfile_found=yes, sndfile_found=no) - AC_SUBST(SNDFILE_CFLAGS) - AC_SUBST(SNDFILE_LIBS) -]) - -AC_DEFUN([AC_PATH_READLINE], [ - AC_CHECK_HEADER(readline/readline.h, - AC_CHECK_LIB(readline, main, - [ readline_found=yes - AC_SUBST(READLINE_LIBS, "-lreadline") - ], readline_found=no), - []) -]) - -AC_DEFUN([AC_PATH_CHECK], [ - PKG_CHECK_MODULES(CHECK, check >= 0.9.6, check_found=yes, check_found=no) - AC_SUBST(CHECK_CFLAGS) - AC_SUBST(CHECK_LIBS) -]) - -AC_DEFUN([AC_PATH_OUI], [ - AC_ARG_WITH(ouifile, - AS_HELP_STRING([--with-ouifile=PATH],[Path to the oui.txt file @<:@auto@:>@]), - [ac_with_ouifile=$withval], - [ac_with_ouifile="/var/lib/misc/oui.txt"]) - AC_DEFINE_UNQUOTED(OUIFILE, ["$ac_with_ouifile"], [Define the OUI file path]) -]) - -AC_DEFUN([AC_ARG_BLUEZ], [ - debug_enable=no - optimization_enable=yes - fortify_enable=yes - pie_enable=yes - sndfile_enable=${sndfile_found} - hal_enable=no - usb_enable=${usb_found} - alsa_enable=${alsa_found} - gstreamer_enable=${gstreamer_found} - audio_enable=yes - input_enable=yes - serial_enable=yes - network_enable=yes - sap_enable=no - service_enable=yes - health_enable=no - pnat_enable=no - tools_enable=yes - hidd_enable=no - pand_enable=no - dund_enable=no - cups_enable=no - test_enable=no - bccmd_enable=no - pcmcia_enable=no - hid2hci_enable=no - dfutool_enable=no - datafiles_enable=yes - telephony_driver=dummy - maemo6_enable=no - sap_driver=dummy - dbusoob_enable=no - wiimote_enable=no - gatt_enable=no - - AC_ARG_ENABLE(optimization, AC_HELP_STRING([--disable-optimization], [disable code optimization]), [ - optimization_enable=${enableval} - ]) - - AC_ARG_ENABLE(fortify, AC_HELP_STRING([--disable-fortify], [disable compile time buffer checks]), [ - fortify_enable=${enableval} - ]) - - AC_ARG_ENABLE(pie, AC_HELP_STRING([--disable-pie], [disable position independent executables flag]), [ - pie_enable=${enableval} - ]) - - AC_ARG_ENABLE(network, AC_HELP_STRING([--disable-network], [disable network plugin]), [ - network_enable=${enableval} - ]) - - AC_ARG_ENABLE(sap, AC_HELP_STRING([--enable-sap], [enable sap plugin]), [ - sap_enable=${enableval} - ]) - - AC_ARG_WITH(sap, AC_HELP_STRING([--with-sap=DRIVER], [select SAP driver]), [ - sap_driver=${withval} - ]) - AC_SUBST([SAP_DRIVER], [sap-${sap_driver}.c]) - - AC_ARG_ENABLE(serial, AC_HELP_STRING([--disable-serial], [disable serial plugin]), [ - serial_enable=${enableval} - ]) - - AC_ARG_ENABLE(input, AC_HELP_STRING([--disable-input], [disable input plugin]), [ - input_enable=${enableval} - ]) - - AC_ARG_ENABLE(audio, AC_HELP_STRING([--disable-audio], [disable audio plugin]), [ - audio_enable=${enableval} ]) - - AC_ARG_ENABLE(service, AC_HELP_STRING([--disable-service], [disable service plugin]), [ - service_enable=${enableval} - ]) - - AC_ARG_ENABLE(health, AC_HELP_STRING([--enable-health], [enable health plugin]), [ - health_enable=${enableval} - ]) - - AC_ARG_ENABLE(pnat, AC_HELP_STRING([--enable-pnat], [enable pnat plugin]), [ - pnat_enable=${enableval} - ]) - - AC_ARG_ENABLE(gstreamer, AC_HELP_STRING([--enable-gstreamer], [enable GStreamer support]), [ - gstreamer_enable=${enableval} - ]) - - AC_ARG_ENABLE(alsa, AC_HELP_STRING([--enable-alsa], [enable ALSA support]), [ - alsa_enable=${enableval} - ]) - - AC_ARG_ENABLE(usb, AC_HELP_STRING([--enable-usb], [enable USB support]), [ - usb_enable=${enableval} - ]) - - AC_ARG_ENABLE(tools, AC_HELP_STRING([--enable-tools], [install Bluetooth utilities]), [ - tools_enable=${enableval} - ]) - - AC_ARG_ENABLE(bccmd, AC_HELP_STRING([--enable-bccmd], [install BCCMD interface utility]), [ - bccmd_enable=${enableval} - ]) - - AC_ARG_ENABLE(pcmcia, AC_HELP_STRING([--enable-pcmcia], [install PCMCIA serial script]), [ - pcmcia_enable=${enableval} - ]) - - AC_ARG_ENABLE(hid2hci, AC_HELP_STRING([--enable-hid2hci], [install HID mode switching utility]), [ - hid2hci_enable=${enableval} - ]) - - AC_ARG_ENABLE(dfutool, AC_HELP_STRING([--enable-dfutool], [install DFU firmware upgrade utility]), [ - dfutool_enable=${enableval} - ]) - - AC_ARG_ENABLE(hidd, AC_HELP_STRING([--enable-hidd], [install HID daemon]), [ - hidd_enable=${enableval} - ]) - - AC_ARG_ENABLE(pand, AC_HELP_STRING([--enable-pand], [install PAN daemon]), [ - pand_enable=${enableval} - ]) - - AC_ARG_ENABLE(dund, AC_HELP_STRING([--enable-dund], [install DUN daemon]), [ - dund_enable=${enableval} - ]) - - AC_ARG_ENABLE(cups, AC_HELP_STRING([--enable-cups], [install CUPS backend support]), [ - cups_enable=${enableval} - ]) - - AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test], [install test programs]), [ - test_enable=${enableval} - ]) - - AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--enable-datafiles], [install Bluetooth configuration and data files]), [ - datafiles_enable=${enableval} - ]) - - AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable compiling with debugging information]), [ - debug_enable=${enableval} - ]) - - AC_ARG_WITH(telephony, AC_HELP_STRING([--with-telephony=DRIVER], [select telephony driver]), [ - telephony_driver=${withval} - ]) - - AC_SUBST([TELEPHONY_DRIVER], [telephony-${telephony_driver}.c]) - - AC_ARG_ENABLE(maemo6, AC_HELP_STRING([--enable-maemo6], [compile with maemo6 plugin]), [ - maemo6_enable=${enableval} - ]) - - AC_ARG_ENABLE(dbusoob, AC_HELP_STRING([--enable-dbusoob], [compile with D-Bus OOB plugin]), [ - dbusoob_enable=${enableval} - ]) - - AC_ARG_ENABLE(wiimote, AC_HELP_STRING([--enable-wiimote], [compile with Wii Remote plugin]), [ - wiimote_enable=${enableval} - ]) - - AC_ARG_ENABLE(hal, AC_HELP_STRING([--enable-hal], [Use HAL to determine adapter class]), [ - hal_enable=${enableval} + AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], + [enable compiling with debugging information]), [ + if (test "${enableval}" = "yes" && + test "${ac_cv_prog_cc_g}" = "yes"); then + misc_cflags="$misc_cflags -g" + fi ]) - - AC_ARG_ENABLE(gatt, AC_HELP_STRING([--enable-gatt], [enable gatt module]), [ - gatt_enable=${enableval} + AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie], + [enable position independent executables flag]), [ + if (test "${enableval}" = "yes" && + test "${ac_cv_prog_cc_pie}" = "yes"); then + misc_cflags="$misc_cflags -fPIC" + misc_ldflags="$misc_ldflags -pie" + fi ]) - - misc_cflags="" - misc_ldflags="" - - if (test "${fortify_enable}" = "yes"); then - misc_cflags="$misc_cflags -D_FORTIFY_SOURCE=2" - fi - - if (test "${pie_enable}" = "yes" && test "${ac_cv_prog_cc_pie}" = "yes"); then - misc_cflags="$misc_cflags -fPIC" - misc_ldflags="$misc_ldflags -pie" - fi - - if (test "${debug_enable}" = "yes" && test "${ac_cv_prog_cc_g}" = "yes"); then - misc_cflags="$misc_cflags -g" - fi - - if (test "${optimization_enable}" = "no"); then - misc_cflags="$misc_cflags -O0" - fi - AC_SUBST([MISC_CFLAGS], $misc_cflags) AC_SUBST([MISC_LDFLAGS], $misc_ldflags) - - if (test "${usb_enable}" = "yes" && test "${usb_found}" = "yes"); then - AC_DEFINE(HAVE_LIBUSB, 1, [Define to 1 if you have USB library.]) - fi - - AM_CONDITIONAL(SNDFILE, test "${sndfile_enable}" = "yes" && test "${sndfile_found}" = "yes") - AM_CONDITIONAL(USB, test "${usb_enable}" = "yes" && test "${usb_found}" = "yes") - AM_CONDITIONAL(SBC, test "${alsa_enable}" = "yes" || test "${gstreamer_enable}" = "yes" || - test "${test_enable}" = "yes") - AM_CONDITIONAL(ALSA, test "${alsa_enable}" = "yes" && test "${alsa_found}" = "yes") - AM_CONDITIONAL(GSTREAMER, test "${gstreamer_enable}" = "yes" && test "${gstreamer_found}" = "yes") - AM_CONDITIONAL(AUDIOPLUGIN, test "${audio_enable}" = "yes") - AM_CONDITIONAL(INPUTPLUGIN, test "${input_enable}" = "yes") - AM_CONDITIONAL(SERIALPLUGIN, test "${serial_enable}" = "yes") - AM_CONDITIONAL(NETWORKPLUGIN, test "${network_enable}" = "yes") - AM_CONDITIONAL(SAPPLUGIN, test "${sap_enable}" = "yes") - AM_CONDITIONAL(SERVICEPLUGIN, test "${service_enable}" = "yes") - AM_CONDITIONAL(HEALTHPLUGIN, test "${health_enable}" = "yes") - AM_CONDITIONAL(MCAP, test "${health_enable}" = "yes") - AM_CONDITIONAL(HAL, test "${hal_enable}" = "yes") - AM_CONDITIONAL(READLINE, test "${readline_found}" = "yes") - AM_CONDITIONAL(PNATPLUGIN, test "${pnat_enable}" = "yes") - AM_CONDITIONAL(HIDD, test "${hidd_enable}" = "yes") - AM_CONDITIONAL(PAND, test "${pand_enable}" = "yes") - AM_CONDITIONAL(DUND, test "${dund_enable}" = "yes") - AM_CONDITIONAL(CUPS, test "${cups_enable}" = "yes") - AM_CONDITIONAL(TEST, test "${test_enable}" = "yes" && test "${check_found}" = "yes") - AM_CONDITIONAL(TOOLS, test "${tools_enable}" = "yes") - AM_CONDITIONAL(BCCMD, test "${bccmd_enable}" = "yes") - AM_CONDITIONAL(PCMCIA, test "${pcmcia_enable}" = "yes") - AM_CONDITIONAL(HID2HCI, test "${hid2hci_enable}" = "yes" && test "${usb_found}" = "yes" && test "${udev_found}" = "yes") - AM_CONDITIONAL(DFUTOOL, test "${dfutool_enable}" = "yes" && test "${usb_found}" = "yes") - AM_CONDITIONAL(DATAFILES, test "${datafiles_enable}" = "yes") - AM_CONDITIONAL(MAEMO6PLUGIN, test "${maemo6_enable}" = "yes") - AM_CONDITIONAL(DBUSOOBPLUGIN, test "${dbusoob_enable}" = "yes") - AM_CONDITIONAL(WIIMOTEPLUGIN, test "${wiimote_enable}" = "yes") - AM_CONDITIONAL(GATTMODULES, test "${gatt_enable}" = "yes") ]) diff -Nru bluez-4.101/aclocal.m4 bluez-5.23/aclocal.m4 --- bluez-4.101/aclocal.m4 2012-06-22 16:37:20.000000000 +0000 +++ bluez-5.23/aclocal.m4 2014-09-07 23:25:43.000000000 +0000 @@ -1,4 +1,4 @@ -# generated automatically by aclocal 1.11.3 -*- Autoconf -*- +# generated automatically by aclocal 1.11.6 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, @@ -2526,17 +2526,6 @@ esac ;; -gnu*) - version_type=linux # correct to gnu/linux during the next big refactor - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no @@ -2653,7 +2642,7 @@ ;; # This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu) +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no @@ -3269,10 +3258,6 @@ fi ;; -gnu*) - lt_cv_deplibs_check_method=pass_all - ;; - haiku*) lt_cv_deplibs_check_method=pass_all ;; @@ -3311,7 +3296,7 @@ ;; # This must be glibc/ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu) +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; @@ -4063,7 +4048,7 @@ ;; esac ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler @@ -4362,7 +4347,7 @@ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64 which still supported -KPIC. ecc*) @@ -6251,9 +6236,6 @@ _LT_TAGVAR(ld_shlibs, $1)=yes ;; - gnu*) - ;; - haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes @@ -6415,7 +6397,7 @@ _LT_TAGVAR(inherit_rpath, $1)=yes ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler @@ -8804,7 +8786,7 @@ [am__api_version='1.11' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.11.3], [], +m4_if([$1], [1.11.6], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -8820,7 +8802,7 @@ # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.11.3])dnl +[AM_AUTOMAKE_VERSION([1.11.6])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) @@ -9182,18 +9164,6 @@ [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) ]) -# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 -# Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# serial 8 - -# AM_CONFIG_HEADER is obsolete. It has been replaced by AC_CONFIG_HEADERS. -AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)]) - # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, @@ -9378,27 +9348,6 @@ rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) -# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 -# Free Software Foundation, Inc. -# -# This file is free software; the Free Software Foundation -# gives unlimited permission to copy and/or distribute it, -# with or without modifications, as long as this notice is preserved. - -# serial 5 - -# AM_PROG_LEX -# ----------- -# Autoconf leaves LEX=: if lex or flex can't be found. Change that to a -# "missing" invocation, for better error output. -AC_DEFUN([AM_PROG_LEX], -[AC_PREREQ(2.50)dnl -AC_REQUIRE([AM_MISSING_HAS_RUN])dnl -AC_REQUIRE([AC_PROG_LEX])dnl -if test "$LEX" = :; then - LEX=${am_missing_run}flex -fi]) - # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering @@ -9632,6 +9581,25 @@ AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) +# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 1 + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 diff -Nru bluez-4.101/alert/main.c bluez-5.23/alert/main.c --- bluez-4.101/alert/main.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/alert/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2011 Nokia Corporation - * Copyright (C) 2011 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include "plugin.h" -#include "hcid.h" -#include "log.h" -#include "server.h" - -static int alert_init(void) -{ - if (!main_opts.gatt_enabled) { - DBG("GATT is disabled"); - return -ENOTSUP; - } - - return alert_server_init(); -} - -static void alert_exit(void) -{ - if (!main_opts.gatt_enabled) - return; - - alert_server_exit(); -} - -BLUETOOTH_PLUGIN_DEFINE(alert, VERSION, - BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, - alert_init, alert_exit) diff -Nru bluez-4.101/alert/server.c bluez-5.23/alert/server.c --- bluez-4.101/alert/server.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/alert/server.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2011 Nokia Corporation - * Copyright (C) 2011 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "server.h" - -int alert_server_init(void) -{ - return 0; -} - -void alert_server_exit(void) -{ -} diff -Nru bluez-4.101/alert/server.h bluez-5.23/alert/server.h --- bluez-4.101/alert/server.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/alert/server.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2011 Nokia Corporation - * Copyright (C) 2011 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -int alert_server_init(void); -void alert_server_exit(void); diff -Nru bluez-4.101/android/a2dp.c bluez-5.23/android/a2dp.c --- bluez-4.101/android/a2dp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/a2dp.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,1764 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "btio/btio.h" +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "profiles/audio/a2dp-codecs.h" +#include "src/log.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "a2dp.h" +#include "utils.h" +#include "bluetooth.h" +#include "avdtp.h" +#include "avrcp.h" +#include "audio-msg.h" + +#define SVC_HINT_CAPTURING 0x08 +#define IDLE_TIMEOUT 1 +#define AUDIO_RETRY_TIMEOUT 2 + +static GIOChannel *server = NULL; +static GSList *devices = NULL; +static GSList *endpoints = NULL; +static GSList *setups = NULL; +static bdaddr_t adapter_addr; +static uint32_t record_id = 0; +static guint audio_retry_id = 0; +static bool audio_retrying = false; + +static struct ipc *hal_ipc = NULL; +static struct ipc *audio_ipc = NULL; + +struct a2dp_preset { + void *data; + int8_t len; +}; + +struct a2dp_endpoint { + uint8_t id; + uint8_t codec; + struct avdtp_local_sep *sep; + struct a2dp_preset *caps; + GSList *presets; +}; + +struct a2dp_device { + bdaddr_t dst; + uint8_t state; + GIOChannel *io; + struct avdtp *session; + guint idle_id; +}; + +struct a2dp_setup { + struct a2dp_device *dev; + struct a2dp_endpoint *endpoint; + struct a2dp_preset *preset; + struct avdtp_stream *stream; + uint8_t state; +}; + +static int device_cmp(gconstpointer s, gconstpointer user_data) +{ + const struct a2dp_device *dev = s; + const bdaddr_t *dst = user_data; + + return bacmp(&dev->dst, dst); +} + +static void preset_free(void *data) +{ + struct a2dp_preset *preset = data; + + g_free(preset->data); + g_free(preset); +} + +static void unregister_endpoint(void *data) +{ + struct a2dp_endpoint *endpoint = data; + + if (endpoint->sep) + avdtp_unregister_sep(endpoint->sep); + + if (endpoint->caps) + preset_free(endpoint->caps); + + g_slist_free_full(endpoint->presets, preset_free); + + g_free(endpoint); +} + +static void setup_free(void *data) +{ + struct a2dp_setup *setup = data; + + if (!g_slist_find(setup->endpoint->presets, setup->preset)) + preset_free(setup->preset); + + g_free(setup); +} + +static void setup_remove(struct a2dp_setup *setup) +{ + setups = g_slist_remove(setups, setup); + setup_free(setup); +} + +static void setup_remove_all_by_dev(struct a2dp_device *dev) +{ + GSList *l = setups; + + while (l) { + struct a2dp_setup *setup = l->data; + GSList *next = g_slist_next(l); + + if (setup->dev == dev) + setup_remove(setup); + + l = next; + } +} + +static void a2dp_device_free(void *data) +{ + struct a2dp_device *dev = data; + + if (dev->idle_id > 0) + g_source_remove(dev->idle_id); + + if (dev->session) + avdtp_unref(dev->session); + + if (dev->io) { + g_io_channel_shutdown(dev->io, FALSE, NULL); + g_io_channel_unref(dev->io); + } + + setup_remove_all_by_dev(dev); + + g_free(dev); +} + +static void a2dp_device_remove(struct a2dp_device *dev) +{ + devices = g_slist_remove(devices, dev); + a2dp_device_free(dev); +} + +static struct a2dp_device *a2dp_device_new(const bdaddr_t *dst) +{ + struct a2dp_device *dev; + + dev = g_new0(struct a2dp_device, 1); + bacpy(&dev->dst, dst); + devices = g_slist_prepend(devices, dev); + + return dev; +} + +static bool a2dp_device_connect(struct a2dp_device *dev, BtIOConnect cb) +{ + GError *err = NULL; + + dev->io = bt_io_connect(cb, dev, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + return false; + } + + return true; +} + +static void bt_a2dp_notify_state(struct a2dp_device *dev, uint8_t state) +{ + struct hal_ev_a2dp_conn_state ev; + char address[18]; + + if (dev->state == state) + return; + + dev->state = state; + + ba2str(&dev->dst, address); + DBG("device %s state %u", address, state); + + bdaddr2android(&dev->dst, ev.bdaddr); + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_CONN_STATE, + sizeof(ev), &ev); + + if (state != HAL_A2DP_STATE_DISCONNECTED) + return; + + bt_avrcp_disconnect(&dev->dst); + + a2dp_device_remove(dev); +} + +static void bt_audio_notify_state(struct a2dp_setup *setup, uint8_t state) +{ + struct hal_ev_a2dp_audio_state ev; + char address[18]; + + if (setup->state == state) + return; + + setup->state = state; + + ba2str(&setup->dev->dst, address); + DBG("device %s state %u", address, state); + + bdaddr2android(&setup->dev->dst, ev.bdaddr); + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_EV_A2DP_AUDIO_STATE, + sizeof(ev), &ev); +} + +static void disconnect_cb(void *user_data) +{ + struct a2dp_device *dev = user_data; + + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); +} + +static int sbc_check_config(void *caps, uint8_t caps_len, void *conf, + uint8_t conf_len) +{ + a2dp_sbc_t *cap, *config; + + if (conf_len != caps_len || conf_len != sizeof(a2dp_sbc_t)) { + error("SBC: Invalid configuration size (%u)", conf_len); + return -EINVAL; + } + + cap = caps; + config = conf; + + if (!(cap->frequency & config->frequency)) { + error("SBC: Unsupported frequency (%u) by endpoint", + config->frequency); + return -EINVAL; + } + + if (!(cap->channel_mode & config->channel_mode)) { + error("SBC: Unsupported channel mode (%u) by endpoint", + config->channel_mode); + return -EINVAL; + } + + if (!(cap->block_length & config->block_length)) { + error("SBC: Unsupported block length (%u) by endpoint", + config->block_length); + return -EINVAL; + } + + if (!(cap->allocation_method & config->allocation_method)) { + error("SBC: Unsupported allocation method (%u) by endpoint", + config->block_length); + return -EINVAL; + } + + if (config->max_bitpool < cap->min_bitpool) { + error("SBC: Invalid maximun bitpool (%u < %u)", + config->max_bitpool, cap->min_bitpool); + return -EINVAL; + } + + if (config->min_bitpool > cap->max_bitpool) { + error("SBC: Invalid minimun bitpool (%u > %u)", + config->min_bitpool, cap->min_bitpool); + return -EINVAL; + } + + if (config->max_bitpool > cap->max_bitpool) + return -ERANGE; + + if (config->min_bitpool < cap->min_bitpool) + return -ERANGE; + + return 0; +} + +static int aac_check_config(void *caps, uint8_t caps_len, void *conf, + uint8_t conf_len) +{ + a2dp_aac_t *cap, *config; + + if (conf_len != caps_len || conf_len != sizeof(a2dp_aac_t)) { + error("AAC: Invalid configuration size (%u)", conf_len); + return -EINVAL; + } + + cap = caps; + config = conf; + + if (!(cap->object_type & config->object_type)) { + error("AAC: Unsupported object type (%u) by endpoint", + config->object_type); + return -EINVAL; + } + + if (!(AAC_GET_FREQUENCY(*cap) & AAC_GET_FREQUENCY(*config))) { + error("AAC: Unsupported frequency (%u) by endpoint", + AAC_GET_FREQUENCY(*config)); + return -EINVAL; + } + + if (!(cap->channels & config->channels)) { + error("AAC: Unsupported channels (%u) by endpoint", + config->channels); + return -EINVAL; + } + + /* VBR support in SNK is mandatory but let's make sure we don't try to + * have VBR on remote which for some reason does not support it + */ + if (!cap->vbr && config->vbr) { + error("AAC: Unsupported VBR (%u) by endpoint", + config->vbr); + return -EINVAL; + } + + if (AAC_GET_BITRATE(*cap) < AAC_GET_BITRATE(*config)) + return -ERANGE; + + return 0; +} + +static int aptx_check_config(void *caps, uint8_t caps_len, void *conf, + uint8_t conf_len) +{ + a2dp_aptx_t *cap, *config; + + if (conf_len != caps_len || conf_len != sizeof(a2dp_aptx_t)) { + error("APTX: Invalid configuration size (%u)", conf_len); + return -EINVAL; + } + + cap = caps; + config = conf; + + if (!(cap->frequency & config->frequency)) { + error("APTX: Unsupported frequenct (%u) by endpoint", + config->frequency); + return -EINVAL; + } + + if (!(cap->channel_mode & config->channel_mode)) { + error("APTX: Unsupported channel mode (%u) by endpoint", + config->channel_mode); + return -EINVAL; + } + + return 0; +} + +static int check_capabilities(struct a2dp_preset *preset, + struct avdtp_media_codec_capability *codec, + uint8_t codec_len) +{ + a2dp_vendor_codec_t *vndcodec; + + /* Codec specific */ + switch (codec->media_codec_type) { + case A2DP_CODEC_SBC: + return sbc_check_config(codec->data, codec_len, preset->data, + preset->len); + case A2DP_CODEC_MPEG24: + return aac_check_config(codec->data, codec_len, preset->data, + preset->len); + case A2DP_CODEC_VENDOR: + vndcodec = (void *) codec->data; + if (btohl(vndcodec->vendor_id) == APTX_VENDOR_ID && + btohs(vndcodec->codec_id) == APTX_CODEC_ID) + return aptx_check_config(codec->data, codec_len, + preset->data, preset->len); + return -EINVAL; + default: + return -EINVAL; + } +} + +static struct a2dp_preset *sbc_select_range(void *caps, uint8_t caps_len, + void *conf, uint8_t conf_len) +{ + struct a2dp_preset *p; + a2dp_sbc_t *cap, *config; + + cap = caps; + config = conf; + + config->min_bitpool = MAX(config->min_bitpool, cap->min_bitpool); + config->max_bitpool = MIN(config->max_bitpool, cap->max_bitpool); + + p = g_new0(struct a2dp_preset, 1); + p->len = conf_len; + p->data = g_memdup(conf, p->len); + + return p; +} + +static struct a2dp_preset *aac_select_range(void *caps, uint8_t caps_len, + void *conf, uint8_t conf_len) +{ + struct a2dp_preset *p; + a2dp_aac_t *cap, *config; + uint32_t bitrate; + + cap = caps; + config = conf; + + bitrate = MIN(AAC_GET_BITRATE(*cap), AAC_GET_BITRATE(*config)); + AAC_SET_BITRATE(*config, bitrate); + + p = g_new0(struct a2dp_preset, 1); + p->len = conf_len; + p->data = g_memdup(conf, p->len); + + return p; +} + +static struct a2dp_preset *select_preset_range(struct a2dp_preset *preset, + struct avdtp_media_codec_capability *codec, + uint8_t codec_len) +{ + /* Codec specific */ + switch (codec->media_codec_type) { + case A2DP_CODEC_SBC: + return sbc_select_range(codec->data, codec_len, preset->data, + preset->len); + case A2DP_CODEC_MPEG24: + return aac_select_range(codec->data, codec_len, preset->data, + preset->len); + default: + return NULL; + } +} + +static struct a2dp_preset *select_preset(struct a2dp_endpoint *endpoint, + struct avdtp_remote_sep *rsep) +{ + struct avdtp_service_capability *service; + struct avdtp_media_codec_capability *codec; + GSList *l; + uint8_t codec_len; + + service = avdtp_get_codec(rsep); + codec = (struct avdtp_media_codec_capability *) service->data; + codec_len = service->length - sizeof(*codec); + + for (l = endpoint->presets; l; l = g_slist_next(l)) { + struct a2dp_preset *preset = l->data; + int err; + + err = check_capabilities(preset, codec, codec_len); + if (err == 0) + return preset; + + if (err == -ERANGE) + return select_preset_range(preset, codec, codec_len); + } + + return NULL; +} + +static void setup_add(struct a2dp_device *dev, struct a2dp_endpoint *endpoint, + struct a2dp_preset *preset, struct avdtp_stream *stream) +{ + struct a2dp_setup *setup; + + setup = g_new0(struct a2dp_setup, 1); + setup->dev = dev; + setup->endpoint = endpoint; + setup->preset = preset; + setup->stream = stream; + setups = g_slist_append(setups, setup); + + if (dev->idle_id > 0) { + g_source_remove(dev->idle_id); + dev->idle_id = 0; + } +} + +static int select_configuration(struct a2dp_device *dev, + struct a2dp_endpoint *endpoint, + struct avdtp_remote_sep *rsep) +{ + struct a2dp_preset *preset; + struct avdtp_stream *stream; + struct avdtp_service_capability *service; + struct avdtp_media_codec_capability *codec; + GSList *caps; + int err; + + preset = select_preset(endpoint, rsep); + if (!preset) { + error("Unable to select codec preset"); + return -EINVAL; + } + + service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); + caps = g_slist_append(NULL, service); + + codec = g_malloc0(sizeof(*codec) + preset->len); + codec->media_type = AVDTP_MEDIA_TYPE_AUDIO; + codec->media_codec_type = endpoint->codec; + memcpy(codec->data, preset->data, preset->len); + + service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec, + sizeof(*codec) + preset->len); + caps = g_slist_append(caps, service); + + g_free(codec); + + err = avdtp_set_configuration(dev->session, rsep, endpoint->sep, caps, + &stream); + g_slist_free_full(caps, g_free); + if (err < 0) { + error("avdtp_set_configuration: %s", strerror(-err)); + return err; + } + + setup_add(dev, endpoint, preset, stream); + + return 0; +} + +static void discover_cb(struct avdtp *session, GSList *seps, + struct avdtp_error *err, void *user_data) +{ + struct a2dp_device *dev = user_data; + struct a2dp_endpoint *endpoint = NULL; + struct avdtp_remote_sep *rsep = NULL; + GSList *l; + + for (l = endpoints; l; l = g_slist_next(l)) { + endpoint = l->data; + + rsep = avdtp_find_remote_sep(session, endpoint->sep); + if (rsep) + break; + } + + if (!rsep) { + error("Unable to find matching endpoint"); + goto failed; + } + + if (select_configuration(dev, endpoint, rsep) < 0) + goto failed; + + return; + +failed: + avdtp_shutdown(session); +} + +static gboolean idle_timeout(gpointer user_data) +{ + struct a2dp_device *dev = user_data; + int err; + + dev->idle_id = 0; + + err = avdtp_discover(dev->session, discover_cb, dev); + if (err == 0) + return FALSE; + + error("avdtp_discover: %s", strerror(-err)); + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); + + return FALSE; +} + +static void signaling_connect_cb(GIOChannel *chan, GError *err, + gpointer user_data) +{ + struct a2dp_device *dev = user_data; + uint16_t imtu, omtu; + GError *gerr = NULL; + int fd; + + if (err) { + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); + error("%s", err->message); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + goto failed; + } + + fd = g_io_channel_unix_get_fd(chan); + + /* FIXME: Add proper version */ + dev->session = avdtp_new(fd, imtu, omtu, 0x0100); + if (!dev->session) + goto failed; + + avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev); + + /* Proceed to stream setup if initiator */ + if (dev->io) { + int perr; + + g_io_channel_unref(dev->io); + dev->io = NULL; + + perr = avdtp_discover(dev->session, discover_cb, dev); + if (perr < 0) { + error("avdtp_discover: %s", strerror(-perr)); + goto failed; + } + bt_avrcp_connect(&dev->dst); + } else /* Init idle timeout to discover */ + dev->idle_id = g_timeout_add_seconds(IDLE_TIMEOUT, idle_timeout, + dev); + + return; + +failed: + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); +} + +static void bt_a2dp_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_a2dp_connect *cmd = buf; + struct a2dp_device *dev; + uint8_t status; + char addr[18]; + bdaddr_t dst; + GSList *l; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = a2dp_device_new(&dst); + if (!a2dp_device_connect(dev, signaling_connect_cb)) { + a2dp_device_remove(dev); + status = HAL_STATUS_FAILED; + goto failed; + } + + ba2str(&dev->dst, addr); + DBG("connecting to %s", addr); + + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, status); +} + +static void bt_a2dp_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_a2dp_connect *cmd = buf; + uint8_t status; + struct a2dp_device *dev; + GSList *l; + bdaddr_t dst; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + status = HAL_STATUS_SUCCESS; + + if (dev->io) { + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTED); + goto failed; + } + + /* Wait AVDTP session to shutdown */ + avdtp_shutdown(dev->session); + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_DISCONNECTING); + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, + status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_A2DP_CONNECT */ + { bt_a2dp_connect, false, sizeof(struct hal_cmd_a2dp_connect) }, + /* HAL_OP_A2DP_DISCONNECT */ + { bt_a2dp_disconnect, false, sizeof(struct hal_cmd_a2dp_disconnect) }, +}; + +static struct a2dp_setup *find_setup_by_device(struct a2dp_device *dev) +{ + GSList *l; + + for (l = setups; l; l = g_slist_next(l)) { + struct a2dp_setup *setup = l->data; + + if (setup->dev == dev) + return setup; + } + + return NULL; +} + +static void transport_connect_cb(GIOChannel *chan, GError *err, + gpointer user_data) +{ + struct a2dp_device *dev = user_data; + struct a2dp_setup *setup; + uint16_t imtu, omtu; + GError *gerr = NULL; + int fd; + + if (err) { + error("%s", err->message); + return; + } + + setup = find_setup_by_device(dev); + if (!setup) { + error("Unable to find stream setup"); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + return; + } + + fd = g_io_channel_unix_get_fd(chan); + + if (!avdtp_stream_set_transport(setup->stream, fd, imtu, omtu)) { + error("avdtp_stream_set_transport: failed"); + return; + } + + g_io_channel_set_close_on_unref(chan, FALSE); + + if (dev->io) { + g_io_channel_unref(dev->io); + dev->io = NULL; + } + + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTED); +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + struct a2dp_device *dev; + bdaddr_t dst; + char address[18]; + GError *gerr = NULL; + GSList *l; + + if (err) { + error("%s", err->message); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + ba2str(&dst, address); + DBG("Incoming connection from %s", address); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) { + transport_connect_cb(chan, err, l->data); + return; + } + + dev = a2dp_device_new(&dst); + bt_a2dp_notify_state(dev, HAL_A2DP_STATE_CONNECTING); + signaling_connect_cb(chan, err, dev); +} + +static sdp_record_t *a2dp_record(void) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid; + sdp_profile_desc_t profile[1]; + sdp_list_t *aproto, *proto[2]; + sdp_record_t *record; + sdp_data_t *psm, *version, *features; + uint16_t lp = AVDTP_UUID; + uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &a2dp_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); + profile[0].version = a2dp_ver; + pfseq = sdp_list_append(NULL, &profile[0]); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap_uuid); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto[0] = sdp_list_append(proto[0], psm); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID); + proto[1] = sdp_list_append(NULL, &avdtp_uuid); + version = sdp_data_alloc(SDP_UINT16, &avdtp_ver); + proto[1] = sdp_list_append(proto[1], version); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + features = sdp_data_alloc(SDP_UINT16, &feat); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); + + sdp_set_info_attr(record, "Audio Source", NULL, NULL); + + sdp_data_free(psm); + sdp_data_free(version); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static gboolean sep_getcap_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_preset *cap = endpoint->caps; + struct avdtp_service_capability *service; + struct avdtp_media_codec_capability *codec; + + *caps = NULL; + + service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); + *caps = g_slist_append(*caps, service); + + codec = g_malloc0(sizeof(*codec) + cap->len); + codec->media_type = AVDTP_MEDIA_TYPE_AUDIO; + codec->media_codec_type = endpoint->codec; + memcpy(codec->data, cap->data, cap->len); + + service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec, + sizeof(*codec) + cap->len); + *caps = g_slist_append(*caps, service); + g_free(codec); + + return TRUE; +} + +static int check_config(struct a2dp_endpoint *endpoint, + struct a2dp_preset *config) +{ + GSList *l; + struct a2dp_preset *caps; + + for (l = endpoint->presets; l; l = g_slist_next(l)) { + struct a2dp_preset *preset = l->data; + + if (preset->len != config->len) + continue; + + if (memcmp(preset->data, config->data, preset->len) == 0) + return 0; + } + + caps = endpoint->caps; + + /* Codec specific */ + switch (endpoint->codec) { + case A2DP_CODEC_SBC: + return sbc_check_config(caps->data, caps->len, config->data, + config->len); + default: + return -EINVAL; + } +} + +static struct a2dp_device *find_device_by_session(struct avdtp *session) +{ + GSList *l; + + for (l = devices; l; l = g_slist_next(l)) { + struct a2dp_device *dev = l->data; + + if (dev->session == session) + return dev; + } + + return NULL; +} + +static struct a2dp_setup *find_setup(uint8_t id) +{ + GSList *l; + + for (l = setups; l; l = g_slist_next(l)) { + struct a2dp_setup *setup = l->data; + + if (setup->endpoint->id == id) + return setup; + } + + return NULL; +} + +static void setup_remove_by_id(uint8_t id) +{ + struct a2dp_setup *setup; + + setup = find_setup(id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", id); + return; + } + + setup_remove(setup); +} + +static gboolean sep_setconf_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + GSList *caps, + avdtp_set_configuration_cb cb, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_device *dev; + struct a2dp_preset *preset = NULL; + + DBG(""); + + dev = find_device_by_session(session); + if (!dev) { + error("Unable to find device for session %p", session); + return FALSE; + } + + for (; caps != NULL; caps = g_slist_next(caps)) { + struct avdtp_service_capability *cap = caps->data; + struct avdtp_media_codec_capability *codec; + + if (cap->category == AVDTP_DELAY_REPORTING) + return FALSE; + + if (cap->category != AVDTP_MEDIA_CODEC) + continue; + + codec = (struct avdtp_media_codec_capability *) cap->data; + + if (codec->media_codec_type != endpoint->codec) + return FALSE; + + preset = g_new0(struct a2dp_preset, 1); + preset->len = cap->length - sizeof(*codec); + preset->data = g_memdup(codec->data, preset->len); + + if (check_config(endpoint, preset) < 0) { + preset_free(preset); + return FALSE; + } + } + + if (!preset) + return FALSE; + + setup_add(dev, endpoint, preset, stream); + + cb(session, stream, NULL); + + return TRUE; +} + +static gboolean sep_open_ind(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + *err = AVDTP_SEP_NOT_IN_USE; + return FALSE; + } + + return TRUE; +} + +static gboolean sep_close_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + *err = AVDTP_SEP_NOT_IN_USE; + return FALSE; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); + + setup_remove(setup); + + return TRUE; +} + +static gboolean sep_start_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + *err = AVDTP_SEP_NOT_IN_USE; + return FALSE; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STARTED); + + return TRUE; +} + +static gboolean sep_suspend_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + uint8_t *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + *err = AVDTP_SEP_NOT_IN_USE; + return FALSE; + } + + bt_audio_notify_state(setup, HAL_AUDIO_SUSPEND); + + return TRUE; +} + +static struct avdtp_sep_ind sep_ind = { + .get_capability = sep_getcap_ind, + .set_configuration = sep_setconf_ind, + .open = sep_open_ind, + .close = sep_close_ind, + .start = sep_start_ind, + .suspend = sep_suspend_ind, +}; + +static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + int ret; + + DBG(""); + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for endpoint %u", + endpoint->id); + return; + } + + if (err) + goto failed; + + ret = avdtp_open(session, stream); + if (ret < 0) { + error("avdtp_open: %s", strerror(-ret)); + goto failed; + } + + return; + +failed: + setup_remove(setup); +} + +static void sep_open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_device *dev; + + DBG(""); + + if (err) + goto failed; + + dev = find_device_by_session(session); + if (!dev) { + error("Unable to find device for session"); + goto failed; + } + + a2dp_device_connect(dev, transport_connect_cb); + + return; + +failed: + setup_remove_by_id(endpoint->id); +} + +static void sep_start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + if (err) { + setup_remove_by_id(endpoint->id); + return; + } + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for %u endpoint", + endpoint->id); + return; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STARTED); +} + +static void sep_suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + if (err) { + setup_remove_by_id(endpoint->id); + return; + } + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for %u endpoint", + endpoint->id); + return; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); +} + +static void sep_close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + struct a2dp_setup *setup; + + DBG(""); + + if (err) + return; + + setup = find_setup(endpoint->id); + if (!setup) { + error("Unable to find stream setup for %u endpoint", + endpoint->id); + return; + } + + bt_audio_notify_state(setup, HAL_AUDIO_STOPPED); + + setup_remove(setup); +} + +static void sep_abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + struct a2dp_endpoint *endpoint = user_data; + + DBG(""); + + if (err) + return; + + setup_remove_by_id(endpoint->id); +} + +static struct avdtp_sep_cfm sep_cfm = { + .set_configuration = sep_setconf_cfm, + .open = sep_open_cfm, + .start = sep_start_cfm, + .suspend = sep_suspend_cfm, + .close = sep_close_cfm, + .abort = sep_abort_cfm, +}; + +static uint8_t register_endpoint(const uint8_t *uuid, uint8_t codec, + GSList *presets) +{ + struct a2dp_endpoint *endpoint; + + /* FIXME: Add proper check for uuid */ + + endpoint = g_new0(struct a2dp_endpoint, 1); + endpoint->id = g_slist_length(endpoints) + 1; + endpoint->codec = codec; + endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, + AVDTP_MEDIA_TYPE_AUDIO, + codec, FALSE, &sep_ind, + &sep_cfm, endpoint); + endpoint->caps = presets->data; + endpoint->presets = g_slist_copy(g_slist_nth(presets, 1)); + + if (endpoint->codec == A2DP_CODEC_VENDOR) { + a2dp_vendor_codec_t *vndcodec = (void *) endpoint->caps->data; + + avdtp_sep_set_vendor_codec(endpoint->sep, + btohl(vndcodec->vendor_id), + btohs(vndcodec->codec_id)); + } + + endpoints = g_slist_append(endpoints, endpoint); + + return endpoint->id; +} + +static GSList *parse_presets(const struct audio_preset *p, uint8_t count, + uint16_t len) +{ + GSList *l = NULL; + uint8_t i; + + for (i = 0; count > i; i++) { + const uint8_t *ptr = (const uint8_t *) p; + struct a2dp_preset *preset; + + if (len < sizeof(struct audio_preset)) { + DBG("Invalid preset index %u", i); + g_slist_free_full(l, preset_free); + return NULL; + } + + len -= sizeof(struct audio_preset); + if (len == 0 || len < p->len) { + DBG("Invalid preset size of %u for index %u", len, i); + g_slist_free_full(l, preset_free); + return NULL; + } + + preset = g_new0(struct a2dp_preset, 1); + preset->len = p->len; + preset->data = g_memdup(p->data, preset->len); + l = g_slist_append(l, preset); + + len -= preset->len; + ptr += sizeof(*p) + preset->len; + p = (const struct audio_preset *) ptr; + } + + return l; +} + +static void bt_audio_open(const void *buf, uint16_t len) +{ + const struct audio_cmd_open *cmd = buf; + struct audio_rsp_open rsp; + GSList *presets; + + DBG(""); + + audio_retrying = false; + + if (cmd->presets == 0) { + error("No audio presets found"); + goto failed; + } + + presets = parse_presets(cmd->preset, cmd->presets, len - sizeof(*cmd)); + if (!presets) { + error("No audio presets found"); + goto failed; + } + + rsp.id = register_endpoint(cmd->uuid, cmd->codec, presets); + if (rsp.id == 0) { + g_slist_free_full(presets, preset_free); + error("Unable to register endpoint"); + goto failed; + } + + g_slist_free(presets); + + ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN, + sizeof(rsp), &rsp, -1); + + return; + +failed: + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN, + AUDIO_STATUS_FAILED); +} + +static struct a2dp_endpoint *find_endpoint(uint8_t id) +{ + GSList *l; + + for (l = endpoints; l; l = g_slist_next(l)) { + struct a2dp_endpoint *endpoint = l->data; + + if (endpoint->id == id) + return endpoint; + } + + return NULL; +} + +static void bt_audio_close(const void *buf, uint16_t len) +{ + const struct audio_cmd_close *cmd = buf; + struct a2dp_endpoint *endpoint; + + DBG(""); + + endpoint = find_endpoint(cmd->id); + if (!endpoint) { + error("Unable to find endpoint %u", cmd->id); + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, + AUDIO_STATUS_FAILED); + return; + } + + endpoints = g_slist_remove(endpoints, endpoint); + unregister_endpoint(endpoint); + + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, + AUDIO_STATUS_SUCCESS); +} + +static void bt_stream_open(const void *buf, uint16_t len) +{ + const struct audio_cmd_open_stream *cmd = buf; + struct audio_rsp_open_stream *rsp; + struct a2dp_setup *setup; + int fd; + uint16_t omtu; + + DBG(""); + + if (cmd->id) + setup = find_setup(cmd->id); + else + setup = setups ? setups->data : NULL; + if (!setup) { + error("Unable to find stream for endpoint %u", cmd->id); + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, + AUDIO_STATUS_FAILED); + return; + } + + if (!avdtp_stream_get_transport(setup->stream, &fd, NULL, &omtu, + NULL)) { + error("avdtp_stream_get_transport: failed"); + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, + AUDIO_STATUS_FAILED); + return; + } + + len = sizeof(struct audio_rsp_open_stream) + + sizeof(struct audio_preset) + setup->preset->len; + rsp = g_malloc0(len); + rsp->id = setup->endpoint->id; + rsp->mtu = omtu; + rsp->preset->len = setup->preset->len; + memcpy(rsp->preset->data, setup->preset->data, setup->preset->len); + + ipc_send_rsp_full(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, + len, rsp, fd); + + g_free(rsp); +} + +static void bt_stream_close(const void *buf, uint16_t len) +{ + const struct audio_cmd_close_stream *cmd = buf; + struct a2dp_setup *setup; + int err; + + DBG(""); + + setup = find_setup(cmd->id); + if (!setup) { + error("Unable to find stream for endpoint %u", cmd->id); + goto failed; + } + + err = avdtp_close(setup->dev->session, setup->stream, FALSE); + if (err < 0) { + error("avdtp_close: %s", strerror(-err)); + goto failed; + } + + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, + AUDIO_STATUS_SUCCESS); + + return; + +failed: + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, + AUDIO_STATUS_FAILED); +} + +static void bt_stream_resume(const void *buf, uint16_t len) +{ + const struct audio_cmd_resume_stream *cmd = buf; + struct a2dp_setup *setup; + int err; + + DBG(""); + + setup = find_setup(cmd->id); + if (!setup) { + error("Unable to find stream for endpoint %u", cmd->id); + goto failed; + } + + if (setup->state != HAL_AUDIO_STARTED) { + err = avdtp_start(setup->dev->session, setup->stream); + if (err < 0) { + error("avdtp_start: %s", strerror(-err)); + goto failed; + } + } + + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, + AUDIO_STATUS_SUCCESS); + + return; + +failed: + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, + AUDIO_STATUS_FAILED); +} + +static void bt_stream_suspend(const void *buf, uint16_t len) +{ + const struct audio_cmd_suspend_stream *cmd = buf; + struct a2dp_setup *setup; + int err; + + DBG(""); + + setup = find_setup(cmd->id); + if (!setup) { + error("Unable to find stream for endpoint %u", cmd->id); + goto failed; + } + + err = avdtp_suspend(setup->dev->session, setup->stream); + if (err < 0) { + error("avdtp_suspend: %s", strerror(-err)); + goto failed; + } + + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, + AUDIO_STATUS_SUCCESS); + + return; + +failed: + ipc_send_rsp(audio_ipc, AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, + AUDIO_STATUS_FAILED); +} + +static const struct ipc_handler audio_handlers[] = { + /* AUDIO_OP_OPEN */ + { bt_audio_open, true, sizeof(struct audio_cmd_open) }, + /* AUDIO_OP_CLOSE */ + { bt_audio_close, false, sizeof(struct audio_cmd_close) }, + /* AUDIO_OP_OPEN_STREAM */ + { bt_stream_open, false, sizeof(struct audio_cmd_open_stream) }, + /* AUDIO_OP_CLOSE_STREAM */ + { bt_stream_close, false, sizeof(struct audio_cmd_close_stream) }, + /* AUDIO_OP_RESUME_STREAM */ + { bt_stream_resume, false, sizeof(struct audio_cmd_resume_stream) }, + /* AUDIO_OP_SUSPEND_STREAM */ + { bt_stream_suspend, false, sizeof(struct audio_cmd_suspend_stream) }, +}; + +static void bt_audio_unregister(void) +{ + DBG(""); + + if (audio_retry_id > 0) + g_source_remove(audio_retry_id); + + g_slist_free_full(endpoints, unregister_endpoint); + endpoints = NULL; + + g_slist_free_full(setups, setup_free); + setups = NULL; + + ipc_cleanup(audio_ipc); + audio_ipc = NULL; +} + +static bool bt_audio_register(ipc_disconnect_cb disconnect) +{ + DBG(""); + + audio_ipc = ipc_init(BLUEZ_AUDIO_SK_PATH, sizeof(BLUEZ_AUDIO_SK_PATH), + AUDIO_SERVICE_ID_MAX, false, disconnect, NULL); + if (!audio_ipc) + return false; + + ipc_register(audio_ipc, AUDIO_SERVICE_ID, audio_handlers, + G_N_ELEMENTS(audio_handlers)); + + return true; +} + +static gboolean audio_retry_register(void *data) +{ + ipc_disconnect_cb cb = data; + + audio_retry_id = 0; + audio_retrying = true; + + bt_audio_register(cb); + + return FALSE; +} + +static void audio_disconnected(void *data) +{ + GSList *l; + bool restart; + + DBG(""); + + if (audio_retrying) + goto retry; + + restart = endpoints != NULL ? true : false; + + bt_audio_unregister(); + + for (l = devices; l; l = g_slist_next(l)) { + struct a2dp_device *dev = l->data; + + avdtp_shutdown(dev->session); + } + + if (!restart) + return; + +retry: + audio_retry_id = g_timeout_add_seconds(AUDIO_RETRY_TIMEOUT, + audio_retry_register, + audio_disconnected); +} + +bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + GError *err = NULL; + sdp_record_t *rec; + + DBG(""); + + bacpy(&adapter_addr, addr); + + server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_MASTER, true, + BT_IO_OPT_INVALID); + if (!server) { + error("Failed to listen on AVDTP channel: %s", err->message); + g_error_free(err); + return false; + } + + rec = a2dp_record(); + if (!rec) { + error("Failed to allocate A2DP record"); + goto fail; + } + + if (bt_adapter_add_record(rec, SVC_HINT_CAPTURING) < 0) { + error("Failed to register A2DP record"); + sdp_record_free(rec); + goto fail; + } + record_id = rec->handle; + + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_A2DP, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + if (bt_audio_register(audio_disconnected)) + return true; + +fail: + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + return false; +} + +void bt_a2dp_unregister(void) +{ + DBG(""); + + g_slist_free_full(setups, setup_free); + setups = NULL; + + g_slist_free_full(endpoints, unregister_endpoint); + endpoints = NULL; + + g_slist_free_full(devices, a2dp_device_free); + devices = NULL; + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_A2DP); + hal_ipc = NULL; + + bt_adapter_remove_record(record_id); + record_id = 0; + + if (server) { + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + } + + if (audio_ipc) { + ipc_unregister(audio_ipc, AUDIO_SERVICE_ID); + ipc_cleanup(audio_ipc); + audio_ipc = NULL; + } +} diff -Nru bluez-4.101/android/a2dp.h bluez-5.23/android/a2dp.h --- bluez-4.101/android/a2dp.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/a2dp.h 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +bool bt_a2dp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_a2dp_unregister(void); diff -Nru bluez-4.101/android/Android.mk bluez-5.23/android/Android.mk --- bluez-4.101/android/Android.mk 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/Android.mk 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,725 @@ +LOCAL_PATH := external/bluetooth + +# Retrieve BlueZ version from configure.ac file +BLUEZ_VERSION := `grep "^AC_INIT" $(LOCAL_PATH)/bluez/configure.ac | sed -e "s/.*,.\(.*\))/\1/"` + +ANDROID_VERSION := `echo $(PLATFORM_VERSION) | awk -F. '{ printf "0x%02d%02d%02d",$$1,$$2,$$3 }'` + +# Specify pathmap for glib and sbc +pathmap_INCL += glib:external/bluetooth/glib \ + sbc:external/bluetooth/sbc \ + +# Specify common compiler flags +BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \ + -DANDROID_VERSION=$(ANDROID_VERSION) \ + -DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \ + +# Enable warnings enabled in autotools build +BLUEZ_COMMON_CFLAGS += -Wall -Wextra \ + -Wdeclaration-after-statement \ + -Wmissing-declarations \ + -Wredundant-decls \ + -Wcast-align \ + +# Disable warnings enabled by Android but not enabled in autotools build +BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith \ + -Wno-missing-field-initializers \ + -Wno-unused-parameter \ + +# +# Android BlueZ daemon (bluetoothd) +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/android/main.c \ + bluez/android/bluetooth.c \ + bluez/android/scpp.c \ + bluez/android/dis.c \ + bluez/android/bas.c \ + bluez/android/hog.c \ + bluez/android/hidhost.c \ + bluez/android/socket.c \ + bluez/android/ipc.c \ + bluez/android/avdtp.c \ + bluez/android/a2dp.c \ + bluez/android/avctp.c \ + bluez/android/avrcp.c \ + bluez/android/avrcp-lib.c \ + bluez/android/pan.c \ + bluez/android/handsfree.c \ + bluez/android/gatt.c \ + bluez/android/health.c \ + bluez/android/mcap-lib.c \ + bluez/src/log.c \ + bluez/src/shared/mgmt.c \ + bluez/src/shared/util.c \ + bluez/src/shared/queue.c \ + bluez/src/shared/ringbuf.c \ + bluez/src/shared/hfp.c \ + bluez/src/shared/gatt-db.c \ + bluez/src/shared/io-glib.c \ + bluez/src/shared/crypto.c \ + bluez/src/shared/uhid.c \ + bluez/src/sdpd-database.c \ + bluez/src/sdpd-service.c \ + bluez/src/sdpd-request.c \ + bluez/src/sdpd-server.c \ + bluez/src/uuid-helper.c \ + bluez/src/eir.c \ + bluez/lib/sdp.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + bluez/lib/uuid.c \ + bluez/btio/btio.c \ + bluez/src/sdp-client.c \ + bluez/profiles/network/bnep.c \ + bluez/attrib/gattrib.c \ + bluez/attrib/gatt.c \ + bluez/attrib/att.c + +LOCAL_C_INCLUDES := \ + $(call include-path-for, glib) \ + $(call include-path-for, glib)/glib \ + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_SHARED_LIBRARIES := \ + libglib \ + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_TAGS := optional + +# for userdebug/eng this module is bluetoothd-main since bluetoothd is used as +# wrapper to launch bluetooth with Valgrind +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +LOCAL_MODULE := bluetoothd-main +LOCAL_STRIP_MODULE := false +else +LOCAL_MODULE := bluetoothd +endif + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# bluetooth.default.so HAL +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/android/hal-ipc.c \ + bluez/android/hal-bluetooth.c \ + bluez/android/hal-socket.c \ + bluez/android/hal-hidhost.c \ + bluez/android/hal-pan.c \ + bluez/android/hal-a2dp.c \ + bluez/android/hal-avrcp.c \ + bluez/android/hal-handsfree.c \ + bluez/android/hal-gatt.c \ + bluez/android/hal-utils.c \ + bluez/android/hal-health.c \ + +LOCAL_C_INCLUDES += \ + $(call include-path-for, system-core) \ + $(call include-path-for, libhardware) \ + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE := bluetooth.default +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc + +include $(BUILD_SHARED_LIBRARY) + +# +# haltest +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/android/client/haltest.c \ + bluez/android/client/pollhandler.c \ + bluez/android/client/terminal.c \ + bluez/android/client/history.c \ + bluez/android/client/tabcompletion.c \ + bluez/android/client/if-audio.c \ + bluez/android/client/if-sco.c \ + bluez/android/client/if-av.c \ + bluez/android/client/if-rc.c \ + bluez/android/client/if-bt.c \ + bluez/android/client/if-hf.c \ + bluez/android/client/if-hh.c \ + bluez/android/client/if-pan.c \ + bluez/android/client/if-hl.c \ + bluez/android/client/if-sock.c \ + bluez/android/client/if-gatt.c \ + bluez/android/hal-utils.c \ + +LOCAL_C_INCLUDES += \ + $(call include-path-for, system-core) \ + $(call include-path-for, libhardware) \ + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/bluez/android \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_SHARED_LIBRARIES := libhardware + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := haltest + +include $(BUILD_EXECUTABLE) + +# +# mcaptest +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/src/log.c \ + bluez/btio/btio.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + bluez/android/mcap-lib.c \ + bluez/android/mcaptest.c \ + +LOCAL_C_INCLUDES := \ + $(call include-path-for, glib) \ + $(call include-path-for, glib)/glib \ + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_SHARED_LIBRARIES := \ + libglib \ + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := mcaptest + +include $(BUILD_EXECUTABLE) + +# +# avdtptest +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/android/avdtptest.c \ + bluez/android/avdtp.c \ + bluez/src/log.c \ + bluez/btio/btio.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/bluez \ + $(call include-path-for, glib) \ + $(call include-path-for, glib)/glib \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_SHARED_LIBRARIES := \ + libglib \ + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := avdtptest + +include $(BUILD_EXECUTABLE) + +# +# btmon +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/monitor/main.c \ + bluez/monitor/mainloop.c \ + bluez/monitor/display.c \ + bluez/monitor/hcidump.c \ + bluez/monitor/control.c \ + bluez/monitor/packet.c \ + bluez/monitor/l2cap.c \ + bluez/monitor/avctp.c \ + bluez/monitor/uuid.c \ + bluez/monitor/sdp.c \ + bluez/monitor/vendor.c \ + bluez/monitor/lmp.c \ + bluez/monitor/crc.c \ + bluez/monitor/ll.c \ + bluez/monitor/hwdb.c \ + bluez/monitor/keys.c \ + bluez/monitor/ellisys.c \ + bluez/monitor/analyze.c \ + bluez/src/shared/util.c \ + bluez/src/shared/queue.c \ + bluez/src/shared/crypto.c \ + bluez/src/shared/btsnoop.c \ + bluez/lib/hci.c \ + bluez/lib/bluetooth.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := btmon + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# btproxy +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/btproxy.c \ + bluez/monitor/mainloop.c \ + bluez/src/shared/util.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := btproxy + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# A2DP audio +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/src/shared/queue.c \ + bluez/android/hal-audio.c \ + bluez/android/hal-audio-sbc.c \ + bluez/android/hal-audio-aptx.c \ + +LOCAL_C_INCLUDES = \ + $(LOCAL_PATH)/bluez \ + $(call include-path-for, system-core) \ + $(call include-path-for, libhardware) \ + $(call include-path-for, sbc) \ + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libsbc \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) +LOCAL_LDFLAGS := -ldl + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := audio.a2dp.default + +include $(BUILD_SHARED_LIBRARY) + +# +# SCO audio +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := bluez/android/hal-sco.c + +LOCAL_C_INCLUDES = \ + $(call include-path-for, system-core) \ + $(call include-path-for, libhardware) \ + $(call include-path-for, audio-utils) \ + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libaudioutils \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := audio.sco.default + +include $(BUILD_SHARED_LIBRARY) + +# +# l2cap-test +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/l2test.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := l2test + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# bluetoothd-snoop +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/android/bluetoothd-snoop.c \ + bluez/monitor/mainloop.c \ + bluez/src/shared/btsnoop.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + $(LOCAL_PATH)/bluez/lib \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bluetoothd-snoop + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# init.bluetooth.rc +# + +include $(CLEAR_VARS) + +LOCAL_MODULE := init.bluetooth.rc +LOCAL_MODULE_CLASS := ETC +LOCAL_SRC_FILES := bluez/android/$(LOCAL_MODULE) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) + +include $(BUILD_PREBUILT) + +# +# btmgmt +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/btmgmt.c \ + bluez/lib/bluetooth.c \ + bluez/lib/sdp.c \ + bluez/monitor/mainloop.c \ + bluez/src/shared/io-mainloop.c \ + bluez/src/shared/mgmt.c \ + bluez/src/shared/queue.c \ + bluez/src/shared/util.c \ + bluez/src/uuid-helper.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := btmgmt + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# hcitool +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/hcitool.c \ + bluez/src/oui.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := hcitool + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# l2ping +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/l2ping.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := l2ping + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# avtest +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/avtest.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := avtest + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# hciattach +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/hciattach.c \ + bluez/tools/hciattach_st.c \ + bluez/tools/hciattach_ti.c \ + bluez/tools/hciattach_tialt.c \ + bluez/tools/hciattach_ath3k.c \ + bluez/tools/hciattach_qualcomm.c \ + bluez/tools/hciattach_intel.c \ + bluez/tools/hciattach_bcm43xx.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := hciattach + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# libsbc +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + sbc/sbc/sbc.c \ + sbc/sbc/sbc_primitives.c \ + sbc/sbc/sbc_primitives_mmx.c \ + sbc/sbc/sbc_primitives_neon.c \ + sbc/sbc/sbc_primitives_armv6.c \ + sbc/sbc/sbc_primitives_iwmmxt.c \ + +LOCAL_C_INCLUDES:= \ + $(LOCAL_PATH)/sbc \ + +LOCAL_CFLAGS:= \ + -Os \ + -Wno-sign-compare \ + -Wno-missing-field-initializers \ + -Wno-unused-parameter \ + -Wno-type-limits \ + -Wno-empty-body \ + +LOCAL_MODULE := libsbc + +include $(BUILD_SHARED_LIBRARY) + +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) + +# +# bluetoothd (debug) +# this is just a wrapper used in userdebug/eng to launch bluetoothd-main +# with/without Valgrind +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/android/bluetoothd-wrapper.c + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bluetoothd + +LOCAL_REQUIRED_MODULES := \ + bluetoothd-main \ + valgrind \ + memcheck-$(TARGET_ARCH)-linux \ + vgpreload_core-$(TARGET_ARCH)-linux \ + vgpreload_memcheck-$(TARGET_ARCH)-linux \ + default.supp + +include $(BUILD_EXECUTABLE) + +endif + +# +# bluetooth-headers +# + +include $(CLEAR_VARS) + +LOCAL_MODULE := bluetooth-headers +LOCAL_NODULE_TAGS := optional +LOCAL_MODULE_CLASS := STATIC_LIBRARIES + +include_path := $(local-intermediates-dir)/include +include_files := $(wildcard $(LOCAL_PATH)/bluez/lib/*.h) +$(shell mkdir -p $(include_path)/bluetooth) +$(foreach file,$(include_files),$(shell cp -u $(file) $(include_path)/bluetooth)) + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(include_path) + +include $(BUILD_STATIC_LIBRARY) + +# +# avtest +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/avinfo.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := avinfo + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) + +# +# rctest +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + bluez/tools/rctest.c \ + bluez/lib/bluetooth.c \ + bluez/lib/hci.c \ + bluez/lib/sdp.c \ + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/bluez \ + +LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) + +LOCAL_STATIC_LIBRARIES := \ + bluetooth-headers \ + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := rctest + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac + +include $(BUILD_EXECUTABLE) diff -Nru bluez-4.101/android/audio-ipc-api.txt bluez-5.23/android/audio-ipc-api.txt --- bluez-4.101/android/audio-ipc-api.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/audio-ipc-api.txt 2014-02-22 01:42:17.000000000 +0000 @@ -0,0 +1,87 @@ +Bluetooth Audio Plugin +====================== + +The audio plugin happen to be in a different socket but all the rules for +HAL socket apply here as well, the abstract socket name is +"\0bluez_audio_socket" (tentative): + + .---Audio---. .--Android--. + | Plugin | | Daemon | + | | Command | | + | | --------------------------> | | + | | | | + | | <-------------------------- | | + | | Response | | + | | | | + | | | | + | | | | + '-----------' '-----------' + + + Audio HAL Daemon + ---------------------------------------------------- + + call dev->open() --> command 0x01 + return dev->open() <-- response 0x01 + + call dev->open_output_stream() --> command 0x03 + return dev->open_output_stream() <-- response 0x03 + + call stream->write() --> command 0x05 + return stream->write() <-- response 0x05 + + call stream->common.standby() --> command 0x06 + return stream->common.standby() <-- response 0x06 + + call dev->close_output_stream() --> command 0x04 + return dev->close_output_stream() <-- response 0x04 + + call dev->close() --> command 0x02 + return dev->close() <-- response 0x02 + +Audio Service (ID 0) +==================== + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Opcode 0x01 - Open Audio Endpoint commmand + + Command parameters: Service UUID (16 octets) + Codec ID (1 octet) + Number of codec presets (1 octet) + Codec capabilities length (1 octet) + Codec capabilities (variable) + Codec preset # length (1 octet) + Codec preset # configuration (variable) + ... + Response parameters: Endpoint ID (1 octet) + + Opcode 0x02 - Close Audio Endpoint command + + Command parameters: Endpoint ID (1 octet) + Response parameters: + + Opcode 0x03 - Open Stream command + + Command parameters: Endpoint ID (1 octet) + Response parameters: Outgoing MTU (2 octets) + Codec configuration length (1 octet) + Codec configuration (1 octet) + File descriptor (inline) + + Opcode 0x04 - Close Stream command + + Command parameters: Endpoint ID (1 octet) + Response parameters: + + Opcode 0x05 - Resume Stream command + + Command parameters: Endpoint ID (1 octet) + Response parameters: + + Opcode 0x06 - Suspend Stream command + + Command parameters: Endpoint ID (1 octet) + Response parameters: diff -Nru bluez-4.101/android/audio-msg.h bluez-5.23/android/audio-msg.h --- bluez-4.101/android/audio-msg.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/audio-msg.h 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#define BLUEZ_AUDIO_MTU 1024 + +static const char BLUEZ_AUDIO_SK_PATH[] = "\0bluez_audio_socket"; + +#define AUDIO_SERVICE_ID 0 +#define AUDIO_SERVICE_ID_MAX AUDIO_SERVICE_ID + +#define AUDIO_STATUS_SUCCESS IPC_STATUS_SUCCESS +#define AUDIO_STATUS_FAILED 0x01 + +#define AUDIO_OP_STATUS IPC_OP_STATUS + +#define AUDIO_OP_OPEN 0x01 +struct audio_preset { + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +struct audio_cmd_open { + uint8_t uuid[16]; + uint8_t codec; + uint8_t presets; + struct audio_preset preset[0]; +} __attribute__((packed)); + +struct audio_rsp_open { + uint8_t id; +} __attribute__((packed)); + +#define AUDIO_OP_CLOSE 0x02 +struct audio_cmd_close { + uint8_t id; +} __attribute__((packed)); + +#define AUDIO_OP_OPEN_STREAM 0x03 +struct audio_cmd_open_stream { + uint8_t id; +} __attribute__((packed)); + +struct audio_rsp_open_stream { + uint16_t id; + uint16_t mtu; + struct audio_preset preset[0]; +} __attribute__((packed)); + +#define AUDIO_OP_CLOSE_STREAM 0x04 +struct audio_cmd_close_stream { + uint8_t id; +} __attribute__((packed)); + +#define AUDIO_OP_RESUME_STREAM 0x05 +struct audio_cmd_resume_stream { + uint8_t id; +} __attribute__((packed)); + +#define AUDIO_OP_SUSPEND_STREAM 0x06 +struct audio_cmd_suspend_stream { + uint8_t id; +} __attribute__((packed)); diff -Nru bluez-4.101/android/audio_utils/resampler.c bluez-5.23/android/audio_utils/resampler.c --- bluez-4.101/android/audio_utils/resampler.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/audio_utils/resampler.c 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,270 @@ +/* +** Copyright 2011, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 + +#include +#include +#include +#include +#include +#include + +#include "hal-log.h" + +struct resampler { + struct resampler_itfe itfe; + SpeexResamplerState *speex_resampler; // handle on speex resampler + struct resampler_buffer_provider *provider; // buffer provider installed by client + uint32_t in_sample_rate; // input sampling rate in Hz + uint32_t out_sample_rate; // output sampling rate in Hz + uint32_t channel_count; // number of channels (interleaved) + int16_t *in_buf; // input buffer + size_t in_buf_size; // input buffer size + size_t frames_in; // number of frames in input buffer + size_t frames_rq; // cached number of output frames + size_t frames_needed; // minimum number of input frames to produce + // frames_rq output frames + int32_t speex_delay_ns; // delay introduced by speex resampler in ns +}; + + +//------------------------------------------------------------------------------ +// speex based resampler +//------------------------------------------------------------------------------ + +static void resampler_reset(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + rsmp->frames_in = 0; + rsmp->frames_rq = 0; + + if (rsmp != NULL && rsmp->speex_resampler != NULL) { + speex_resampler_reset_mem(rsmp->speex_resampler); + } +} + +static int32_t resampler_delay_ns(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + int32_t delay = (int32_t)((1000000000 * (int64_t)rsmp->frames_in) / rsmp->in_sample_rate); + delay += rsmp->speex_delay_ns; + + return delay; +} + +// outputs a number of frames less or equal to *outFrameCount and updates *outFrameCount +// with the actual number of frames produced. +static int resampler_resample_from_provider(struct resampler_itfe *resampler, + int16_t *out, + size_t *outFrameCount) +{ + struct resampler *rsmp = (struct resampler *)resampler; + size_t framesRq; + size_t framesWr; + size_t inFrames; + + if (rsmp == NULL || out == NULL || outFrameCount == NULL) { + return -EINVAL; + } + if (rsmp->provider == NULL) { + *outFrameCount = 0; + return -ENOSYS; + } + + framesRq = *outFrameCount; + // update and cache the number of frames needed at the input sampling rate to produce + // the number of frames requested at the output sampling rate + if (framesRq != rsmp->frames_rq) { + rsmp->frames_needed = (framesRq * rsmp->in_sample_rate) / rsmp->out_sample_rate + 1; + rsmp->frames_rq = framesRq; + } + + framesWr = 0; + inFrames = 0; + while (framesWr < framesRq) { + size_t outFrames; + if (rsmp->frames_in < rsmp->frames_needed) { + struct resampler_buffer buf; + // make sure that the number of frames present in rsmp->in_buf (rsmp->frames_in) is at + // least the number of frames needed to produce the number of frames requested at + // the output sampling rate + if (rsmp->in_buf_size < rsmp->frames_needed) { + rsmp->in_buf_size = rsmp->frames_needed; + rsmp->in_buf = (int16_t *)realloc(rsmp->in_buf, + rsmp->in_buf_size * rsmp->channel_count * sizeof(int16_t)); + } + buf.frame_count = rsmp->frames_needed - rsmp->frames_in; + rsmp->provider->get_next_buffer(rsmp->provider, &buf); + if (buf.raw == NULL) { + break; + } + memcpy(rsmp->in_buf + rsmp->frames_in * rsmp->channel_count, + buf.raw, + buf.frame_count * rsmp->channel_count * sizeof(int16_t)); + rsmp->frames_in += buf.frame_count; + rsmp->provider->release_buffer(rsmp->provider, &buf); + } + + outFrames = framesRq - framesWr; + inFrames = rsmp->frames_in; + if (rsmp->channel_count == 1) { + speex_resampler_process_int(rsmp->speex_resampler, + 0, + rsmp->in_buf, + (void *) &inFrames, + out + framesWr, + (void *) &outFrames); + } else { + speex_resampler_process_interleaved_int(rsmp->speex_resampler, + rsmp->in_buf, + (void *) &inFrames, + out + framesWr * rsmp->channel_count, + (void *) &outFrames); + } + framesWr += outFrames; + rsmp->frames_in -= inFrames; + + if ((framesWr != framesRq) && (rsmp->frames_in != 0)) + warn("ReSampler::resample() remaining %zd frames in and %zd out", + rsmp->frames_in, (framesRq - framesWr)); + } + if (rsmp->frames_in) { + memmove(rsmp->in_buf, + rsmp->in_buf + inFrames * rsmp->channel_count, + rsmp->frames_in * rsmp->channel_count * sizeof(int16_t)); + } + *outFrameCount = framesWr; + + return 0; +} + +static int resampler_resample_from_input(struct resampler_itfe *resampler, + int16_t *in, + size_t *inFrameCount, + int16_t *out, + size_t *outFrameCount) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + if (rsmp == NULL || in == NULL || inFrameCount == NULL || + out == NULL || outFrameCount == NULL) { + return -EINVAL; + } + if (rsmp->provider != NULL) { + *outFrameCount = 0; + return -ENOSYS; + } + + if (rsmp->channel_count == 1) { + speex_resampler_process_int(rsmp->speex_resampler, + 0, + in, + (void *) inFrameCount, + out, + (void *) outFrameCount); + } else { + speex_resampler_process_interleaved_int(rsmp->speex_resampler, + in, + (void *) inFrameCount, + out, + (void *) outFrameCount); + } + + DBG("resampler_resample_from_input() DONE in %zd out %zd", *inFrameCount, *outFrameCount); + + return 0; +} + +int create_resampler(uint32_t inSampleRate, + uint32_t outSampleRate, + uint32_t channelCount, + uint32_t quality, + struct resampler_buffer_provider* provider, + struct resampler_itfe **resampler) +{ + int error; + struct resampler *rsmp; + int frames; + + DBG("create_resampler() In SR %d Out SR %d channels %d", + inSampleRate, outSampleRate, channelCount); + + if (resampler == NULL) { + return -EINVAL; + } + + *resampler = NULL; + + if (quality <= RESAMPLER_QUALITY_MIN || quality >= RESAMPLER_QUALITY_MAX) { + return -EINVAL; + } + + rsmp = (struct resampler *)calloc(1, sizeof(struct resampler)); + + rsmp->speex_resampler = speex_resampler_init(channelCount, + inSampleRate, + outSampleRate, + quality, + &error); + if (rsmp->speex_resampler == NULL) { + error("ReSampler: Cannot create speex resampler: %s", speex_resampler_strerror(error)); + free(rsmp); + return -ENODEV; + } + + rsmp->itfe.reset = resampler_reset; + rsmp->itfe.resample_from_provider = resampler_resample_from_provider; + rsmp->itfe.resample_from_input = resampler_resample_from_input; + rsmp->itfe.delay_ns = resampler_delay_ns; + + rsmp->provider = provider; + rsmp->in_sample_rate = inSampleRate; + rsmp->out_sample_rate = outSampleRate; + rsmp->channel_count = channelCount; + rsmp->in_buf = NULL; + rsmp->in_buf_size = 0; + + resampler_reset(&rsmp->itfe); + + frames = speex_resampler_get_input_latency(rsmp->speex_resampler); + rsmp->speex_delay_ns = (int32_t)((1000000000 * (int64_t)frames) / rsmp->in_sample_rate); + frames = speex_resampler_get_output_latency(rsmp->speex_resampler); + rsmp->speex_delay_ns += (int32_t)((1000000000 * (int64_t)frames) / rsmp->out_sample_rate); + + *resampler = &rsmp->itfe; + DBG("create_resampler() DONE rsmp %p &rsmp->itfe %p speex %p", + rsmp, &rsmp->itfe, rsmp->speex_resampler); + return 0; +} + +void release_resampler(struct resampler_itfe *resampler) +{ + struct resampler *rsmp = (struct resampler *)resampler; + + if (rsmp == NULL) { + return; + } + + free(rsmp->in_buf); + + if (rsmp->speex_resampler != NULL) { + speex_resampler_destroy(rsmp->speex_resampler); + } + free(rsmp); +} diff -Nru bluez-4.101/android/audio_utils/resampler.h bluez-5.23/android/audio_utils/resampler.h --- bluez-4.101/android/audio_utils/resampler.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/audio_utils/resampler.h 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,109 @@ +/* +** Copyright 2008, The Android Open-Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_RESAMPLER_H +#define ANDROID_RESAMPLER_H + +#include +#include + +__BEGIN_DECLS + + +#define RESAMPLER_QUALITY_MAX 10 +#define RESAMPLER_QUALITY_MIN 0 +#define RESAMPLER_QUALITY_DEFAULT 4 +#define RESAMPLER_QUALITY_VOIP 3 +#define RESAMPLER_QUALITY_DESKTOP 5 + +struct resampler_buffer { + union { + void* raw; + short* i16; + int8_t* i8; + }; + size_t frame_count; +}; + +/* call back interface used by the resampler to get new data */ +struct resampler_buffer_provider +{ + /** + * get a new buffer of data: + * as input: buffer->frame_count is the number of frames requested + * as output: buffer->frame_count is the number of frames returned + * buffer->raw points to data returned + */ + int (*get_next_buffer)(struct resampler_buffer_provider *provider, + struct resampler_buffer *buffer); + /** + * release a consumed buffer of data: + * as input: buffer->frame_count is the number of frames released + * buffer->raw points to data released + */ + void (*release_buffer)(struct resampler_buffer_provider *provider, + struct resampler_buffer *buffer); +}; + +/* resampler interface */ +struct resampler_itfe { + /** + * reset resampler state + */ + void (*reset)(struct resampler_itfe *resampler); + /** + * resample input from buffer provider and output at most *outFrameCount to out buffer. + * *outFrameCount is updated with the actual number of frames produced. + */ + int (*resample_from_provider)(struct resampler_itfe *resampler, + int16_t *out, + size_t *outFrameCount); + /** + * resample at most *inFrameCount frames from in buffer and output at most + * *outFrameCount to out buffer. *inFrameCount and *outFrameCount are updated respectively + * with the number of frames remaining in input and written to output. + */ + int (*resample_from_input)(struct resampler_itfe *resampler, + int16_t *in, + size_t *inFrameCount, + int16_t *out, + size_t *outFrameCount); + /** + * return the latency introduced by the resampler in ns. + */ + int32_t (*delay_ns)(struct resampler_itfe *resampler); +}; + +/** + * create a resampler according to input parameters passed. + * If resampler_buffer_provider is not NULL only resample_from_provider() can be called. + * If resampler_buffer_provider is NULL only resample_from_input() can be called. + */ +int create_resampler(uint32_t inSampleRate, + uint32_t outSampleRate, + uint32_t channelCount, + uint32_t quality, + struct resampler_buffer_provider *provider, + struct resampler_itfe **); + +/** + * release resampler resources. + */ +void release_resampler(struct resampler_itfe *); + +__END_DECLS + +#endif // ANDROID_RESAMPLER_H diff -Nru bluez-4.101/android/avctp.c bluez-5.23/android/avctp.c --- bluez-4.101/android/avctp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avctp.c 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,1625 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * Copyright (C) 2011 Texas Instruments, Inc. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "src/log.h" +#include "src/uinput.h" + +#include "avctp.h" + +/* + * AV/C Panel 1.23, page 76: + * command with the pressed value is valid for two seconds + */ +#define AVC_PRESS_TIMEOUT 2 + +#define QUIRK_NO_RELEASE 1 << 0 + +/* Message types */ +#define AVCTP_COMMAND 0 +#define AVCTP_RESPONSE 1 + +/* Packet types */ +#define AVCTP_PACKET_SINGLE 0 +#define AVCTP_PACKET_START 1 +#define AVCTP_PACKET_CONTINUE 2 +#define AVCTP_PACKET_END 3 + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avctp_header { + uint8_t ipid:1; + uint8_t cr:1; + uint8_t packet_type:2; + uint8_t transaction:4; + uint16_t pid; +} __attribute__ ((packed)); + +struct avc_header { + uint8_t code:4; + uint8_t _hdr0:4; + uint8_t subunit_id:3; + uint8_t subunit_type:5; + uint8_t opcode; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avctp_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t cr:1; + uint8_t ipid:1; + uint16_t pid; +} __attribute__ ((packed)); + +struct avc_header { + uint8_t _hdr0:4; + uint8_t code:4; + uint8_t subunit_type:5; + uint8_t subunit_id:3; + uint8_t opcode; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +struct avctp_control_req { + struct avctp_pending_req *p; + uint8_t code; + uint8_t subunit; + uint8_t op; + struct iovec *iov; + int iov_cnt; + avctp_rsp_cb func; + void *user_data; +}; + +struct avctp_browsing_req { + struct avctp_pending_req *p; + struct iovec *iov; + int iov_cnt; + avctp_browsing_rsp_cb func; + void *user_data; +}; + +typedef int (*avctp_process_cb) (void *data); + +struct avctp_pending_req { + struct avctp_channel *chan; + uint8_t transaction; + guint timeout; + int err; + avctp_process_cb process; + void *data; + avctp_destroy_cb_t destroy; +}; + +struct avctp_channel { + struct avctp *session; + GIOChannel *io; + uint8_t transaction; + guint watch; + uint16_t imtu; + uint16_t omtu; + uint8_t *buffer; + GSList *handlers; + struct avctp_pending_req *p; + GQueue *queue; + GSList *processed; + guint process_id; + avctp_destroy_cb_t destroy; +}; + +struct key_pressed { + uint8_t op; + uint8_t *params; + size_t params_len; + guint timer; +}; + +struct avctp { + int uinput; + + unsigned int passthrough_id; + unsigned int unit_id; + unsigned int subunit_id; + + struct avctp_channel *control; + struct avctp_channel *browsing; + + struct avctp_passthrough_handler *handler; + + uint8_t key_quirks[256]; + struct key_pressed key; + uint16_t version; + + avctp_destroy_cb_t destroy; + void *data; +}; + +struct avctp_passthrough_handler { + avctp_passthrough_cb cb; + void *user_data; + unsigned int id; +}; + +struct avctp_pdu_handler { + uint8_t opcode; + avctp_control_pdu_cb cb; + void *user_data; + unsigned int id; +}; + +struct avctp_browsing_pdu_handler { + avctp_browsing_pdu_cb cb; + void *user_data; + unsigned int id; + avctp_destroy_cb_t destroy; +}; + +static struct { + const char *name; + uint8_t avc; + uint16_t uinput; +} key_map[] = { + { "SELECT", AVC_SELECT, KEY_SELECT }, + { "UP", AVC_UP, KEY_UP }, + { "DOWN", AVC_DOWN, KEY_DOWN }, + { "LEFT", AVC_LEFT, KEY_LEFT }, + { "RIGHT", AVC_RIGHT, KEY_RIGHT }, + { "ROOT MENU", AVC_ROOT_MENU, KEY_MENU }, + { "CONTENTS MENU", AVC_CONTENTS_MENU, KEY_PROGRAM }, + { "FAVORITE MENU", AVC_FAVORITE_MENU, KEY_FAVORITES }, + { "EXIT", AVC_EXIT, KEY_EXIT }, + { "ON DEMAND MENU", AVC_ON_DEMAND_MENU, KEY_MENU }, + { "APPS MENU", AVC_APPS_MENU, KEY_MENU }, + { "0", AVC_0, KEY_0 }, + { "1", AVC_1, KEY_1 }, + { "2", AVC_2, KEY_2 }, + { "3", AVC_3, KEY_3 }, + { "4", AVC_4, KEY_4 }, + { "5", AVC_5, KEY_5 }, + { "6", AVC_6, KEY_6 }, + { "7", AVC_7, KEY_7 }, + { "8", AVC_8, KEY_8 }, + { "9", AVC_9, KEY_9 }, + { "DOT", AVC_DOT, KEY_DOT }, + { "ENTER", AVC_ENTER, KEY_ENTER }, + { "CHANNEL UP", AVC_CHANNEL_UP, KEY_CHANNELUP }, + { "CHANNEL DOWN", AVC_CHANNEL_DOWN, KEY_CHANNELDOWN }, + { "CHANNEL PREVIOUS", AVC_CHANNEL_PREVIOUS, KEY_LAST }, + { "INPUT SELECT", AVC_INPUT_SELECT, KEY_CONFIG }, + { "INFO", AVC_INFO, KEY_INFO }, + { "HELP", AVC_HELP, KEY_HELP }, + { "POWER", AVC_POWER, KEY_POWER2 }, + { "VOLUME UP", AVC_VOLUME_UP, KEY_VOLUMEUP }, + { "VOLUME DOWN", AVC_VOLUME_DOWN, KEY_VOLUMEDOWN }, + { "MUTE", AVC_MUTE, KEY_MUTE }, + { "PLAY", AVC_PLAY, KEY_PLAYCD }, + { "STOP", AVC_STOP, KEY_STOPCD }, + { "PAUSE", AVC_PAUSE, KEY_PAUSECD }, + { "FORWARD", AVC_FORWARD, KEY_NEXTSONG }, + { "BACKWARD", AVC_BACKWARD, KEY_PREVIOUSSONG }, + { "RECORD", AVC_RECORD, KEY_RECORD }, + { "REWIND", AVC_REWIND, KEY_REWIND }, + { "FAST FORWARD", AVC_FAST_FORWARD, KEY_FASTFORWARD }, + { "LIST", AVC_LIST, KEY_LIST }, + { "F1", AVC_F1, KEY_F1 }, + { "F2", AVC_F2, KEY_F2 }, + { "F3", AVC_F3, KEY_F3 }, + { "F4", AVC_F4, KEY_F4 }, + { "F5", AVC_F5, KEY_F5 }, + { "F6", AVC_F6, KEY_F6 }, + { "F7", AVC_F7, KEY_F7 }, + { "F8", AVC_F8, KEY_F8 }, + { "F9", AVC_F9, KEY_F9 }, + { "RED", AVC_RED, KEY_RED }, + { "GREEN", AVC_GREEN, KEY_GREEN }, + { "BLUE", AVC_BLUE, KEY_BLUE }, + { "YELLOW", AVC_YELLOW, KEY_YELLOW }, + { NULL } +}; + +static gboolean process_queue(gpointer user_data); +static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, + uint8_t subunit, uint8_t *operands, + size_t operand_count, void *user_data); + +static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) +{ + struct uinput_event event; + int err; + + memset(&event, 0, sizeof(event)); + event.type = type; + event.code = code; + event.value = value; + + do { + err = write(fd, &event, sizeof(event)); + } while (err < 0 && errno == EINTR); + + if (err < 0) { + err = -errno; + error("send_event: %s (%d)", strerror(-err), -err); + } + + return err; +} + +static void send_key(int fd, uint16_t key, int pressed) +{ + send_event(fd, EV_KEY, key, pressed); + send_event(fd, EV_SYN, SYN_REPORT, 0); +} + +static gboolean auto_release(gpointer user_data) +{ + struct avctp *session = user_data; + + session->key.timer = 0; + + DBG("AV/C: key press timeout"); + + send_key(session->uinput, session->key.op, 0); + + return FALSE; +} + +static ssize_t handle_panel_passthrough(struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avctp_passthrough_handler *handler = session->handler; + const char *status; + int pressed, i; + + if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) { + *code = AVC_CTYPE_REJECTED; + return operand_count; + } + + if (operand_count == 0) + goto done; + + if (operands[0] & 0x80) { + status = "released"; + pressed = 0; + } else { + status = "pressed"; + pressed = 1; + } + + if (session->key.timer == 0 && handler != NULL) { + if (handler->cb(session, operands[0] & 0x7F, + pressed, handler->user_data)) + goto done; + } + + if (session->uinput < 0) { + DBG("AV/C: uinput not initialized"); + *code = AVC_CTYPE_NOT_IMPLEMENTED; + return 0; + } + + for (i = 0; key_map[i].name != NULL; i++) { + uint8_t key_quirks; + + if ((operands[0] & 0x7F) != key_map[i].avc) + continue; + + DBG("AV/C: %s %s", key_map[i].name, status); + + key_quirks = session->key_quirks[key_map[i].avc]; + + if (key_quirks & QUIRK_NO_RELEASE) { + if (!pressed) { + DBG("AV/C: Ignoring release"); + break; + } + + DBG("AV/C: treating key press as press + release"); + send_key(session->uinput, key_map[i].uinput, 1); + send_key(session->uinput, key_map[i].uinput, 0); + break; + } + + if (pressed) { + if (session->key.timer > 0) { + g_source_remove(session->key.timer); + send_key(session->uinput, session->key.op, 0); + } + + session->key.op = key_map[i].uinput; + session->key.timer = g_timeout_add_seconds( + AVC_PRESS_TIMEOUT, + auto_release, + session); + } else if (session->key.timer > 0) { + g_source_remove(session->key.timer); + session->key.timer = 0; + } + + send_key(session->uinput, key_map[i].uinput, pressed); + break; + } + + if (key_map[i].name == NULL) { + DBG("AV/C: unknown button 0x%02X %s", + operands[0] & 0x7F, status); + *code = AVC_CTYPE_NOT_IMPLEMENTED; + return operand_count; + } + +done: + *code = AVC_CTYPE_ACCEPTED; + return operand_count; +} + +static ssize_t handle_unit_info(struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + if (*code != AVC_CTYPE_STATUS) { + *code = AVC_CTYPE_REJECTED; + return 0; + } + + *code = AVC_CTYPE_STABLE; + + /* + * The first operand should be 0x07 for the UNITINFO response. + * Neither AVRCP (section 22.1, page 117) nor AVC Digital + * Interface Command Set (section 9.2.1, page 45) specs + * explain this value but both use it + */ + if (operand_count >= 1) + operands[0] = 0x07; + if (operand_count >= 2) + operands[1] = AVC_SUBUNIT_PANEL << 3; + + DBG("reply to AVC_OP_UNITINFO"); + + return operand_count; +} + +static ssize_t handle_subunit_info(struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + if (*code != AVC_CTYPE_STATUS) { + *code = AVC_CTYPE_REJECTED; + return 0; + } + + *code = AVC_CTYPE_STABLE; + + /* + * The first operand should be 0x07 for the UNITINFO response. + * Neither AVRCP (section 22.1, page 117) nor AVC Digital + * Interface Command Set (section 9.2.1, page 45) specs + * explain this value but both use it + */ + if (operand_count >= 2) + operands[1] = AVC_SUBUNIT_PANEL << 3; + + DBG("reply to AVC_OP_SUBUNITINFO"); + + return operand_count; +} + +static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode) +{ + for (; list; list = list->next) { + struct avctp_pdu_handler *handler = list->data; + + if (handler->opcode == opcode) + return handler; + } + + return NULL; +} + +static void pending_destroy(gpointer data, gpointer user_data) +{ + struct avctp_pending_req *req = data; + + if (req->destroy) + req->destroy(req->data); + + if (req->timeout > 0) + g_source_remove(req->timeout); + + g_free(req); +} + +static void avctp_channel_destroy(struct avctp_channel *chan) +{ + g_io_channel_shutdown(chan->io, TRUE, NULL); + g_io_channel_unref(chan->io); + + if (chan->watch) + g_source_remove(chan->watch); + + if (chan->p) + pending_destroy(chan->p, NULL); + + if (chan->process_id > 0) + g_source_remove(chan->process_id); + + if (chan->destroy) + chan->destroy(chan); + + g_free(chan->buffer); + g_queue_foreach(chan->queue, pending_destroy, NULL); + g_queue_free(chan->queue); + g_slist_foreach(chan->processed, pending_destroy, NULL); + g_slist_free(chan->processed); + g_slist_free_full(chan->handlers, g_free); + g_free(chan); +} + +static int avctp_send(struct avctp_channel *control, uint8_t transaction, + uint8_t cr, uint8_t code, + uint8_t subunit, uint8_t opcode, + const struct iovec *iov, int iov_cnt) +{ + struct avctp_header avctp; + struct avc_header avc; + struct msghdr msg; + int sk, err = 0; + struct iovec pdu[iov_cnt + 2]; + int i; + size_t len = sizeof(avctp) + sizeof(avc); + + DBG(""); + + pdu[0].iov_base = &avctp; + pdu[0].iov_len = sizeof(avctp); + pdu[1].iov_base = &avc; + pdu[1].iov_len = sizeof(avc); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 2].iov_base = iov[i].iov_base; + pdu[i + 2].iov_len = iov[i].iov_len; + len += iov[i].iov_len; + } + + if (control->omtu < len) + return -EOVERFLOW; + + sk = g_io_channel_unix_get_fd(control->io); + + memset(&avctp, 0, sizeof(avctp)); + + avctp.transaction = transaction; + avctp.packet_type = AVCTP_PACKET_SINGLE; + avctp.cr = cr; + avctp.pid = htons(AV_REMOTE_SVCLASS_ID); + + memset(&avc, 0, sizeof(avc)); + + avc.code = code; + avc.subunit_type = subunit; + avc.opcode = opcode; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = pdu; + msg.msg_iovlen = iov_cnt + 2; + + if (sendmsg(sk, &msg, 0) < 0) + err = -errno; + + return err; +} + +static int avctp_browsing_send(struct avctp_channel *browsing, + uint8_t transaction, uint8_t cr, + const struct iovec *iov, int iov_cnt) +{ + struct avctp_header avctp; + struct msghdr msg; + struct iovec pdu[iov_cnt + 1]; + int sk, err = 0; + int i; + size_t len = sizeof(avctp); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + len += iov[i].iov_len; + } + + pdu[0].iov_base = &avctp; + pdu[0].iov_len = sizeof(avctp); + + if (browsing->omtu < len) + return -EOVERFLOW; + + sk = g_io_channel_unix_get_fd(browsing->io); + + memset(&avctp, 0, sizeof(avctp)); + + avctp.transaction = transaction; + avctp.packet_type = AVCTP_PACKET_SINGLE; + avctp.cr = cr; + avctp.pid = htons(AV_REMOTE_SVCLASS_ID); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = pdu; + msg.msg_iovlen = iov_cnt + 1; + + if (sendmsg(sk, &msg, 0) < 0) + err = -errno; + + return err; +} + +static void control_req_destroy(void *data) +{ + struct avctp_control_req *req = data; + struct avctp_pending_req *p = req->p; + struct avctp *session = p->chan->session; + int i; + + if (p->err == 0 || req->func == NULL) + goto done; + + req->func(session, AVC_CTYPE_REJECTED, req->subunit, NULL, 0, + req->user_data); + +done: + for (i = 0; i < req->iov_cnt; i++) + g_free(req->iov[i].iov_base); + + g_free(req->iov); + g_free(req); +} + +static void browsing_req_destroy(void *data) +{ + struct avctp_browsing_req *req = data; + struct avctp_pending_req *p = req->p; + struct avctp *session = p->chan->session; + int i; + + if (p->err == 0 || req->func == NULL) + goto done; + + req->func(session, NULL, 0, req->user_data); + +done: + for (i = 0; i < req->iov_cnt; i++) + g_free(req->iov[i].iov_base); + + g_free(req->iov); + g_free(req); +} + +static gboolean req_timeout(gpointer user_data) +{ + struct avctp_channel *chan = user_data; + struct avctp_pending_req *p = chan->p; + + DBG("transaction %u", p->transaction); + + p->timeout = 0; + p->err = -ETIMEDOUT; + + pending_destroy(p, NULL); + chan->p = NULL; + + if (chan->process_id == 0) + chan->process_id = g_idle_add(process_queue, chan); + + return FALSE; +} + +static int process_control(void *data) +{ + struct avctp_control_req *req = data; + struct avctp_pending_req *p = req->p; + + return avctp_send(p->chan, p->transaction, AVCTP_COMMAND, req->code, + req->subunit, req->op, req->iov, req->iov_cnt); +} + +static int process_browsing(void *data) +{ + struct avctp_browsing_req *req = data; + struct avctp_pending_req *p = req->p; + + return avctp_browsing_send(p->chan, p->transaction, AVCTP_COMMAND, + req->iov, req->iov_cnt); +} + +static gboolean process_queue(void *user_data) +{ + struct avctp_channel *chan = user_data; + struct avctp_pending_req *p = chan->p; + + chan->process_id = 0; + + if (p != NULL) + return FALSE; + + while ((p = g_queue_pop_head(chan->queue))) { + + if (p->process(p->data) == 0) + break; + + pending_destroy(p, NULL); + } + + if (p == NULL) + return FALSE; + + chan->p = p; + p->timeout = g_timeout_add_seconds(2, req_timeout, chan); + + return FALSE; + +} + +static void control_response(struct avctp_channel *control, + struct avctp_header *avctp, + struct avc_header *avc, + uint8_t *operands, + size_t operand_count) +{ + struct avctp_pending_req *p = control->p; + struct avctp_control_req *req; + GSList *l; + + if (p && p->transaction == avctp->transaction) { + control->processed = g_slist_prepend(control->processed, p); + + if (p->timeout > 0) { + g_source_remove(p->timeout); + p->timeout = 0; + } + + control->p = NULL; + + if (control->process_id == 0) + control->process_id = g_idle_add(process_queue, + control); + } + + for (l = control->processed; l; l = l->next) { + p = l->data; + req = p->data; + + if (p->transaction != avctp->transaction) + continue; + + if (req->func && req->func(control->session, avc->code, + avc->subunit_type, + operands, operand_count, + req->user_data)) + return; + + control->processed = g_slist_remove(control->processed, p); + pending_destroy(p, NULL); + + return; + } +} + +static void browsing_response(struct avctp_channel *browsing, + struct avctp_header *avctp, + uint8_t *operands, + size_t operand_count) +{ + struct avctp_pending_req *p = browsing->p; + struct avctp_browsing_req *req; + GSList *l; + + if (p && p->transaction == avctp->transaction) { + browsing->processed = g_slist_prepend(browsing->processed, p); + + if (p->timeout > 0) { + g_source_remove(p->timeout); + p->timeout = 0; + } + + browsing->p = NULL; + + if (browsing->process_id == 0) + browsing->process_id = g_idle_add(process_queue, + browsing); + } + + for (l = browsing->processed; l; l = l->next) { + p = l->data; + req = p->data; + + if (p->transaction != avctp->transaction) + continue; + + if (req->func && req->func(browsing->session, operands, + operand_count, req->user_data)) + return; + + browsing->processed = g_slist_remove(browsing->processed, p); + pending_destroy(p, NULL); + + return; + } +} + +static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avctp *session = data; + struct avctp_channel *browsing = session->browsing; + uint8_t *buf = browsing->buffer; + uint8_t *operands; + struct avctp_header *avctp; + int sock, ret, packet_size, operand_count; + struct avctp_browsing_pdu_handler *handler; + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) + goto failed; + + sock = g_io_channel_unix_get_fd(chan); + + ret = read(sock, buf, browsing->imtu); + if (ret <= 0) + goto failed; + + if (ret < AVCTP_HEADER_LENGTH) { + error("Too small AVCTP packet"); + goto failed; + } + + avctp = (struct avctp_header *) buf; + + if (avctp->packet_type != AVCTP_PACKET_SINGLE) { + error("Invalid packet type"); + goto failed; + } + + operands = buf + AVCTP_HEADER_LENGTH; + ret -= AVCTP_HEADER_LENGTH; + operand_count = ret; + + if (avctp->cr == AVCTP_RESPONSE) { + browsing_response(browsing, avctp, operands, operand_count); + return TRUE; + } + + packet_size = AVCTP_HEADER_LENGTH; + avctp->cr = AVCTP_RESPONSE; + + handler = g_slist_nth_data(browsing->handlers, 0); + if (handler == NULL) { + DBG("handler not found"); + /* FIXME: Add general reject */ + /* packet_size += avrcp_browsing_general_reject(operands); */ + goto send; + } + + ret = handler->cb(session, avctp->transaction, operands, operand_count, + handler->user_data); + if (ret < 0) { + if (ret == -EAGAIN) + return TRUE; + goto failed; + } + + packet_size += ret; + +send: + if (packet_size != 0) { + ret = write(sock, buf, packet_size); + if (ret != packet_size) + goto failed; + } + + return TRUE; + +failed: + DBG("AVCTP Browsing: disconnected"); + + if (session->browsing) { + avctp_channel_destroy(session->browsing); + session->browsing = NULL; + } + + return FALSE; +} + +static gboolean session_cb(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + struct avctp *session = data; + struct avctp_channel *control = session->control; + uint8_t *buf = control->buffer; + uint8_t *operands, code, subunit; + struct avctp_header *avctp; + struct avc_header *avc; + int packet_size, operand_count, sock; + struct avctp_pdu_handler *handler; + ssize_t ret; + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) + goto failed; + + sock = g_io_channel_unix_get_fd(chan); + + ret = read(sock, buf, control->imtu); + if (ret <= 0) + goto failed; + + if (ret < AVCTP_HEADER_LENGTH) { + error("Too small AVCTP packet"); + goto failed; + } + + avctp = (struct avctp_header *) buf; + + ret -= AVCTP_HEADER_LENGTH; + if (ret < AVC_HEADER_LENGTH) { + error("Too small AVC packet"); + goto failed; + } + + avc = (struct avc_header *) (buf + AVCTP_HEADER_LENGTH); + + ret -= AVC_HEADER_LENGTH; + + operands = (uint8_t *) avc + AVC_HEADER_LENGTH; + operand_count = ret; + + if (avctp->cr == AVCTP_RESPONSE) { + control_response(control, avctp, avc, operands, operand_count); + return TRUE; + } + + packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH; + avctp->cr = AVCTP_RESPONSE; + + if (avctp->packet_type != AVCTP_PACKET_SINGLE) { + avc->code = AVC_CTYPE_NOT_IMPLEMENTED; + goto done; + } + + if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) { + avctp->ipid = 1; + packet_size = AVCTP_HEADER_LENGTH; + goto done; + } + + handler = find_handler(control->handlers, avc->opcode); + if (!handler) { + DBG("handler not found for 0x%02x", avc->opcode); + avc->code = AVC_CTYPE_REJECTED; + goto done; + } + + code = avc->code; + subunit = avc->subunit_type; + + ret = handler->cb(session, avctp->transaction, &code, + &subunit, operands, operand_count, + handler->user_data); + if (ret < 0) { + if (ret == -EAGAIN) + return TRUE; + goto failed; + } + + packet_size += ret; + avc->code = code; + avc->subunit_type = subunit; + +done: + ret = write(sock, buf, packet_size); + if (ret != packet_size) + goto failed; + + return TRUE; + +failed: + DBG("AVCTP session %p got disconnected", session); + avctp_shutdown(session); + return FALSE; +} + +static int uinput_create(const char *name) +{ + struct uinput_dev dev; + int fd, err, i; + + fd = open("/dev/uinput", O_RDWR); + if (fd < 0) { + fd = open("/dev/input/uinput", O_RDWR); + if (fd < 0) { + fd = open("/dev/misc/uinput", O_RDWR); + if (fd < 0) { + err = -errno; + error("Can't open input device: %s (%d)", + strerror(-err), -err); + return err; + } + } + } + + memset(&dev, 0, sizeof(dev)); + if (name) + strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1); + + dev.id.bustype = BUS_BLUETOOTH; + dev.id.vendor = 0x0000; + dev.id.product = 0x0000; + dev.id.version = 0x0000; + + if (write(fd, &dev, sizeof(dev)) < 0) { + err = -errno; + error("Can't write device information: %s (%d)", + strerror(-err), -err); + close(fd); + return err; + } + + ioctl(fd, UI_SET_EVBIT, EV_KEY); + ioctl(fd, UI_SET_EVBIT, EV_REL); + ioctl(fd, UI_SET_EVBIT, EV_REP); + ioctl(fd, UI_SET_EVBIT, EV_SYN); + + for (i = 0; key_map[i].name != NULL; i++) + ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput); + + if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { + err = -errno; + error("Can't create uinput device: %s (%d)", + strerror(-err), -err); + close(fd); + return err; + } + + return fd; +} + +int avctp_init_uinput(struct avctp *session, const char *name, + const char *address) +{ + if (g_str_equal(name, "Nokia CK-20W")) { + session->key_quirks[AVC_FORWARD] |= QUIRK_NO_RELEASE; + session->key_quirks[AVC_BACKWARD] |= QUIRK_NO_RELEASE; + session->key_quirks[AVC_PLAY] |= QUIRK_NO_RELEASE; + session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE; + } + + session->uinput = uinput_create(address); + if (session->uinput < 0) { + error("AVCTP: failed to init uinput for %s", address); + return session->uinput; + } + + return 0; +} + +static struct avctp_channel *avctp_channel_create(struct avctp *session, int fd, + size_t imtu, size_t omtu, + avctp_destroy_cb_t destroy) +{ + struct avctp_channel *chan; + + chan = g_new0(struct avctp_channel, 1); + chan->session = session; + chan->io = g_io_channel_unix_new(fd); + chan->queue = g_queue_new(); + chan->imtu = imtu; + chan->omtu = omtu; + chan->buffer = g_malloc0(MAX(imtu, omtu)); + chan->destroy = destroy; + + return chan; +} + +static void handler_free(void *data) +{ + struct avctp_browsing_pdu_handler *handler = data; + + if (handler->destroy) + handler->destroy(handler->user_data); + + g_free(data); +} + +static void avctp_destroy_browsing(void *data) +{ + struct avctp_channel *chan = data; + + g_slist_free_full(chan->handlers, handler_free); + + chan->handlers = NULL; +} + +static struct avctp_pending_req *pending_create(struct avctp_channel *chan, + avctp_process_cb process, + void *data, + avctp_destroy_cb_t destroy) +{ + struct avctp_pending_req *p; + GSList *l, *tmp; + + if (!chan->processed) + goto done; + + tmp = g_slist_copy(chan->processed); + + /* Find first unused transaction id */ + for (l = tmp; l; l = g_slist_next(l)) { + struct avctp_pending_req *req = l->data; + + if (req->transaction == chan->transaction) { + chan->transaction++; + chan->transaction %= 16; + tmp = g_slist_delete_link(tmp, l); + l = tmp; + } + } + + g_slist_free(tmp); + +done: + p = g_new0(struct avctp_pending_req, 1); + p->chan = chan; + p->transaction = chan->transaction; + p->process = process; + p->data = data; + p->destroy = destroy; + + chan->transaction++; + chan->transaction %= 16; + + return p; +} + +static int avctp_send_req(struct avctp *session, uint8_t code, uint8_t subunit, + uint8_t opcode, const struct iovec *iov, int iov_cnt, + avctp_rsp_cb func, void *user_data) +{ + struct avctp_channel *control = session->control; + struct avctp_pending_req *p; + struct avctp_control_req *req; + struct iovec *pdu; + int i; + + if (control == NULL) + return -ENOTCONN; + + pdu = g_new0(struct iovec, iov_cnt); + + for (i = 0; i < iov_cnt; i++) { + pdu[i].iov_len = iov[i].iov_len; + pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len); + } + + req = g_new0(struct avctp_control_req, 1); + req->code = code; + req->subunit = subunit; + req->op = opcode; + req->func = func; + req->iov = pdu; + req->iov_cnt = iov_cnt; + req->user_data = user_data; + + p = pending_create(control, process_control, req, control_req_destroy); + + req->p = p; + + g_queue_push_tail(control->queue, p); + + if (control->process_id == 0) + control->process_id = g_idle_add(process_queue, control); + + return 0; +} + +int avctp_send_browsing_req(struct avctp *session, + const struct iovec *iov, int iov_cnt, + avctp_browsing_rsp_cb func, void *user_data) +{ + struct avctp_channel *browsing = session->browsing; + struct avctp_pending_req *p; + struct avctp_browsing_req *req; + struct iovec *pdu; + int i; + + if (browsing == NULL) + return -ENOTCONN; + + pdu = g_new0(struct iovec, iov_cnt); + + for (i = 0; i < iov_cnt; i++) { + pdu[i].iov_len = iov[i].iov_len; + pdu[i].iov_base = g_memdup(iov[i].iov_base, iov[i].iov_len); + } + + req = g_new0(struct avctp_browsing_req, 1); + req->func = func; + req->iov = pdu; + req->iov_cnt = iov_cnt; + req->user_data = user_data; + + p = pending_create(browsing, process_browsing, req, + browsing_req_destroy); + + req->p = p; + + g_queue_push_tail(browsing->queue, p); + + /* Connection did not complete, delay process of the request */ + if (browsing->watch == 0) + return 0; + + if (browsing->process_id == 0) + browsing->process_id = g_idle_add(process_queue, browsing); + + return 0; +} + +int avctp_send_browsing(struct avctp *session, uint8_t transaction, + const struct iovec *iov, int iov_cnt) +{ + struct avctp_channel *browsing = session->browsing; + + if (browsing == NULL) + return -ENOTCONN; + + return avctp_browsing_send(browsing, transaction, AVCTP_RESPONSE, + iov, iov_cnt); +} + +static const char *op2str(uint8_t op) +{ + int i; + + for (i = 0; key_map[i].name != NULL; i++) { + if ((op & 0x7F) == key_map[i].avc) + return key_map[i].name; + } + + return "UNKNOWN"; +} + +static int avctp_passthrough_press(struct avctp *session, uint8_t op, + uint8_t *params, size_t params_len) +{ + struct iovec iov[2]; + int iov_cnt; + uint8_t operands[2]; + + DBG("%s", op2str(op)); + + iov[0].iov_base = operands; + iov[0].iov_len = sizeof(operands); + + /* Button pressed */ + operands[0] = op & 0x7f; + + if (params_len > 0) { + iov[1].iov_base = params; + iov[1].iov_len = params_len; + iov_cnt = 2; + operands[1] = params_len; + } else { + iov_cnt = 1; + operands[1] = 0; + } + + return avctp_send_req(session, AVC_CTYPE_CONTROL, + AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH, + iov, iov_cnt, avctp_passthrough_rsp, NULL); +} + +static int avctp_passthrough_release(struct avctp *session, uint8_t op, + uint8_t *params, size_t params_len) +{ + struct iovec iov[2]; + int iov_cnt; + uint8_t operands[2]; + + DBG("%s", op2str(op)); + + iov[0].iov_base = operands; + iov[0].iov_len = sizeof(operands); + + /* Button released */ + operands[0] = op | 0x80; + + if (params_len > 0) { + iov[1].iov_base = params; + iov[1].iov_len = params_len; + iov_cnt = 2; + operands[1] = params_len; + } else { + iov_cnt = 1; + operands[1] = 0; + } + + return avctp_send_req(session, AVC_CTYPE_CONTROL, + AVC_SUBUNIT_PANEL, AVC_OP_PASSTHROUGH, + iov, iov_cnt, NULL, NULL); +} + +static gboolean repeat_timeout(gpointer user_data) +{ + struct avctp *session = user_data; + + avctp_passthrough_release(session, session->key.op, session->key.params, + session->key.params_len); + avctp_passthrough_press(session, session->key.op, session->key.params, + session->key.params_len); + + return TRUE; +} + +static void release_pressed(struct avctp *session) +{ + avctp_passthrough_release(session, session->key.op, session->key.params, + session->key.params_len); + + if (session->key.timer > 0) + g_source_remove(session->key.timer); + + session->key.timer = 0; +} + +static bool set_pressed(struct avctp *session, uint8_t op, uint8_t *params, + size_t params_len) +{ + if (session->key.timer > 0) { + if (session->key.op == op) + return TRUE; + release_pressed(session); + } + + if (op != AVC_FAST_FORWARD && op != AVC_REWIND) + return FALSE; + + session->key.op = op; + session->key.params = params; + session->key.params_len = params_len; + session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT, + repeat_timeout, + session); + + return TRUE; +} + +static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code, + uint8_t subunit, uint8_t *operands, + size_t operand_count, void *user_data) +{ + uint8_t *params; + size_t params_len; + + DBG("code 0x%02x operand_count %zd", code, operand_count); + + if (code != AVC_CTYPE_ACCEPTED) + return FALSE; + + if (operands[0] == AVC_VENDOR_UNIQUE) { + params = &operands[2]; + params_len = operand_count - 2; + } else { + params = NULL; + params_len = 0; + } + + if (set_pressed(session, operands[0], params, params_len)) + return FALSE; + + avctp_passthrough_release(session, operands[0], params, params_len); + + return FALSE; +} + +int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params, + size_t params_len) +{ + /* Auto release if key pressed */ + if (session->key.timer > 0) + release_pressed(session); + + return avctp_passthrough_press(session, op, params, params_len); +} + +int avctp_send_vendor(struct avctp *session, uint8_t transaction, + uint8_t code, uint8_t subunit, + const struct iovec *iov, int iov_cnt) +{ + struct avctp_channel *control = session->control; + + if (control == NULL) + return -ENOTCONN; + + return avctp_send(control, transaction, AVCTP_RESPONSE, code, subunit, + AVC_OP_VENDORDEP, iov, iov_cnt); +} + +int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit, + const struct iovec *iov, int iov_cnt, + avctp_rsp_cb func, void *user_data) +{ + return avctp_send_req(session, code, subunit, AVC_OP_VENDORDEP, iov, + iov_cnt, func, user_data); +} + +unsigned int avctp_register_passthrough_handler(struct avctp *session, + avctp_passthrough_cb cb, + void *user_data) +{ + struct avctp_channel *control = session->control; + struct avctp_passthrough_handler *handler; + static unsigned int id = 0; + + if (control == NULL || session->handler != NULL) + return 0; + + handler = g_new(struct avctp_passthrough_handler, 1); + handler->cb = cb; + handler->user_data = user_data; + handler->id = ++id; + + session->handler = handler; + + return handler->id; +} + +bool avctp_unregister_passthrough_handler(struct avctp *session, + unsigned int id) +{ + if (session->handler == NULL) + return false; + + if (session->handler->id != id) + return false; + + g_free(session->handler); + session->handler = NULL; + return true; +} + +unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode, + avctp_control_pdu_cb cb, + void *user_data) +{ + struct avctp_channel *control = session->control; + struct avctp_pdu_handler *handler; + static unsigned int id = 0; + + if (control == NULL) + return 0; + + handler = find_handler(control->handlers, opcode); + if (handler) + return 0; + + handler = g_new(struct avctp_pdu_handler, 1); + handler->opcode = opcode; + handler->cb = cb; + handler->user_data = user_data; + handler->id = ++id; + + control->handlers = g_slist_append(control->handlers, handler); + + return handler->id; +} + +unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, + avctp_browsing_pdu_cb cb, + void *user_data, + avctp_destroy_cb_t destroy) +{ + struct avctp_channel *browsing = session->browsing; + struct avctp_browsing_pdu_handler *handler; + static unsigned int id = 0; + + if (browsing == NULL) + return 0; + + if (browsing->handlers != NULL) + return 0; + + handler = g_new(struct avctp_browsing_pdu_handler, 1); + handler->cb = cb; + handler->user_data = user_data; + handler->id = ++id; + handler->destroy = destroy; + + browsing->handlers = g_slist_append(browsing->handlers, handler); + + return handler->id; +} + +bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id) +{ + struct avctp_channel *control = session->control; + GSList *l; + + if (!control) + return false; + + for (l = control->handlers; l; l = g_slist_next(l)) { + struct avctp_pdu_handler *handler = l->data; + + if (handler->id != id) + continue; + + control->handlers = g_slist_remove(control->handlers, handler); + g_free(handler); + return true; + } + + return false; +} + +bool avctp_unregister_browsing_pdu_handler(struct avctp *session, + unsigned int id) +{ + struct avctp_channel *browsing = session->browsing; + GSList *l; + + if (browsing == NULL) + return false; + + for (l = browsing->handlers; l; l = g_slist_next(l)) { + struct avctp_browsing_pdu_handler *handler = l->data; + + if (handler->id != id) + continue; + + browsing->handlers = g_slist_remove(browsing->handlers, + handler); + g_free(handler); + return true; + } + + return false; +} + +struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version) +{ + struct avctp *session; + struct avctp_channel *control; + GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + session = g_new0(struct avctp, 1); + session->version = version; + + control = avctp_channel_create(session, fd, imtu, omtu, NULL); + if (!control) { + g_free(session); + return NULL; + } + + session->uinput = -1; + session->control = control; + session->passthrough_id = avctp_register_pdu_handler(session, + AVC_OP_PASSTHROUGH, + handle_panel_passthrough, + NULL); + session->unit_id = avctp_register_pdu_handler(session, + AVC_OP_UNITINFO, + handle_unit_info, + NULL); + session->subunit_id = avctp_register_pdu_handler(session, + AVC_OP_SUBUNITINFO, + handle_subunit_info, + NULL); + + control->watch = g_io_add_watch(session->control->io, cond, + (GIOFunc) session_cb, session); + + return session; +} + +int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu, + size_t omtu) +{ + struct avctp_channel *browsing; + GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + if (session->browsing) + return -EISCONN; + + browsing = avctp_channel_create(session, fd, imtu, omtu, + avctp_destroy_browsing); + if (!browsing) + return -EINVAL; + + session->browsing = browsing; + browsing->watch = g_io_add_watch(session->browsing->io, cond, + (GIOFunc) session_browsing_cb, session); + + return 0; +} + +void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb, + void *user_data) +{ + session->destroy = cb; + session->data = user_data; +} + +void avctp_shutdown(struct avctp *session) +{ + if (!session) + return; + + if (session->browsing) + avctp_channel_destroy(session->browsing); + + if (session->control) + avctp_channel_destroy(session->control); + + if (session->destroy) + session->destroy(session->data); + + g_free(session->handler); + + if (session->key.timer > 0) + g_source_remove(session->key.timer); + + if (session->uinput >= 0) { + DBG("AVCTP: closing uinput"); + + ioctl(session->uinput, UI_DEV_DESTROY); + close(session->uinput); + session->uinput = -1; + } + + g_free(session); +} diff -Nru bluez-4.101/android/avctp.h bluez-5.23/android/avctp.h --- bluez-4.101/android/avctp.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avctp.h 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,183 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#define AVCTP_CONTROL_PSM 23 +#define AVCTP_BROWSING_PSM 27 + +#define AVCTP_HEADER_LENGTH 3 +#define AVC_HEADER_LENGTH 3 + +#define AVC_DATA_OFFSET AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH +#define AVC_DATA_MTU 512 + +/* ctype entries */ +#define AVC_CTYPE_CONTROL 0x0 +#define AVC_CTYPE_STATUS 0x1 +#define AVC_CTYPE_NOTIFY 0x3 +#define AVC_CTYPE_NOT_IMPLEMENTED 0x8 +#define AVC_CTYPE_ACCEPTED 0x9 +#define AVC_CTYPE_REJECTED 0xA +#define AVC_CTYPE_STABLE 0xC +#define AVC_CTYPE_CHANGED 0xD +#define AVC_CTYPE_INTERIM 0xF + +/* opcodes */ +#define AVC_OP_VENDORDEP 0x00 +#define AVC_OP_UNITINFO 0x30 +#define AVC_OP_SUBUNITINFO 0x31 +#define AVC_OP_PASSTHROUGH 0x7c + +/* subunits of interest */ +#define AVC_SUBUNIT_PANEL 0x09 + +/* operands in passthrough commands */ +#define AVC_SELECT 0x00 +#define AVC_UP 0x01 +#define AVC_DOWN 0x02 +#define AVC_LEFT 0x03 +#define AVC_RIGHT 0x04 +#define AVC_ROOT_MENU 0x09 +#define AVC_CONTENTS_MENU 0x0b +#define AVC_FAVORITE_MENU 0x0c +#define AVC_EXIT 0x0d +#define AVC_ON_DEMAND_MENU 0x0e +#define AVC_APPS_MENU 0x0f +#define AVC_0 0x20 +#define AVC_1 0x21 +#define AVC_2 0x22 +#define AVC_3 0x23 +#define AVC_4 0x24 +#define AVC_5 0x25 +#define AVC_6 0x26 +#define AVC_7 0x27 +#define AVC_8 0x28 +#define AVC_9 0x29 +#define AVC_DOT 0x2a +#define AVC_ENTER 0x2b +#define AVC_CHANNEL_UP 0x30 +#define AVC_CHANNEL_DOWN 0x31 +#define AVC_CHANNEL_PREVIOUS 0x32 +#define AVC_INPUT_SELECT 0x34 +#define AVC_INFO 0x35 +#define AVC_HELP 0x36 +#define AVC_PAGE_UP 0x37 +#define AVC_PAGE_DOWN 0x38 +#define AVC_LOCK 0x3a +#define AVC_POWER 0x40 +#define AVC_VOLUME_UP 0x41 +#define AVC_VOLUME_DOWN 0x42 +#define AVC_MUTE 0x43 +#define AVC_PLAY 0x44 +#define AVC_STOP 0x45 +#define AVC_PAUSE 0x46 +#define AVC_RECORD 0x47 +#define AVC_REWIND 0x48 +#define AVC_FAST_FORWARD 0x49 +#define AVC_EJECT 0x4a +#define AVC_FORWARD 0x4b +#define AVC_BACKWARD 0x4c +#define AVC_LIST 0x4d +#define AVC_F1 0x71 +#define AVC_F2 0x72 +#define AVC_F3 0x73 +#define AVC_F4 0x74 +#define AVC_F5 0x75 +#define AVC_F6 0x76 +#define AVC_F7 0x77 +#define AVC_F8 0x78 +#define AVC_F9 0x79 +#define AVC_RED 0x7a +#define AVC_GREEN 0x7b +#define AVC_BLUE 0x7c +#define AVC_YELLOW 0x7c + +#define AVC_VENDOR_UNIQUE 0x7e + +#define AVC_VENDOR_NEXT_GROUP 0x00 +#define AVC_VENDOR_PREV_GROUP 0x01 + +struct avctp; + +typedef bool (*avctp_passthrough_cb) (struct avctp *session, + uint8_t op, bool pressed, + void *user_data); +typedef ssize_t (*avctp_control_pdu_cb) (struct avctp *session, + uint8_t transaction, uint8_t *code, + uint8_t *subunit, uint8_t *operands, + size_t operand_count, void *user_data); +typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code, + uint8_t subunit, uint8_t *operands, + size_t operand_count, void *user_data); +typedef gboolean (*avctp_browsing_rsp_cb) (struct avctp *session, + uint8_t *operands, size_t operand_count, + void *user_data); +typedef ssize_t (*avctp_browsing_pdu_cb) (struct avctp *session, + uint8_t transaction, + uint8_t *operands, size_t operand_count, + void *user_data); + +typedef void (*avctp_destroy_cb_t) (void *user_data); + +struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version); +void avctp_set_destroy_cb(struct avctp *session, avctp_destroy_cb_t cb, + void *user_data); + +int avctp_init_uinput(struct avctp *session, const char *name, + const char *address); +int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu, + size_t omtu); + +void avctp_shutdown(struct avctp *session); + +unsigned int avctp_register_passthrough_handler(struct avctp *session, + avctp_passthrough_cb cb, + void *user_data); +bool avctp_unregister_passthrough_handler(struct avctp *session, + unsigned int id); + +unsigned int avctp_register_pdu_handler(struct avctp *session, uint8_t opcode, + avctp_control_pdu_cb cb, + void *user_data); +bool avctp_unregister_pdu_handler(struct avctp *session, unsigned int id); + +unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, + avctp_browsing_pdu_cb cb, + void *user_data, + avctp_destroy_cb_t destroy); +bool avctp_unregister_browsing_pdu_handler(struct avctp *session, + unsigned int id); + +int avctp_send_passthrough(struct avctp *session, uint8_t op, uint8_t *params, + size_t params_len); +int avctp_send_vendor(struct avctp *session, uint8_t transaction, + uint8_t code, uint8_t subunit, + const struct iovec *iov, int iov_cnt); +int avctp_send_vendor_req(struct avctp *session, uint8_t code, uint8_t subunit, + const struct iovec *iov, int iov_cnt, + avctp_rsp_cb func, void *user_data); +int avctp_send_browsing(struct avctp *session, uint8_t transaction, + const struct iovec *iov, int iov_cnt); +int avctp_send_browsing_req(struct avctp *session, + const struct iovec *iov, int iov_cnt, + avctp_browsing_rsp_cb func, void *user_data); diff -Nru bluez-4.101/android/avdtp.c bluez-5.23/android/avdtp.c --- bluez-4.101/android/avdtp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avdtp.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,3452 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "src/log.h" +#include "avdtp.h" +#include "../profiles/audio/a2dp-codecs.h" + +#define MAX_SEID 0x3E + +#ifndef MAX +# define MAX(x, y) ((x) > (y) ? (x) : (y)) +#endif + +#define AVDTP_DISCOVER 0x01 +#define AVDTP_GET_CAPABILITIES 0x02 +#define AVDTP_SET_CONFIGURATION 0x03 +#define AVDTP_GET_CONFIGURATION 0x04 +#define AVDTP_RECONFIGURE 0x05 +#define AVDTP_OPEN 0x06 +#define AVDTP_START 0x07 +#define AVDTP_CLOSE 0x08 +#define AVDTP_SUSPEND 0x09 +#define AVDTP_ABORT 0x0A +#define AVDTP_SECURITY_CONTROL 0x0B +#define AVDTP_GET_ALL_CAPABILITIES 0x0C +#define AVDTP_DELAY_REPORT 0x0D + +#define AVDTP_PKT_TYPE_SINGLE 0x00 +#define AVDTP_PKT_TYPE_START 0x01 +#define AVDTP_PKT_TYPE_CONTINUE 0x02 +#define AVDTP_PKT_TYPE_END 0x03 + +#define AVDTP_MSG_TYPE_COMMAND 0x00 +#define AVDTP_MSG_TYPE_GEN_REJECT 0x01 +#define AVDTP_MSG_TYPE_ACCEPT 0x02 +#define AVDTP_MSG_TYPE_REJECT 0x03 + +#define REQ_TIMEOUT 6 +#define ABORT_TIMEOUT 2 +#define DISCONNECT_TIMEOUT 1 +#define START_TIMEOUT 1 + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avdtp_common_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; +} __attribute__ ((packed)); + +struct avdtp_single_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t signal_id:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct avdtp_start_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; + uint8_t no_of_packets; + uint8_t signal_id:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct avdtp_continue_header { + uint8_t message_type:2; + uint8_t packet_type:2; + uint8_t transaction:4; +} __attribute__ ((packed)); + +struct seid_info { + uint8_t rfa0:1; + uint8_t inuse:1; + uint8_t seid:6; + uint8_t rfa2:3; + uint8_t type:1; + uint8_t media_type:4; +} __attribute__ ((packed)); + +struct seid { + uint8_t rfa0:2; + uint8_t seid:6; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avdtp_common_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; +} __attribute__ ((packed)); + +struct avdtp_single_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; + uint8_t rfa0:2; + uint8_t signal_id:6; +} __attribute__ ((packed)); + +struct avdtp_start_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; + uint8_t no_of_packets; + uint8_t rfa0:2; + uint8_t signal_id:6; +} __attribute__ ((packed)); + +struct avdtp_continue_header { + uint8_t transaction:4; + uint8_t packet_type:2; + uint8_t message_type:2; +} __attribute__ ((packed)); + +struct seid_info { + uint8_t seid:6; + uint8_t inuse:1; + uint8_t rfa0:1; + uint8_t media_type:4; + uint8_t type:1; + uint8_t rfa2:3; +} __attribute__ ((packed)); + +struct seid { + uint8_t seid:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +/* packets */ + +struct discover_resp { + struct seid_info seps[0]; +} __attribute__ ((packed)); + +struct getcap_resp { + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct start_req { + struct seid first_seid; + struct seid other_seids[0]; +} __attribute__ ((packed)); + +struct suspend_req { + struct seid first_seid; + struct seid other_seids[0]; +} __attribute__ ((packed)); + +struct seid_rej { + uint8_t error; +} __attribute__ ((packed)); + +struct conf_rej { + uint8_t category; + uint8_t error; +} __attribute__ ((packed)); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct seid_req { + uint8_t rfa0:2; + uint8_t acp_seid:6; +} __attribute__ ((packed)); + +struct setconf_req { + uint8_t rfa0:2; + uint8_t acp_seid:6; + uint8_t rfa1:2; + uint8_t int_seid:6; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct stream_rej { + uint8_t rfa0:2; + uint8_t acp_seid:6; + uint8_t error; +} __attribute__ ((packed)); + +struct reconf_req { + uint8_t rfa0:2; + uint8_t acp_seid:6; + + uint8_t serv_cap; + uint8_t serv_cap_len; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct delay_req { + uint8_t rfa0:2; + uint8_t acp_seid:6; + uint16_t delay; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct seid_req { + uint8_t acp_seid:6; + uint8_t rfa0:2; +} __attribute__ ((packed)); + +struct setconf_req { + uint8_t acp_seid:6; + uint8_t rfa0:2; + uint8_t int_seid:6; + uint8_t rfa1:2; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct stream_rej { + uint8_t acp_seid:6; + uint8_t rfa0:2; + uint8_t error; +} __attribute__ ((packed)); + +struct reconf_req { + uint8_t acp_seid:6; + uint8_t rfa0:2; + + uint8_t serv_cap; + uint8_t serv_cap_len; + + uint8_t caps[0]; +} __attribute__ ((packed)); + +struct delay_req { + uint8_t acp_seid:6; + uint8_t rfa0:2; + uint16_t delay; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +struct in_buf { + gboolean active; + int no_of_packets; + uint8_t transaction; + uint8_t message_type; + uint8_t signal_id; + uint8_t buf[1024]; + uint8_t data_size; +}; + +struct pending_req { + uint8_t transaction; + uint8_t signal_id; + void *data; + size_t data_size; + struct avdtp_stream *stream; /* Set if the request targeted a stream */ + guint timeout; + gboolean collided; +}; + +struct avdtp_remote_sep { + uint8_t seid; + uint8_t type; + uint8_t media_type; + struct avdtp_service_capability *codec; + gboolean delay_reporting; + GSList *caps; /* of type struct avdtp_service_capability */ + struct avdtp_stream *stream; +}; + +struct avdtp_local_sep { + avdtp_state_t state; + struct avdtp_stream *stream; + struct seid_info info; + uint8_t codec; + uint32_t vndcodec_vendor; + uint16_t vndcodec_codec; + gboolean delay_reporting; + GSList *caps; + struct avdtp_sep_ind *ind; + struct avdtp_sep_cfm *cfm; + void *user_data; +}; + +struct stream_callback { + avdtp_stream_state_cb cb; + void *user_data; + unsigned int id; +}; + +struct discover_callback { + unsigned int id; + avdtp_discover_cb_t cb; + void *user_data; +}; + +struct disconnect_callback { + unsigned int id; + avdtp_disconnect_cb_t cb; + void *user_data; +}; + +struct avdtp_stream { + GIOChannel *io; + uint16_t imtu; + uint16_t omtu; + struct avdtp *session; + struct avdtp_local_sep *lsep; + uint8_t rseid; + GSList *caps; + GSList *callbacks; + struct avdtp_service_capability *codec; + guint io_id; /* Transport GSource ID */ + guint timer; /* Waiting for other side to close or open + * the transport channel */ + gboolean open_acp; /* If we are in ACT role for Open */ + gboolean close_int; /* If we are in INT role for Close */ + gboolean abort_int; /* If we are in INT role for Abort */ + guint start_timer; /* Wait START command timer */ + gboolean delay_reporting; + uint16_t delay; /* AVDTP 1.3 Delay Reporting feature */ + gboolean starting; /* only valid while sep state == OPEN */ +}; + +/* Structure describing an AVDTP connection between two devices */ + +struct avdtp { + unsigned int ref; + + uint16_t version; + + struct avdtp_server *server; + + guint auth_id; + + GIOChannel *io; + guint io_id; + + GSList *seps; /* Elements of type struct avdtp_remote_sep * */ + + GSList *streams; /* Elements of type struct avdtp_stream * */ + + GSList *req_queue; /* Elements of type struct pending_req * */ + GSList *prio_queue; /* Same as req_queue but is processed before it */ + + struct avdtp_stream *pending_open; + + uint16_t imtu; + uint16_t omtu; + + struct in_buf in; + + char *buf; + + struct discover_callback *discover; + struct pending_req *req; + + GSList *disconnect; + + bool shutdown; +}; + +static GSList *lseps = NULL; + +static int send_request(struct avdtp *session, gboolean priority, + struct avdtp_stream *stream, uint8_t signal_id, + void *buffer, size_t size); +static gboolean avdtp_parse_resp(struct avdtp *session, + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size); +static gboolean avdtp_parse_rej(struct avdtp *session, + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size); +static int process_queue(struct avdtp *session); +static void avdtp_sep_set_state(struct avdtp *session, + struct avdtp_local_sep *sep, + avdtp_state_t state); + +static const char *avdtp_statestr(avdtp_state_t state) +{ + switch (state) { + case AVDTP_STATE_IDLE: + return "IDLE"; + case AVDTP_STATE_CONFIGURED: + return "CONFIGURED"; + case AVDTP_STATE_OPEN: + return "OPEN"; + case AVDTP_STATE_STREAMING: + return "STREAMING"; + case AVDTP_STATE_CLOSING: + return "CLOSING"; + case AVDTP_STATE_ABORTING: + return "ABORTING"; + default: + return ""; + } +} + +static gboolean try_send(int sk, void *data, size_t len) +{ + int err; + + do { + err = send(sk, data, len, 0); + } while (err < 0 && errno == EINTR); + + if (err < 0) { + error("send: %s (%d)", strerror(errno), errno); + return FALSE; + } else if ((size_t) err != len) { + error("try_send: complete buffer not sent (%d/%zu bytes)", + err, len); + return FALSE; + } + + return TRUE; +} + +static gboolean avdtp_send(struct avdtp *session, uint8_t transaction, + uint8_t message_type, uint8_t signal_id, + void *data, size_t len) +{ + unsigned int cont_fragments, sent; + struct avdtp_start_header start; + struct avdtp_continue_header cont; + int sock; + + if (session->io == NULL) { + error("avdtp_send: session is closed"); + return FALSE; + } + + sock = g_io_channel_unix_get_fd(session->io); + + /* Single packet - no fragmentation */ + if (sizeof(struct avdtp_single_header) + len <= session->omtu) { + struct avdtp_single_header single; + + memset(&single, 0, sizeof(single)); + + single.transaction = transaction; + single.packet_type = AVDTP_PKT_TYPE_SINGLE; + single.message_type = message_type; + single.signal_id = signal_id; + + memcpy(session->buf, &single, sizeof(single)); + memcpy(session->buf + sizeof(single), data, len); + + return try_send(sock, session->buf, sizeof(single) + len); + } + + /* Check if there is enough space to start packet */ + if (session->omtu < sizeof(start)) { + error("No enough space to fragment packet"); + return FALSE; + } + + /* Count the number of needed fragments */ + cont_fragments = (len - (session->omtu - sizeof(start))) / + (session->omtu - sizeof(cont)) + 1; + + DBG("%zu bytes split into %d fragments", len, cont_fragments + 1); + + /* Send the start packet */ + memset(&start, 0, sizeof(start)); + start.transaction = transaction; + start.packet_type = AVDTP_PKT_TYPE_START; + start.message_type = message_type; + start.no_of_packets = cont_fragments + 1; + start.signal_id = signal_id; + + memcpy(session->buf, &start, sizeof(start)); + memcpy(session->buf + sizeof(start), data, + session->omtu - sizeof(start)); + + if (!try_send(sock, session->buf, session->omtu)) + return FALSE; + + DBG("first packet with %zu bytes sent", session->omtu - sizeof(start)); + + sent = session->omtu - sizeof(start); + + /* Send the continue fragments and the end packet */ + while (sent < len) { + int left, to_copy; + + left = len - sent; + if (left + sizeof(cont) > session->omtu) { + cont.packet_type = AVDTP_PKT_TYPE_CONTINUE; + to_copy = session->omtu - sizeof(cont); + DBG("sending continue with %d bytes", to_copy); + } else { + cont.packet_type = AVDTP_PKT_TYPE_END; + to_copy = left; + DBG("sending end with %d bytes", to_copy); + } + + cont.transaction = transaction; + cont.message_type = message_type; + + memcpy(session->buf, &cont, sizeof(cont)); + memcpy(session->buf + sizeof(cont), data + sent, to_copy); + + if (!try_send(sock, session->buf, to_copy + sizeof(cont))) + return FALSE; + + sent += to_copy; + } + + return TRUE; +} + +static void pending_req_free(void *data) +{ + struct pending_req *req = data; + + if (req->timeout) + g_source_remove(req->timeout); + g_free(req->data); + g_free(req); +} + +static void close_stream(struct avdtp_stream *stream) +{ + int sock; + + if (stream->io == NULL) + return; + + sock = g_io_channel_unix_get_fd(stream->io); + + shutdown(sock, SHUT_RDWR); + + g_io_channel_shutdown(stream->io, FALSE, NULL); + + g_io_channel_unref(stream->io); + stream->io = NULL; +} + +static gboolean stream_close_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + + DBG("Timed out waiting for peer to close the transport channel"); + + stream->timer = 0; + + close_stream(stream); + + return FALSE; +} + +static gboolean stream_open_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + + DBG("Timed out waiting for peer to open the transport channel"); + + stream->timer = 0; + + stream->session->pending_open = NULL; + + avdtp_abort(stream->session, stream); + + return FALSE; +} + +void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id) +{ + err->category = category; + + if (category == AVDTP_ERRNO) + err->err.posix_errno = id; + else + err->err.error_code = id; +} + +uint8_t avdtp_error_category(struct avdtp_error *err) +{ + return err->category; +} + +int avdtp_error_error_code(struct avdtp_error *err) +{ + assert(err->category != AVDTP_ERRNO); + return err->err.error_code; +} + +int avdtp_error_posix_errno(struct avdtp_error *err) +{ + assert(err->category == AVDTP_ERRNO); + return err->err.posix_errno; +} + +static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session, + uint8_t rseid) +{ + GSList *l; + + for (l = session->streams; l != NULL; l = g_slist_next(l)) { + struct avdtp_stream *stream = l->data; + + if (stream->rseid == rseid) + return stream; + } + + return NULL; +} + +static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) +{ + GSList *l; + + for (l = seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_remote_sep *sep = l->data; + + if (sep->seid == seid) + return sep; + } + + return NULL; +} + +static void stream_free(void *data) +{ + struct avdtp_stream *stream = data; + struct avdtp_remote_sep *rsep; + + stream->lsep->info.inuse = 0; + stream->lsep->stream = NULL; + + rsep = find_remote_sep(stream->session->seps, stream->rseid); + if (rsep) + rsep->stream = NULL; + + if (stream->timer) + g_source_remove(stream->timer); + + if (stream->start_timer > 0) + g_source_remove(stream->start_timer); + + if (stream->io) + close_stream(stream); + + if (stream->io_id) + g_source_remove(stream->io_id); + + g_slist_free_full(stream->callbacks, g_free); + g_slist_free_full(stream->caps, g_free); + + g_free(stream); +} + +static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avdtp_stream *stream = data; + struct avdtp_local_sep *sep = stream->lsep; + + if (stream->close_int && sep->cfm && sep->cfm->close) + sep->cfm->close(stream->session, sep, stream, NULL, + sep->user_data); + + if (!(cond & G_IO_NVAL)) + close_stream(stream); + + stream->io_id = 0; + + if (!stream->abort_int) + avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); + + return FALSE; +} + +static void handle_transport_connect(struct avdtp *session, GIOChannel *io, + uint16_t imtu, uint16_t omtu) +{ + struct avdtp_stream *stream = session->pending_open; + struct avdtp_local_sep *sep = stream->lsep; + + session->pending_open = NULL; + + if (stream->timer) { + g_source_remove(stream->timer); + stream->timer = 0; + } + + if (io == NULL) + return; + + if (stream->io == NULL) + stream->io = g_io_channel_ref(io); + + stream->omtu = omtu; + stream->imtu = imtu; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + + stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) transport_cb, stream); +} + +static int pending_req_cmp(gconstpointer a, gconstpointer b) +{ + const struct pending_req *req = a; + const struct avdtp_stream *stream = b; + + if (req->stream == stream) + return 0; + + return -1; +} + +static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream) +{ + GSList *l; + struct pending_req *req; + + while ((l = g_slist_find_custom(session->prio_queue, stream, + pending_req_cmp))) { + req = l->data; + pending_req_free(req); + session->prio_queue = g_slist_remove(session->prio_queue, req); + } + + while ((l = g_slist_find_custom(session->req_queue, stream, + pending_req_cmp))) { + req = l->data; + pending_req_free(req); + session->req_queue = g_slist_remove(session->req_queue, req); + } +} + +static void handle_unanswered_req(struct avdtp *session, + struct avdtp_stream *stream) +{ + struct pending_req *req; + struct avdtp_local_sep *lsep; + struct avdtp_error err; + + if (session->req->signal_id == AVDTP_ABORT) { + /* Avoid freeing the Abort request here */ + DBG("handle_unanswered_req: Abort req, returning"); + session->req->stream = NULL; + return; + } + + req = session->req; + session->req = NULL; + + avdtp_error_init(&err, AVDTP_ERRNO, EIO); + + lsep = stream->lsep; + + switch (req->signal_id) { + case AVDTP_RECONFIGURE: + error("No reply to Reconfigure request"); + if (lsep && lsep->cfm && lsep->cfm->reconfigure) + lsep->cfm->reconfigure(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_OPEN: + error("No reply to Open request"); + if (lsep && lsep->cfm && lsep->cfm->open) + lsep->cfm->open(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_START: + error("No reply to Start request"); + if (lsep && lsep->cfm && lsep->cfm->start) + lsep->cfm->start(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_SUSPEND: + error("No reply to Suspend request"); + if (lsep && lsep->cfm && lsep->cfm->suspend) + lsep->cfm->suspend(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_CLOSE: + error("No reply to Close request"); + if (lsep && lsep->cfm && lsep->cfm->close) + lsep->cfm->close(session, lsep, stream, &err, + lsep->user_data); + break; + case AVDTP_SET_CONFIGURATION: + error("No reply to SetConfiguration request"); + if (lsep && lsep->cfm && lsep->cfm->set_configuration) + lsep->cfm->set_configuration(session, lsep, stream, + &err, lsep->user_data); + } + + pending_req_free(req); +} + +static void avdtp_sep_set_state(struct avdtp *session, + struct avdtp_local_sep *sep, + avdtp_state_t state) +{ + struct avdtp_stream *stream = sep->stream; + avdtp_state_t old_state; + struct avdtp_error err, *err_ptr = NULL; + GSList *l; + + if (!stream) { + error("Error changing sep state: stream not available"); + return; + } + + if (sep->state == state) { + avdtp_error_init(&err, AVDTP_ERRNO, EIO); + DBG("stream state change failed: %s", avdtp_strerror(&err)); + err_ptr = &err; + } else { + err_ptr = NULL; + DBG("stream state changed: %s -> %s", + avdtp_statestr(sep->state), + avdtp_statestr(state)); + } + + old_state = sep->state; + sep->state = state; + + switch (state) { + case AVDTP_STATE_CONFIGURED: + if (sep->info.type == AVDTP_SEP_TYPE_SINK) + avdtp_delay_report(session, stream, stream->delay); + break; + case AVDTP_STATE_OPEN: + stream->starting = FALSE; + break; + case AVDTP_STATE_STREAMING: + if (stream->start_timer) { + g_source_remove(stream->start_timer); + stream->start_timer = 0; + } + stream->open_acp = FALSE; + break; + case AVDTP_STATE_CLOSING: + case AVDTP_STATE_ABORTING: + if (stream->start_timer) { + g_source_remove(stream->start_timer); + stream->start_timer = 0; + } + break; + case AVDTP_STATE_IDLE: + if (stream->start_timer) { + g_source_remove(stream->start_timer); + stream->start_timer = 0; + } + if (session->pending_open == stream) + handle_transport_connect(session, NULL, 0, 0); + if (session->req && session->req->stream == stream) + handle_unanswered_req(session, stream); + /* Remove pending commands for this stream from the queue */ + cleanup_queue(session, stream); + break; + default: + break; + } + + l = stream->callbacks; + while (l != NULL) { + struct stream_callback *cb = l->data; + l = g_slist_next(l); + cb->cb(stream, old_state, state, err_ptr, cb->user_data); + } + + if (state == AVDTP_STATE_IDLE && + g_slist_find(session->streams, stream)) { + session->streams = g_slist_remove(session->streams, stream); + stream_free(stream); + } + + if (session->io && session->shutdown && session->streams == NULL) { + int sock = g_io_channel_unix_get_fd(session->io); + shutdown(sock, SHUT_RDWR); + } +} + +static void finalize_discovery(struct avdtp *session, int err) +{ + struct discover_callback *discover = session->discover; + struct avdtp_error avdtp_err; + + if (!discover) + return; + + session->discover = NULL; + + avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err); + + if (discover->id > 0) + g_source_remove(discover->id); + + if (discover->cb) + discover->cb(session, session->seps, err ? &avdtp_err : NULL, + discover->user_data); + g_free(discover); +} + +static void release_stream(struct avdtp_stream *stream, struct avdtp *session) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->abort && + (sep->state != AVDTP_STATE_ABORTING || + stream->abort_int)) + sep->cfm->abort(session, sep, stream, NULL, sep->user_data); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); +} + +static void sep_free(gpointer data) +{ + struct avdtp_remote_sep *sep = data; + + g_slist_free_full(sep->caps, g_free); + g_free(sep); +} + +static void avdtp_free(void *data) +{ + struct avdtp *session = data; + + DBG("%p", session); + + g_slist_free_full(session->streams, stream_free); + + if (session->io) { + g_io_channel_shutdown(session->io, FALSE, NULL); + g_io_channel_unref(session->io); + } + + if (session->io_id) { + g_source_remove(session->io_id); + session->io_id = 0; + } + + if (session->req) + pending_req_free(session->req); + + g_slist_free_full(session->req_queue, pending_req_free); + g_slist_free_full(session->prio_queue, pending_req_free); + g_slist_free_full(session->seps, sep_free); + g_slist_free_full(session->disconnect, g_free); + + g_free(session->buf); + + g_free(session); +} + +static void process_disconnect(void *data) +{ + struct disconnect_callback *callback = data; + + callback->cb(callback->user_data); + + g_free(callback); +} + +static void connection_lost(struct avdtp *session, int err) +{ + DBG("Disconnected: %s (%d)", strerror(err), err); + + g_slist_foreach(session->streams, (GFunc) release_stream, session); + session->streams = NULL; + + avdtp_ref(session); + + finalize_discovery(session, err); + + g_slist_free_full(session->disconnect, process_disconnect); + session->disconnect = NULL; + + avdtp_unref(session); +} + +void avdtp_unref(struct avdtp *session) +{ + if (!session) + return; + + session->ref--; + + DBG("%p: ref=%d", session, session->ref); + + if (session->ref > 0) + return; + + finalize_discovery(session, ECONNABORTED); + + avdtp_free(session); +} + +struct avdtp *avdtp_ref(struct avdtp *session) +{ + session->ref++; + + DBG("%p: ref=%d", session, session->ref); + + return session; +} + +static struct avdtp_local_sep *find_local_sep_by_seid(uint8_t seid) +{ + GSList *l; + + for (l = lseps; l != NULL; l = g_slist_next(l)) { + struct avdtp_local_sep *sep = l->data; + + if (sep->info.seid == seid) + return sep; + } + + return NULL; +} + +struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, + struct avdtp_local_sep *lsep) +{ + GSList *l; + + if (lsep->info.inuse) + return NULL; + + for (l = session->seps; l != NULL; l = g_slist_next(l)) { + struct avdtp_remote_sep *sep = l->data; + struct avdtp_service_capability *cap; + struct avdtp_media_codec_capability *codec_data; + + /* Type must be different: source <-> sink */ + if (sep->type == lsep->info.type) + continue; + + if (sep->media_type != lsep->info.media_type) + continue; + + if (!sep->codec) + continue; + + cap = sep->codec; + codec_data = (void *) cap->data; + + if (codec_data->media_codec_type != lsep->codec) + continue; + + /* FIXME: Add Vendor Specific Codec match to SEP callback */ + if (lsep->codec == A2DP_CODEC_VENDOR) { + a2dp_vendor_codec_t *vndcodec = + (void *) codec_data->data; + + if (btohl(vndcodec->vendor_id) != lsep->vndcodec_vendor) + continue; + + if (btohs(vndcodec->codec_id) != lsep->vndcodec_codec) + continue; + } + + if (sep->stream == NULL) + return sep; + } + + return NULL; +} + +static GSList *caps_to_list(uint8_t *data, int size, + struct avdtp_service_capability **codec, + gboolean *delay_reporting) +{ + GSList *caps; + int processed; + + if (delay_reporting) + *delay_reporting = FALSE; + + for (processed = 0, caps = NULL; processed + 2 <= size;) { + struct avdtp_service_capability *cap; + uint8_t length, category; + + category = data[0]; + length = data[1]; + + if (processed + 2 + length > size) { + error("Invalid capability data in getcap resp"); + break; + } + + cap = g_malloc(sizeof(struct avdtp_service_capability) + + length); + memcpy(cap, data, 2 + length); + + processed += 2 + length; + data += 2 + length; + + caps = g_slist_append(caps, cap); + + if (category == AVDTP_MEDIA_CODEC && + length >= + sizeof(struct avdtp_media_codec_capability)) + *codec = cap; + else if (category == AVDTP_DELAY_REPORTING && delay_reporting) + *delay_reporting = TRUE; + } + + return caps; +} + +static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction, + uint8_t signal_id) +{ + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT, + signal_id, NULL, 0); +} + +static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction, + void *buf, int size) +{ + GSList *l; + unsigned int rsp_size, sep_count, i; + struct seid_info *seps; + gboolean ret; + + sep_count = g_slist_length(lseps); + + if (sep_count == 0) { + uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND; + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_DISCOVER, &err, sizeof(err)); + } + + rsp_size = sep_count * sizeof(struct seid_info); + + seps = g_new0(struct seid_info, sep_count); + + for (l = lseps, i = 0; l != NULL; l = l->next, i++) { + struct avdtp_local_sep *sep = l->data; + + memcpy(&seps[i], &sep->info, sizeof(struct seid_info)); + } + + ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_DISCOVER, seps, rsp_size); + g_free(seps); + + return ret; +} + +static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, unsigned int size, + gboolean get_all) +{ + GSList *l, *caps; + struct avdtp_local_sep *sep = NULL; + unsigned int rsp_size; + uint8_t err, buf[1024], *ptr = buf; + uint8_t cmd; + + cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES; + + if (size < sizeof(struct seid_req)) { + err = AVDTP_BAD_LENGTH; + goto failed; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (!sep->ind->get_capability(session, sep, &caps, &err, + sep->user_data)) + goto failed; + + for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { + struct avdtp_service_capability *cap = l->data; + + if (rsp_size + cap->length + 2 > sizeof(buf)) + break; + + memcpy(ptr, cap, cap->length + 2); + rsp_size += cap->length + 2; + ptr += cap->length + 2; + + g_free(cap); + } + + if (get_all && sep->delay_reporting) { + ptr[0] = AVDTP_DELAY_REPORTING; + ptr[1] = 0x00; + rsp_size += 2; + } + + g_slist_free(caps); + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd, + buf, rsp_size); + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd, + &err, sizeof(err)); +} + +static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream, + struct avdtp_error *err) +{ + struct conf_rej rej; + struct avdtp_local_sep *sep; + + if (err != NULL) { + rej.error = AVDTP_UNSUPPORTED_CONFIGURATION; + rej.category = err->err.error_code; + avdtp_send(session, session->in.transaction, + AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, + &rej, sizeof(rej)); + return; + } + + if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SET_CONFIGURATION, NULL, 0)) { + stream_free(stream); + return; + } + + sep = stream->lsep; + sep->stream = stream; + sep->info.inuse = 1; + session->streams = g_slist_append(session->streams, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); +} + +static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction, + struct setconf_req *req, unsigned int size) +{ + struct conf_rej rej; + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err, category = 0x00; + GSList *l; + + if (size < sizeof(struct setconf_req)) { + error("Too short getcap request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->stream) { + err = AVDTP_SEP_IN_USE; + goto failed; + } + + stream = g_new0(struct avdtp_stream, 1); + stream->session = session; + stream->lsep = sep; + stream->rseid = req->int_seid; + stream->caps = caps_to_list(req->caps, + size - sizeof(struct setconf_req), + &stream->codec, + &stream->delay_reporting); + + /* + * Verify that the Media Transport capability's length = 0. + * Reject otherwise + */ + for (l = stream->caps; l != NULL; l = g_slist_next(l)) { + struct avdtp_service_capability *cap = l->data; + + if (cap->category == AVDTP_MEDIA_TRANSPORT && + cap->length != 0) { + err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT; + goto failed_stream; + } + } + + if (stream->delay_reporting && session->version < 0x0103) + session->version = 0x0103; + + if (sep->ind && sep->ind->set_configuration) { + if (!sep->ind->set_configuration(session, sep, stream, + stream->caps, + setconf_cb, + sep->user_data)) { + err = AVDTP_UNSUPPORTED_CONFIGURATION; + category = 0x00; + goto failed_stream; + } + } else { + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SET_CONFIGURATION, NULL, 0)) { + stream_free(stream); + return FALSE; + } + + sep->stream = stream; + sep->info.inuse = 1; + session->streams = g_slist_append(session->streams, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); + } + + return TRUE; + +failed_stream: + stream_free(stream); +failed: + rej.error = err; + rej.category = category; + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); +} + +static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) +{ + GSList *l; + struct avdtp_local_sep *sep = NULL; + int rsp_size; + uint8_t err; + uint8_t buf[1024]; + uint8_t *ptr = buf; + + if (size < (int) sizeof(struct seid_req)) { + error("Too short getconf request"); + return FALSE; + } + + memset(buf, 0, sizeof(buf)); + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + if (!sep->stream || !sep->stream->caps) { + err = AVDTP_UNSUPPORTED_CONFIGURATION; + goto failed; + } + + for (l = sep->stream->caps, rsp_size = 0; l; l = g_slist_next(l)) { + struct avdtp_service_capability *cap = l->data; + + if (rsp_size + cap->length + 2 > (int) sizeof(buf)) + break; + + memcpy(ptr, cap, cap->length + 2); + rsp_size += cap->length + 2; + ptr += cap->length + 2; + } + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_GET_CONFIGURATION, buf, rsp_size); + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_GET_CONFIGURATION, &err, sizeof(err)); +} + +static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) +{ + struct conf_rej rej; + + rej.error = AVDTP_NOT_SUPPORTED_COMMAND; + rej.category = 0x00; + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_RECONFIGURE, &rej, sizeof(rej)); +} + +static void check_seid_collision(struct pending_req *req, uint8_t id) +{ + struct seid_req *seid = req->data; + + if (seid->acp_seid == id) + req->collided = TRUE; +} + +static void check_start_collision(struct pending_req *req, uint8_t id) +{ + struct start_req *start = req->data; + struct seid *seid = &start->first_seid; + int count = 1 + req->data_size - sizeof(struct start_req); + int i; + + for (i = 0; i < count; i++, seid++) { + if (seid->seid == id) { + req->collided = TRUE; + return; + } + } +} + +static void check_suspend_collision(struct pending_req *req, uint8_t id) +{ + struct suspend_req *suspend = req->data; + struct seid *seid = &suspend->first_seid; + int count = 1 + req->data_size - sizeof(struct suspend_req); + int i; + + for (i = 0; i < count; i++, seid++) { + if (seid->seid == id) { + req->collided = TRUE; + return; + } + } +} + +static void avdtp_check_collision(struct avdtp *session, uint8_t cmd, + struct avdtp_stream *stream) +{ + struct pending_req *req = session->req; + + if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT)) + return; + + if (cmd == AVDTP_ABORT) + cmd = req->signal_id; + + switch (cmd) { + case AVDTP_OPEN: + case AVDTP_CLOSE: + check_seid_collision(req, stream->rseid); + break; + case AVDTP_START: + check_start_collision(req, stream->rseid); + break; + case AVDTP_SUSPEND: + check_suspend_collision(req, stream->rseid); + break; + } +} + +static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err; + + if (size < sizeof(struct seid_req)) { + error("Too short abort request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->state != AVDTP_STATE_CONFIGURED) { + err = AVDTP_BAD_STATE; + goto failed; + } + + stream = sep->stream; + + if (sep->ind && sep->ind->open) { + if (!sep->ind->open(session, sep, stream, &err, + sep->user_data)) + goto failed; + } + + avdtp_check_collision(session, AVDTP_OPEN, stream); + + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_OPEN, NULL, 0)) + return FALSE; + + stream->open_acp = TRUE; + session->pending_open = stream; + stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, + stream_open_timeout, + stream); + + return TRUE; + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_OPEN, &err, sizeof(err)); +} + +static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction, + struct start_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct stream_rej rej; + struct seid *seid; + uint8_t err, failed_seid; + int seid_count, i; + + if (size < sizeof(struct start_req)) { + error("Too short start request"); + return FALSE; + } + + seid_count = 1 + size - sizeof(struct start_req); + + seid = &req->first_seid; + + for (i = 0; i < seid_count; i++, seid++) { + failed_seid = seid->seid; + + sep = find_local_sep_by_seid(req->first_seid.seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + stream = sep->stream; + + /* Also reject start cmd if state is not open */ + if (sep->state != AVDTP_STATE_OPEN) { + err = AVDTP_BAD_STATE; + goto failed; + } + stream->starting = TRUE; + + if (sep->ind && sep->ind->start) { + if (!sep->ind->start(session, sep, stream, &err, + sep->user_data)) + goto failed; + } + + avdtp_check_collision(session, AVDTP_START, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); + } + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_START, NULL, 0); + +failed: + DBG("Rejecting (%d)", err); + memset(&rej, 0, sizeof(rej)); + rej.acp_seid = failed_seid; + rej.error = err; + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_START, &rej, sizeof(rej)); +} + +static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err; + + if (size < sizeof(struct seid_req)) { + error("Too short close request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + if (sep->state != AVDTP_STATE_OPEN && + sep->state != AVDTP_STATE_STREAMING) { + err = AVDTP_BAD_STATE; + goto failed; + } + + stream = sep->stream; + + if (sep->ind && sep->ind->close) { + if (!sep->ind->close(session, sep, stream, &err, + sep->user_data)) + goto failed; + } + + avdtp_check_collision(session, AVDTP_CLOSE, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); + + if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_CLOSE, NULL, 0)) + return FALSE; + + stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, + stream_close_timeout, + stream); + + return TRUE; + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_CLOSE, &err, sizeof(err)); +} + +static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction, + struct suspend_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + struct stream_rej rej; + struct seid *seid; + uint8_t err, failed_seid; + int seid_count, i; + + if (size < sizeof(struct suspend_req)) { + error("Too short suspend request"); + return FALSE; + } + + seid_count = 1 + size - sizeof(struct suspend_req); + + seid = &req->first_seid; + + for (i = 0; i < seid_count; i++, seid++) { + failed_seid = seid->seid; + + sep = find_local_sep_by_seid(req->first_seid.seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + stream = sep->stream; + + if (sep->state != AVDTP_STATE_STREAMING) { + err = AVDTP_BAD_STATE; + goto failed; + } + + if (sep->ind && sep->ind->suspend) { + if (!sep->ind->suspend(session, sep, stream, &err, + sep->user_data)) + goto failed; + } + + avdtp_check_collision(session, AVDTP_SUSPEND, stream); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + } + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_SUSPEND, NULL, 0); + +failed: + memset(&rej, 0, sizeof(rej)); + rej.acp_seid = failed_seid; + rej.error = err; + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_SUSPEND, &rej, sizeof(rej)); +} + +static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, unsigned int size) +{ + struct avdtp_local_sep *sep; + uint8_t err; + gboolean ret; + + if (size < sizeof(struct seid_req)) { + error("Too short abort request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep || !sep->stream) + return TRUE; + + if (sep->ind && sep->ind->abort) + sep->ind->abort(session, sep, sep->stream, &err, + sep->user_data); + + avdtp_check_collision(session, AVDTP_ABORT, sep->stream); + + ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_ABORT, NULL, 0); + if (ret) + avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); + + return ret; +} + +static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction, + struct seid_req *req, int size) +{ + return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL); +} + +static gboolean avdtp_delayreport_cmd(struct avdtp *session, + uint8_t transaction, + struct delay_req *req, + unsigned int size) +{ + struct avdtp_local_sep *sep; + struct avdtp_stream *stream; + uint8_t err; + + if (size < sizeof(struct delay_req)) { + error("Too short delay report request"); + return FALSE; + } + + sep = find_local_sep_by_seid(req->acp_seid); + if (!sep || !sep->stream) { + err = AVDTP_BAD_ACP_SEID; + goto failed; + } + + stream = sep->stream; + + switch (sep->state) { + case AVDTP_STATE_IDLE: + case AVDTP_STATE_ABORTING: + case AVDTP_STATE_CLOSING: + err = AVDTP_BAD_STATE; + goto failed; + default: + break; + } + + stream->delay = ntohs(req->delay); + + if (sep->ind && sep->ind->delayreport) { + if (!sep->ind->delayreport(session, sep, stream->rseid, + stream->delay, &err, + sep->user_data)) + goto failed; + } + + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, + AVDTP_DELAY_REPORT, NULL, 0); + +failed: + return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, + AVDTP_DELAY_REPORT, &err, sizeof(err)); +} + +static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction, + uint8_t signal_id, void *buf, int size) +{ + switch (signal_id) { + case AVDTP_DISCOVER: + DBG("Received DISCOVER_CMD"); + return avdtp_discover_cmd(session, transaction, buf, size); + case AVDTP_GET_CAPABILITIES: + DBG("Received GET_CAPABILITIES_CMD"); + return avdtp_getcap_cmd(session, transaction, buf, size, + FALSE); + case AVDTP_GET_ALL_CAPABILITIES: + DBG("Received GET_ALL_CAPABILITIES_CMD"); + return avdtp_getcap_cmd(session, transaction, buf, size, TRUE); + case AVDTP_SET_CONFIGURATION: + DBG("Received SET_CONFIGURATION_CMD"); + return avdtp_setconf_cmd(session, transaction, buf, size); + case AVDTP_GET_CONFIGURATION: + DBG("Received GET_CONFIGURATION_CMD"); + return avdtp_getconf_cmd(session, transaction, buf, size); + case AVDTP_RECONFIGURE: + DBG("Received RECONFIGURE_CMD"); + return avdtp_reconf_cmd(session, transaction, buf, size); + case AVDTP_OPEN: + DBG("Received OPEN_CMD"); + return avdtp_open_cmd(session, transaction, buf, size); + case AVDTP_START: + DBG("Received START_CMD"); + return avdtp_start_cmd(session, transaction, buf, size); + case AVDTP_CLOSE: + DBG("Received CLOSE_CMD"); + return avdtp_close_cmd(session, transaction, buf, size); + case AVDTP_SUSPEND: + DBG("Received SUSPEND_CMD"); + return avdtp_suspend_cmd(session, transaction, buf, size); + case AVDTP_ABORT: + DBG("Received ABORT_CMD"); + return avdtp_abort_cmd(session, transaction, buf, size); + case AVDTP_SECURITY_CONTROL: + DBG("Received SECURITY_CONTROL_CMD"); + return avdtp_secctl_cmd(session, transaction, buf, size); + case AVDTP_DELAY_REPORT: + DBG("Received DELAY_REPORT_CMD"); + return avdtp_delayreport_cmd(session, transaction, buf, size); + default: + DBG("Received unknown request id %u", signal_id); + return avdtp_unknown_cmd(session, transaction, signal_id); + } +} + +enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS }; + +static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, + void *buf, size_t size) +{ + struct avdtp_common_header *header = buf; + struct avdtp_single_header *single = (void *) session->buf; + struct avdtp_start_header *start = (void *) session->buf; + void *payload; + gsize payload_size; + + switch (header->packet_type) { + case AVDTP_PKT_TYPE_SINGLE: + if (size < sizeof(*single)) { + error("Received too small single packet (%zu bytes)", + size); + return PARSE_ERROR; + } + if (session->in.active) { + error("SINGLE: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(*single); + payload_size = size - sizeof(*single); + + session->in.active = TRUE; + session->in.data_size = 0; + session->in.no_of_packets = 1; + session->in.transaction = header->transaction; + session->in.message_type = header->message_type; + session->in.signal_id = single->signal_id; + + break; + case AVDTP_PKT_TYPE_START: + if (size < sizeof(*start)) { + error("Received too small start packet (%zu bytes)", + size); + return PARSE_ERROR; + } + if (session->in.active) { + error("START: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + + session->in.active = TRUE; + session->in.data_size = 0; + session->in.transaction = header->transaction; + session->in.message_type = header->message_type; + session->in.no_of_packets = start->no_of_packets; + session->in.signal_id = start->signal_id; + + payload = session->buf + sizeof(*start); + payload_size = size - sizeof(*start); + + break; + case AVDTP_PKT_TYPE_CONTINUE: + if (size < sizeof(struct avdtp_continue_header)) { + error("Received too small continue packet (%zu bytes)", + size); + return PARSE_ERROR; + } + if (!session->in.active) { + error("CONTINUE: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + if (session->in.transaction != header->transaction) { + error("Continue transaction id doesn't match"); + return PARSE_ERROR; + } + if (session->in.no_of_packets <= 1) { + error("Too few continue packets"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(struct avdtp_continue_header); + payload_size = size - sizeof(struct avdtp_continue_header); + + break; + case AVDTP_PKT_TYPE_END: + if (size < sizeof(struct avdtp_continue_header)) { + error("Received too small end packet (%zu bytes)", + size); + return PARSE_ERROR; + } + if (!session->in.active) { + error("END: Invalid AVDTP packet fragmentation"); + return PARSE_ERROR; + } + if (session->in.transaction != header->transaction) { + error("End transaction id doesn't match"); + return PARSE_ERROR; + } + if (session->in.no_of_packets > 1) { + error("Got an end packet too early"); + return PARSE_ERROR; + } + + payload = session->buf + sizeof(struct avdtp_continue_header); + payload_size = size - sizeof(struct avdtp_continue_header); + + break; + default: + error("Invalid AVDTP packet type 0x%02X", header->packet_type); + return PARSE_ERROR; + } + + if (session->in.data_size + payload_size > + sizeof(session->in.buf)) { + error("Not enough incoming buffer space!"); + return PARSE_ERROR; + } + + memcpy(session->in.buf + session->in.data_size, payload, payload_size); + session->in.data_size += payload_size; + + if (session->in.no_of_packets > 1) { + session->in.no_of_packets--; + DBG("Received AVDTP fragment. %d to go", + session->in.no_of_packets); + return PARSE_FRAGMENT; + } + + session->in.active = FALSE; + + return PARSE_SUCCESS; +} + +static gboolean session_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct avdtp *session = data; + struct avdtp_common_header *header; + ssize_t size; + int fd; + + DBG(""); + + if (cond & G_IO_NVAL) { + session->io_id = 0; + + return FALSE; + } + + header = (void *) session->buf; + + if (cond & (G_IO_HUP | G_IO_ERR)) + goto failed; + + fd = g_io_channel_unix_get_fd(chan); + size = read(fd, session->buf, session->imtu); + if (size < 0) { + error("IO Channel read error"); + goto failed; + } + + if ((size_t) size < sizeof(struct avdtp_common_header)) { + error("Received too small packet (%zu bytes)", size); + goto failed; + } + + switch (avdtp_parse_data(session, session->buf, size)) { + case PARSE_ERROR: + goto failed; + case PARSE_FRAGMENT: + return TRUE; + case PARSE_SUCCESS: + break; + } + + if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) { + if (!avdtp_parse_cmd(session, session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { + error("Unable to handle command. Disconnecting"); + goto failed; + } + + if (session->req && session->req->collided) { + DBG("Collision detected"); + goto next; + } + + return TRUE; + } + + if (session->req == NULL) { + error("No pending request, ignoring message"); + return TRUE; + } + + if (header->transaction != session->req->transaction) { + error("Transaction label doesn't match"); + return TRUE; + } + + if (session->in.signal_id != session->req->signal_id) { + error("Response signal doesn't match"); + return TRUE; + } + + g_source_remove(session->req->timeout); + session->req->timeout = 0; + + switch (header->message_type) { + case AVDTP_MSG_TYPE_ACCEPT: + if (!avdtp_parse_resp(session, session->req->stream, + session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { + error("Unable to parse accept response"); + goto failed; + } + break; + case AVDTP_MSG_TYPE_REJECT: + if (!avdtp_parse_rej(session, session->req->stream, + session->in.transaction, + session->in.signal_id, + session->in.buf, + session->in.data_size)) { + error("Unable to parse reject response"); + goto failed; + } + break; + case AVDTP_MSG_TYPE_GEN_REJECT: + error("Received a General Reject message"); + break; + default: + error("Unknown message type 0x%02X", header->message_type); + break; + } + +next: + pending_req_free(session->req); + session->req = NULL; + + process_queue(session); + + return TRUE; + +failed: + connection_lost(session, EIO); + + session->io_id = 0; + + return FALSE; +} + +static int set_priority(int fd, int priority) +{ + int err; + + err = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &priority, + sizeof(priority)); + if (err == 0 || errno == ENOTSOCK) + return 0; + + err = -errno; + error("setsockopt(SO_PRIORITY): %s (%d)", strerror(-err), -err); + + return err; +} + +struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version) +{ + struct avdtp *session; + GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + int new_fd; + + new_fd = dup(fd); + if (new_fd < 0) { + error("dup(): %s (%d)", strerror(errno), errno); + return NULL; + } + + if (set_priority(new_fd, 6) < 0) + return NULL; + + session = g_new0(struct avdtp, 1); + session->io = g_io_channel_unix_new(new_fd); + session->version = version; + session->imtu = imtu; + session->omtu = omtu; + session->buf = g_malloc0(MAX(session->imtu, session->omtu)); + + /* This watch should be low priority since otherwise the + * connect callback might be dispatched before the session + * callback if the kernel wakes us up at the same time for + * them. This could happen if a headset is very quick in + * sending the Start command after connecting the stream + * transport channel. + */ + session->io_id = g_io_add_watch_full(session->io, G_PRIORITY_LOW, cond, + (GIOFunc) session_cb, session, + NULL); + + return avdtp_ref(session); +} + +unsigned int avdtp_add_disconnect_cb(struct avdtp *session, + avdtp_disconnect_cb_t cb, + void *user_data) +{ + struct disconnect_callback *callback; + static unsigned int id = 0; + + callback = g_new0(struct disconnect_callback, 1); + callback->id = ++id; + callback->cb = cb; + callback->user_data = user_data; + session->disconnect = g_slist_append(session->disconnect, callback); + + return id; +} + +gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id) +{ + GSList *l; + + for (l = session->disconnect; l; l = g_slist_next(l)) { + struct disconnect_callback *callback = l->data; + + if (callback->id != id) + continue; + + session->disconnect = g_slist_remove(session->disconnect, + callback); + g_free(callback); + return TRUE; + } + + return FALSE; +} + +void avdtp_shutdown(struct avdtp *session) +{ + GSList *l; + bool aborting = false; + + if (!session->io) + return; + + for (l = session->streams; l; l = g_slist_next(l)) { + struct avdtp_stream *stream = l->data; + + if (stream->abort_int || + avdtp_close(session, stream, TRUE) == 0) + aborting = true; + } + + if (aborting) { + /* defer shutdown until all streams are aborted properly */ + session->shutdown = true; + } else { + int sock = g_io_channel_unix_get_fd(session->io); + + shutdown(sock, SHUT_RDWR); + } +} + +static void queue_request(struct avdtp *session, struct pending_req *req, + gboolean priority) +{ + if (priority) + session->prio_queue = g_slist_append(session->prio_queue, req); + else + session->req_queue = g_slist_append(session->req_queue, req); +} + +static uint8_t req_get_seid(struct pending_req *req) +{ + if (req->signal_id == AVDTP_DISCOVER) + return 0; + + return ((struct seid_req *) (req->data))->acp_seid; +} + +static int cancel_request(struct avdtp *session, int err) +{ + struct pending_req *req; + struct seid_req sreq; + struct avdtp_local_sep *lsep; + struct avdtp_stream *stream; + uint8_t seid; + struct avdtp_error averr; + + req = session->req; + session->req = NULL; + + avdtp_error_init(&averr, AVDTP_ERRNO, err); + + seid = req_get_seid(req); + if (seid) + stream = find_stream_by_rseid(session, seid); + else + stream = NULL; + + if (stream) { + stream->abort_int = TRUE; + lsep = stream->lsep; + } else + lsep = NULL; + + switch (req->signal_id) { + case AVDTP_RECONFIGURE: + error("Reconfigure: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->reconfigure) + lsep->cfm->reconfigure(session, lsep, stream, &averr, + lsep->user_data); + break; + case AVDTP_OPEN: + error("Open: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->open) + lsep->cfm->open(session, lsep, stream, &averr, + lsep->user_data); + break; + case AVDTP_START: + error("Start: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->start) { + lsep->cfm->start(session, lsep, stream, &averr, + lsep->user_data); + if (stream) + stream->starting = FALSE; + } + break; + case AVDTP_SUSPEND: + error("Suspend: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->suspend) + lsep->cfm->suspend(session, lsep, stream, &averr, + lsep->user_data); + break; + case AVDTP_CLOSE: + error("Close: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->close) { + lsep->cfm->close(session, lsep, stream, &averr, + lsep->user_data); + if (stream) + stream->close_int = FALSE; + } + break; + case AVDTP_SET_CONFIGURATION: + error("SetConfiguration: %s (%d)", strerror(err), err); + if (lsep && lsep->cfm && lsep->cfm->set_configuration) + lsep->cfm->set_configuration(session, lsep, stream, + &averr, lsep->user_data); + goto failed; + case AVDTP_DISCOVER: + error("Discover: %s (%d)", strerror(err), err); + goto failed; + case AVDTP_GET_CAPABILITIES: + error("GetCapabilities: %s (%d)", strerror(err), err); + goto failed; + case AVDTP_ABORT: + error("Abort: %s (%d)", strerror(err), err); + goto failed; + } + + if (!stream) + goto failed; + + memset(&sreq, 0, sizeof(sreq)); + sreq.acp_seid = seid; + + err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq, + sizeof(sreq)); + if (err < 0) { + error("Unable to send abort request"); + goto failed; + } + + goto done; + +failed: + connection_lost(session, err); +done: + pending_req_free(req); + return err; +} + +static gboolean request_timeout(gpointer user_data) +{ + struct avdtp *session = user_data; + + cancel_request(session, ETIMEDOUT); + + return FALSE; +} + +static int send_req(struct avdtp *session, gboolean priority, + struct pending_req *req) +{ + static int transaction = 0; + int err; + + if (session->req != NULL) { + queue_request(session, req, priority); + return 0; + } + + req->transaction = transaction++; + transaction %= 16; + + /* FIXME: Should we retry to send if the buffer + was not totally sent or in case of EINTR? */ + if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND, + req->signal_id, req->data, req->data_size)) { + err = -EIO; + goto failed; + } + + session->req = req; + + req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ? + ABORT_TIMEOUT : REQ_TIMEOUT, + request_timeout, + session); + return 0; + +failed: + g_free(req->data); + g_free(req); + return err; +} + +static int send_request(struct avdtp *session, gboolean priority, + struct avdtp_stream *stream, uint8_t signal_id, + void *buffer, size_t size) +{ + struct pending_req *req; + + if (size > 0 && !buffer) { + DBG("Invalid buffer %p", buffer); + return -EINVAL; + } + + if (stream && stream->abort_int && signal_id != AVDTP_ABORT) { + DBG("Unable to send requests while aborting"); + return -EINVAL; + } + + req = g_new0(struct pending_req, 1); + req->signal_id = signal_id; + req->data_size = size; + req->stream = stream; + + if (size > 0) { + req->data = g_malloc(size); + memcpy(req->data, buffer, size); + } + + return send_req(session, priority, req); +} + +static gboolean avdtp_discover_resp(struct avdtp *session, + struct discover_resp *resp, int size) +{ + int sep_count, i; + uint8_t getcap_cmd; + int ret = 0; + gboolean getcap_pending = FALSE; + + if (session->version >= 0x0103) + getcap_cmd = AVDTP_GET_ALL_CAPABILITIES; + else + getcap_cmd = AVDTP_GET_CAPABILITIES; + + sep_count = size / sizeof(struct seid_info); + + for (i = 0; i < sep_count; i++) { + struct avdtp_remote_sep *sep; + struct avdtp_stream *stream; + struct seid_req req; + + DBG("seid %d type %d media %d in use %d", + resp->seps[i].seid, resp->seps[i].type, + resp->seps[i].media_type, resp->seps[i].inuse); + + stream = find_stream_by_rseid(session, resp->seps[i].seid); + + sep = find_remote_sep(session->seps, resp->seps[i].seid); + if (!sep) { + if (resp->seps[i].inuse && !stream) + continue; + sep = g_new0(struct avdtp_remote_sep, 1); + session->seps = g_slist_append(session->seps, sep); + } + + sep->stream = stream; + sep->seid = resp->seps[i].seid; + sep->type = resp->seps[i].type; + sep->media_type = resp->seps[i].media_type; + + memset(&req, 0, sizeof(req)); + req.acp_seid = sep->seid; + + ret = send_request(session, TRUE, NULL, getcap_cmd, + &req, sizeof(req)); + if (ret < 0) + break; + getcap_pending = TRUE; + } + + if (!getcap_pending) + finalize_discovery(session, -ret); + + return TRUE; +} + +static gboolean avdtp_get_capabilities_resp(struct avdtp *session, + struct getcap_resp *resp, + unsigned int size) +{ + struct avdtp_remote_sep *sep; + uint8_t seid; + + /* Check for minimum required packet size includes: + * 1. getcap resp header + * 2. media transport capability (2 bytes) + * 3. media codec capability type + length (2 bytes) + * 4. the actual media codec elements + * */ + if (size < (sizeof(struct getcap_resp) + 4 + + sizeof(struct avdtp_media_codec_capability))) { + error("Too short getcap resp packet"); + return FALSE; + } + + seid = ((struct seid_req *) session->req->data)->acp_seid; + + sep = find_remote_sep(session->seps, seid); + + DBG("seid %d type %d media %d", sep->seid, + sep->type, sep->media_type); + + if (sep->caps) { + g_slist_free_full(sep->caps, g_free); + sep->caps = NULL; + sep->codec = NULL; + sep->delay_reporting = FALSE; + } + + sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp), + &sep->codec, &sep->delay_reporting); + + return TRUE; +} + +static gboolean avdtp_set_configuration_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_single_header *resp, + int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->set_configuration) + sep->cfm->set_configuration(session, sep, stream, NULL, + sep->user_data); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); + + return TRUE; +} + +static gboolean avdtp_reconfigure_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_single_header *resp, + int size) +{ + return TRUE; +} + +static gboolean avdtp_open_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + session->pending_open = stream; + + if (!stream->open_acp && sep->cfm && sep->cfm->open) + sep->cfm->open(session, sep, stream, NULL, sep->user_data); + + return TRUE; +} + +static gboolean avdtp_start_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + /* We might be in STREAMING already if both sides send START_CMD at the + * same time and the one in SNK role doesn't reject it as it should */ + if (sep->state != AVDTP_STATE_STREAMING) + avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); + + if (sep->cfm && sep->cfm->start) + sep->cfm->start(session, sep, stream, NULL, sep->user_data); + + return TRUE; +} + +static gboolean avdtp_close_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); + + close_stream(stream); + + return TRUE; +} + +static gboolean avdtp_suspend_resp(struct avdtp *session, + struct avdtp_stream *stream, + void *data, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); + + if (sep->cfm && sep->cfm->suspend) + sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); + + return TRUE; +} + +static gboolean avdtp_abort_resp(struct avdtp *session, + struct avdtp_stream *stream, + struct seid_rej *resp, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); + + if (sep->cfm && sep->cfm->abort) + sep->cfm->abort(session, sep, stream, NULL, sep->user_data); + + avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); + + return TRUE; +} + +static gboolean avdtp_delay_report_resp(struct avdtp *session, + struct avdtp_stream *stream, + void *data, int size) +{ + struct avdtp_local_sep *sep = stream->lsep; + + if (sep->cfm && sep->cfm->delay_report) + sep->cfm->delay_report(session, sep, stream, NULL, + sep->user_data); + + return TRUE; +} + +static gboolean avdtp_parse_resp(struct avdtp *session, + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size) +{ + struct pending_req *next; + const char *get_all = ""; + + if (session->prio_queue) + next = session->prio_queue->data; + else if (session->req_queue) + next = session->req_queue->data; + else + next = NULL; + + switch (signal_id) { + case AVDTP_DISCOVER: + DBG("DISCOVER request succeeded"); + return avdtp_discover_resp(session, buf, size); + case AVDTP_GET_ALL_CAPABILITIES: + get_all = "ALL_"; + case AVDTP_GET_CAPABILITIES: + DBG("GET_%sCAPABILITIES request succeeded", get_all); + if (!avdtp_get_capabilities_resp(session, buf, size)) + return FALSE; + if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES || + next->signal_id == AVDTP_GET_ALL_CAPABILITIES))) + finalize_discovery(session, 0); + return TRUE; + } + + /* The remaining commands require an existing stream so bail out + * here if the stream got unexpectedly disconnected */ + if (!stream) { + DBG("AVDTP: stream was closed while waiting for reply"); + return TRUE; + } + + switch (signal_id) { + case AVDTP_SET_CONFIGURATION: + DBG("SET_CONFIGURATION request succeeded"); + return avdtp_set_configuration_resp(session, stream, + buf, size); + case AVDTP_RECONFIGURE: + DBG("RECONFIGURE request succeeded"); + return avdtp_reconfigure_resp(session, stream, buf, size); + case AVDTP_OPEN: + DBG("OPEN request succeeded"); + return avdtp_open_resp(session, stream, buf, size); + case AVDTP_SUSPEND: + DBG("SUSPEND request succeeded"); + return avdtp_suspend_resp(session, stream, buf, size); + case AVDTP_START: + DBG("START request succeeded"); + return avdtp_start_resp(session, stream, buf, size); + case AVDTP_CLOSE: + DBG("CLOSE request succeeded"); + return avdtp_close_resp(session, stream, buf, size); + case AVDTP_ABORT: + DBG("ABORT request succeeded"); + return avdtp_abort_resp(session, stream, buf, size); + case AVDTP_DELAY_REPORT: + DBG("DELAY_REPORT request succeeded"); + return avdtp_delay_report_resp(session, stream, buf, size); + } + + error("Unknown signal id in accept response: %u", signal_id); + return TRUE; +} + +static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size, + struct avdtp_error *err) +{ + if (size < sizeof(struct seid_rej)) { + error("Too small packet for seid_rej"); + return FALSE; + } + + avdtp_error_init(err, 0x00, rej->error); + + return TRUE; +} + +static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size, + struct avdtp_error *err) +{ + if (size < sizeof(struct conf_rej)) { + error("Too small packet for conf_rej"); + return FALSE; + } + + avdtp_error_init(err, rej->category, rej->error); + + return TRUE; +} + +static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size, + struct avdtp_error *err, + uint8_t *acp_seid) +{ + if (size < sizeof(struct stream_rej)) { + error("Too small packet for stream_rej"); + return FALSE; + } + + avdtp_error_init(err, 0x00, rej->error); + + if (acp_seid) + *acp_seid = rej->acp_seid; + + return TRUE; +} + +static gboolean avdtp_parse_rej(struct avdtp *session, + struct avdtp_stream *stream, + uint8_t transaction, uint8_t signal_id, + void *buf, int size) +{ + struct avdtp_error err; + uint8_t acp_seid; + struct avdtp_local_sep *sep = stream ? stream->lsep : NULL; + + switch (signal_id) { + case AVDTP_DISCOVER: + case AVDTP_GET_CAPABILITIES: + case AVDTP_GET_ALL_CAPABILITIES: + if (!seid_rej_to_err(buf, size, &err)) + return FALSE; + error("%s request rejected: %s (%d)", + signal_id == AVDTP_DISCOVER ? "DISCOVER" : + signal_id == AVDTP_GET_CAPABILITIES ? + "GET_CAPABILITIES" : "GET_ALL_CAPABILITIES", + avdtp_strerror(&err), err.err.error_code); + if (session->discover) { + session->discover->cb(session, session->seps, &err, + session->discover->user_data); + g_free(session->discover); + session->discover = NULL; + } + return TRUE; + case AVDTP_OPEN: + if (!seid_rej_to_err(buf, size, &err)) + return FALSE; + error("OPEN request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->open) + sep->cfm->open(session, sep, stream, &err, + sep->user_data); + return TRUE; + case AVDTP_SET_CONFIGURATION: + if (!conf_rej_to_err(buf, size, &err)) + return FALSE; + error("SET_CONFIGURATION request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->set_configuration) + sep->cfm->set_configuration(session, sep, stream, + &err, sep->user_data); + return TRUE; + case AVDTP_GET_CONFIGURATION: + if (!seid_rej_to_err(buf, size, &err)) + return FALSE; + error("GET_CONFIGURATION request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->get_configuration) + sep->cfm->get_configuration(session, sep, stream, &err, + sep->user_data); + return TRUE; + case AVDTP_RECONFIGURE: + if (!conf_rej_to_err(buf, size, &err)) + return FALSE; + error("RECONFIGURE request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->reconfigure) + sep->cfm->reconfigure(session, sep, stream, &err, + sep->user_data); + return TRUE; + case AVDTP_START: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("START request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->start) { + sep->cfm->start(session, sep, stream, &err, + sep->user_data); + stream->starting = FALSE; + } + return TRUE; + case AVDTP_SUSPEND: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("SUSPEND request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->suspend) + sep->cfm->suspend(session, sep, stream, &err, + sep->user_data); + return TRUE; + case AVDTP_CLOSE: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("CLOSE request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->close) { + sep->cfm->close(session, sep, stream, &err, + sep->user_data); + stream->close_int = FALSE; + } + return TRUE; + case AVDTP_ABORT: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("ABORT request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->abort) + sep->cfm->abort(session, sep, stream, &err, + sep->user_data); + return FALSE; + case AVDTP_DELAY_REPORT: + if (!stream_rej_to_err(buf, size, &err, &acp_seid)) + return FALSE; + error("DELAY_REPORT request rejected: %s (%d)", + avdtp_strerror(&err), err.err.error_code); + if (sep && sep->cfm && sep->cfm->delay_report) + sep->cfm->delay_report(session, sep, stream, &err, + sep->user_data); + return TRUE; + default: + error("Unknown reject response signal id: %u", signal_id); + return TRUE; + } +} + +struct avdtp_service_capability *avdtp_stream_get_codec( + struct avdtp_stream *stream) +{ + GSList *l; + + for (l = stream->caps; l; l = l->next) { + struct avdtp_service_capability *cap = l->data; + + if (cap->category == AVDTP_MEDIA_CODEC) + return cap; + } + + return NULL; +} + +static gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, + struct avdtp_service_capability *cap) +{ + GSList *l; + struct avdtp_service_capability *stream_cap; + + for (l = stream->caps; l; l = g_slist_next(l)) { + stream_cap = l->data; + + if (stream_cap->category != cap->category || + stream_cap->length != cap->length) + continue; + + if (memcmp(stream_cap->data, cap->data, cap->length) == 0) + return TRUE; + } + + return FALSE; +} + +gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, + GSList *caps) +{ + for (; caps; caps = g_slist_next(caps)) { + struct avdtp_service_capability *cap = caps->data; + + if (!avdtp_stream_has_capability(stream, cap)) + return FALSE; + } + + return TRUE; +} + +struct avdtp_remote_sep *avdtp_stream_get_remote_sep( + struct avdtp_stream *stream) +{ + GSList *l; + + for (l = stream->session->seps; l; l = l->next) { + struct avdtp_remote_sep *sep = l->data; + + if (sep->seid == stream->rseid) + return sep; + } + + return NULL; +} + +gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, + size_t imtu, size_t omtu) +{ + GIOChannel *io; + + if (stream != stream->session->pending_open) + return FALSE; + + if (set_priority(fd, 5) < 0) + return FALSE; + + io = g_io_channel_unix_new(fd); + + handle_transport_connect(stream->session, io, imtu, omtu); + + g_io_channel_unref(io); + + return TRUE; + +} + +gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, + uint16_t *imtu, uint16_t *omtu, + GSList **caps) +{ + if (stream->io == NULL) + return FALSE; + + if (sock) + *sock = g_io_channel_unix_get_fd(stream->io); + + if (omtu) + *omtu = stream->omtu; + + if (imtu) + *imtu = stream->imtu; + + if (caps) + *caps = stream->caps; + + return TRUE; +} + +static int process_queue(struct avdtp *session) +{ + GSList **queue, *l; + struct pending_req *req; + + if (session->req) + return 0; + + if (session->prio_queue) + queue = &session->prio_queue; + else + queue = &session->req_queue; + + if (!*queue) + return 0; + + l = *queue; + req = l->data; + + *queue = g_slist_remove(*queue, req); + + return send_req(session, FALSE, req); +} + +struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep) +{ + return sep->codec; +} + +struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, + const void *data, + int length) +{ + struct avdtp_service_capability *cap; + + if (category < AVDTP_MEDIA_TRANSPORT || + category > AVDTP_DELAY_REPORTING) + return NULL; + + if (length > 0 && !data) + return NULL; + + cap = g_malloc(sizeof(struct avdtp_service_capability) + length); + cap->category = category; + cap->length = length; + + if (length > 0) + memcpy(cap->data, data, length); + + return cap; +} + +static gboolean process_discover(gpointer data) +{ + struct avdtp *session = data; + + session->discover->id = 0; + + finalize_discovery(session, 0); + + return FALSE; +} + +int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, + void *user_data) +{ + int err; + + if (session->discover) + return -EBUSY; + + session->discover = g_new0(struct discover_callback, 1); + + if (session->seps) { + session->discover->cb = cb; + session->discover->user_data = user_data; + session->discover->id = g_idle_add(process_discover, session); + return 0; + } + + err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0); + if (err == 0) { + session->discover->cb = cb; + session->discover->user_data = user_data; + } + + return err; +} + +gboolean avdtp_stream_remove_cb(struct avdtp *session, + struct avdtp_stream *stream, + unsigned int id) +{ + GSList *l; + struct stream_callback *cb; + + if (!stream) + return FALSE; + + for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) { + struct stream_callback *tmp = l->data; + if (tmp && tmp->id == id) { + cb = tmp; + break; + } + } + + if (!cb) + return FALSE; + + stream->callbacks = g_slist_remove(stream->callbacks, cb); + g_free(cb); + + return TRUE; +} + +unsigned int avdtp_stream_add_cb(struct avdtp *session, + struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data) +{ + struct stream_callback *stream_cb; + static unsigned int id = 0; + + stream_cb = g_new(struct stream_callback, 1); + stream_cb->cb = cb; + stream_cb->user_data = data; + stream_cb->id = ++id; + + stream->callbacks = g_slist_append(stream->callbacks, stream_cb); + + return stream_cb->id; +} + +int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION, + &req, sizeof(req)); +} + +static void copy_capabilities(gpointer data, gpointer user_data) +{ + struct avdtp_service_capability *src_cap = data; + struct avdtp_service_capability *dst_cap; + GSList **l = user_data; + + dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data, + src_cap->length); + + *l = g_slist_append(*l, dst_cap); +} + +int avdtp_set_configuration(struct avdtp *session, + struct avdtp_remote_sep *rsep, + struct avdtp_local_sep *lsep, + GSList *caps, + struct avdtp_stream **stream) +{ + struct setconf_req *req; + struct avdtp_stream *new_stream; + unsigned char *ptr; + int err, caps_len; + struct avdtp_service_capability *cap; + GSList *l; + + if (!(lsep && rsep)) + return -EINVAL; + + DBG("%p: int_seid=%u, acp_seid=%u", session, + lsep->info.seid, rsep->seid); + + new_stream = g_new0(struct avdtp_stream, 1); + new_stream->session = session; + new_stream->lsep = lsep; + new_stream->rseid = rsep->seid; + + if (rsep->delay_reporting && lsep->delay_reporting) { + struct avdtp_service_capability *delay_reporting; + + delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, + NULL, 0); + caps = g_slist_append(caps, delay_reporting); + new_stream->delay_reporting = TRUE; + } + + g_slist_foreach(caps, copy_capabilities, &new_stream->caps); + + /* Calculate total size of request */ + for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { + cap = l->data; + caps_len += cap->length + 2; + } + + req = g_malloc0(sizeof(struct setconf_req) + caps_len); + + req->int_seid = lsep->info.seid; + req->acp_seid = rsep->seid; + + /* Copy the capabilities into the request */ + for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { + cap = l->data; + memcpy(ptr, cap, cap->length + 2); + ptr += cap->length + 2; + } + + err = send_request(session, FALSE, new_stream, + AVDTP_SET_CONFIGURATION, req, + sizeof(struct setconf_req) + caps_len); + if (err < 0) + stream_free(new_stream); + else { + lsep->info.inuse = 1; + lsep->stream = new_stream; + rsep->stream = new_stream; + session->streams = g_slist_append(session->streams, new_stream); + if (stream) + *stream = new_stream; + } + + g_free(req); + + return err; +} + +int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state > AVDTP_STATE_CONFIGURED) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + return send_request(session, FALSE, stream, AVDTP_OPEN, + &req, sizeof(req)); +} + +static gboolean start_timeout(gpointer user_data) +{ + struct avdtp_stream *stream = user_data; + struct avdtp *session = stream->session; + + stream->open_acp = FALSE; + + if (avdtp_start(session, stream) < 0) + error("wait_timeout: avdtp_start failed"); + + stream->start_timer = 0; + + return FALSE; +} + +int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) +{ + struct start_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state != AVDTP_STATE_OPEN) + return -EINVAL; + + /* Recommendation 12: + * If the RD has configured and opened a stream it is also responsible + * to start the streaming via GAVDP_START. + */ + if (stream->open_acp) { + /* If timer already active wait it */ + if (stream->start_timer) + return 0; + + stream->start_timer = g_timeout_add_seconds(START_TIMEOUT, + start_timeout, + stream); + return 0; + } + + if (stream->close_int == TRUE) { + error("avdtp_start: rejecting start since close is initiated"); + return -EINVAL; + } + + if (stream->starting == TRUE) { + DBG("stream already started"); + return -EINPROGRESS; + } + + memset(&req, 0, sizeof(req)); + req.first_seid.seid = stream->rseid; + + ret = send_request(session, FALSE, stream, AVDTP_START, + &req, sizeof(req)); + if (ret == 0) + stream->starting = TRUE; + + return ret; +} + +int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, + gboolean immediate) +{ + struct seid_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->close_int == TRUE) { + error("avdtp_close: rejecting since close is already initiated"); + return -EINVAL; + } + + /* If stream is not yet in the OPEN state, let's use ABORT_CMD */ + if (stream->lsep->state < AVDTP_STATE_OPEN) + return avdtp_abort(session, stream); + + if (immediate && session->req && stream == session->req->stream) + return avdtp_abort(session, stream); + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + ret = send_request(session, FALSE, stream, AVDTP_CLOSE, + &req, sizeof(req)); + if (ret == 0) + stream->close_int = TRUE; + + return ret; +} + +int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int) + return -EINVAL; + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + return send_request(session, FALSE, stream, AVDTP_SUSPEND, + &req, sizeof(req)); +} + +int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) +{ + struct seid_req req; + int ret; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state == AVDTP_STATE_ABORTING) + return -EINVAL; + + if (session->req && session->req->timeout > 0 && + stream == session->req->stream) + return cancel_request(session, ECANCELED); + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + + ret = send_request(session, TRUE, stream, AVDTP_ABORT, + &req, sizeof(req)); + if (ret == 0) + stream->abort_int = TRUE; + + return ret; +} + +int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, + uint16_t delay) +{ + struct delay_req req; + + if (!g_slist_find(session->streams, stream)) + return -EINVAL; + + if (stream->lsep->state != AVDTP_STATE_CONFIGURED && + stream->lsep->state != AVDTP_STATE_STREAMING) + return -EINVAL; + + if (!stream->delay_reporting || session->version < 0x0103) + return -EINVAL; + + stream->delay = delay; + + memset(&req, 0, sizeof(req)); + req.acp_seid = stream->rseid; + req.delay = htons(delay); + + return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT, + &req, sizeof(req)); +} + +struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + uint8_t codec_type, + gboolean delay_reporting, + struct avdtp_sep_ind *ind, + struct avdtp_sep_cfm *cfm, + void *user_data) +{ + struct avdtp_local_sep *sep; + + if (g_slist_length(lseps) > MAX_SEID) + return NULL; + + sep = g_new0(struct avdtp_local_sep, 1); + + sep->state = AVDTP_STATE_IDLE; + sep->info.seid = g_slist_length(lseps) + 1; + sep->info.type = type; + sep->info.media_type = media_type; + sep->codec = codec_type; + sep->ind = ind; + sep->cfm = cfm; + sep->user_data = user_data; + sep->delay_reporting = delay_reporting; + + DBG("SEP %p registered: type:%d codec:%d seid:%d", sep, + sep->info.type, sep->codec, sep->info.seid); + lseps = g_slist_append(lseps, sep); + + return sep; +} + +void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id, + uint16_t codec_id) +{ + sep->vndcodec_vendor = vendor_id; + sep->vndcodec_codec = codec_id; +} + +int avdtp_unregister_sep(struct avdtp_local_sep *sep) +{ + if (!sep) + return -EINVAL; + + lseps = g_slist_remove(lseps, sep); + + if (sep->stream) + release_stream(sep->stream, sep->stream->session); + + DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep, + sep->info.type, sep->codec, sep->info.seid); + + g_free(sep); + + return 0; +} + +const char *avdtp_strerror(struct avdtp_error *err) +{ + if (err->category == AVDTP_ERRNO) + return strerror(err->err.posix_errno); + + switch (err->err.error_code) { + case AVDTP_BAD_HEADER_FORMAT: + return "Bad Header Format"; + case AVDTP_BAD_LENGTH: + return "Bad Packet Length"; + case AVDTP_BAD_ACP_SEID: + return "Bad Acceptor SEID"; + case AVDTP_SEP_IN_USE: + return "Stream End Point in Use"; + case AVDTP_SEP_NOT_IN_USE: + return "Stream End Point Not in Use"; + case AVDTP_BAD_SERV_CATEGORY: + return "Bad Service Category"; + case AVDTP_BAD_PAYLOAD_FORMAT: + return "Bad Payload format"; + case AVDTP_NOT_SUPPORTED_COMMAND: + return "Command Not Supported"; + case AVDTP_INVALID_CAPABILITIES: + return "Invalid Capabilities"; + case AVDTP_BAD_RECOVERY_TYPE: + return "Bad Recovery Type"; + case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT: + return "Bad Media Transport Format"; + case AVDTP_BAD_RECOVERY_FORMAT: + return "Bad Recovery Format"; + case AVDTP_BAD_ROHC_FORMAT: + return "Bad Header Compression Format"; + case AVDTP_BAD_CP_FORMAT: + return "Bad Content Protection Format"; + case AVDTP_BAD_MULTIPLEXING_FORMAT: + return "Bad Multiplexing Format"; + case AVDTP_UNSUPPORTED_CONFIGURATION: + return "Configuration not supported"; + case AVDTP_BAD_STATE: + return "Bad State"; + default: + return "Unknown error"; + } +} + +avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep) +{ + return sep->state; +} + +gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream) +{ + return g_slist_find(session->streams, stream) ? TRUE : FALSE; +} diff -Nru bluez-4.101/android/avdtp.h bluez-5.23/android/avdtp.h --- bluez-4.101/android/avdtp.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avdtp.h 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,289 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct avdtp; +struct avdtp_stream; +struct avdtp_local_sep; +struct avdtp_remote_sep; +struct avdtp_error { + uint8_t category; + union { + uint8_t error_code; + int posix_errno; + } err; +}; + +#define AVDTP_PSM 25 + +/* SEP capability categories */ +#define AVDTP_MEDIA_TRANSPORT 0x01 +#define AVDTP_REPORTING 0x02 +#define AVDTP_RECOVERY 0x03 +#define AVDTP_CONTENT_PROTECTION 0x04 +#define AVDTP_HEADER_COMPRESSION 0x05 +#define AVDTP_MULTIPLEXING 0x06 +#define AVDTP_MEDIA_CODEC 0x07 +#define AVDTP_DELAY_REPORTING 0x08 +#define AVDTP_ERRNO 0xff + +/* AVDTP error definitions */ +#define AVDTP_BAD_HEADER_FORMAT 0x01 +#define AVDTP_BAD_LENGTH 0x11 +#define AVDTP_BAD_ACP_SEID 0x12 +#define AVDTP_SEP_IN_USE 0x13 +#define AVDTP_SEP_NOT_IN_USE 0x14 +#define AVDTP_BAD_SERV_CATEGORY 0x17 +#define AVDTP_BAD_PAYLOAD_FORMAT 0x18 +#define AVDTP_NOT_SUPPORTED_COMMAND 0x19 +#define AVDTP_INVALID_CAPABILITIES 0x1A +#define AVDTP_BAD_RECOVERY_TYPE 0x22 +#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT 0x23 +#define AVDTP_BAD_RECOVERY_FORMAT 0x25 +#define AVDTP_BAD_ROHC_FORMAT 0x26 +#define AVDTP_BAD_CP_FORMAT 0x27 +#define AVDTP_BAD_MULTIPLEXING_FORMAT 0x28 +#define AVDTP_UNSUPPORTED_CONFIGURATION 0x29 +#define AVDTP_BAD_STATE 0x31 + +/* SEP types definitions */ +#define AVDTP_SEP_TYPE_SOURCE 0x00 +#define AVDTP_SEP_TYPE_SINK 0x01 + +/* Media types definitions */ +#define AVDTP_MEDIA_TYPE_AUDIO 0x00 +#define AVDTP_MEDIA_TYPE_VIDEO 0x01 +#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02 + +typedef enum { + AVDTP_STATE_IDLE, + AVDTP_STATE_CONFIGURED, + AVDTP_STATE_OPEN, + AVDTP_STATE_STREAMING, + AVDTP_STATE_CLOSING, + AVDTP_STATE_ABORTING, +} avdtp_state_t; + +struct avdtp_service_capability { + uint8_t category; + uint8_t length; + uint8_t data[0]; +} __attribute__ ((packed)); + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avdtp_media_codec_capability { + uint8_t rfa0:4; + uint8_t media_type:4; + uint8_t media_codec_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avdtp_media_codec_capability { + uint8_t media_type:4; + uint8_t rfa0:4; + uint8_t media_codec_type; + uint8_t data[0]; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, + avdtp_state_t old_state, + avdtp_state_t new_state, + struct avdtp_error *err, + void *user_data); + +typedef void (*avdtp_set_configuration_cb) (struct avdtp *session, + struct avdtp_stream *stream, + struct avdtp_error *err); + +/* Callbacks for when a reply is received to a command that we sent */ +struct avdtp_sep_cfm { + void (*set_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, + void *user_data); + void (*get_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, + void *user_data); + void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data); + void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data); + void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); + void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); + void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); + void (*reconfigure) (struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); + void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data); +}; + +/* + * Callbacks for indicating when we received a new command. The return value + * indicates whether the command should be rejected or accepted + */ +struct avdtp_sep_ind { + gboolean (*get_capability) (struct avdtp *session, + struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err, + void *user_data); + gboolean (*set_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + GSList *caps, + avdtp_set_configuration_cb cb, + void *user_data); + gboolean (*get_configuration) (struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t *err, void *user_data); + gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + gboolean (*suspend) (struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data); + gboolean (*reconfigure) (struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t *err, void *user_data); + gboolean (*delayreport) (struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t rseid, uint16_t delay, + uint8_t *err, void *user_data); +}; + +typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, + struct avdtp_error *err, void *user_data); +typedef void (*avdtp_disconnect_cb_t) (void *user_data); + +struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version); + +unsigned int avdtp_add_disconnect_cb(struct avdtp *session, + avdtp_disconnect_cb_t cb, + void *user_data); +gboolean avdtp_remove_disconnect_cb(struct avdtp *session, unsigned int id); + +void avdtp_shutdown(struct avdtp *session); + +void avdtp_unref(struct avdtp *session); +struct avdtp *avdtp_ref(struct avdtp *session); + +struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, + const void *data, + int size); + +struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); + +int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, + void *user_data); + +gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream); + +unsigned int avdtp_stream_add_cb(struct avdtp *session, + struct avdtp_stream *stream, + avdtp_stream_state_cb cb, void *data); +gboolean avdtp_stream_remove_cb(struct avdtp *session, + struct avdtp_stream *stream, + unsigned int id); + +gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd, + size_t imtu, size_t omtu); +gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, + uint16_t *imtu, uint16_t *omtu, + GSList **caps); +struct avdtp_service_capability *avdtp_stream_get_codec( + struct avdtp_stream *stream); +gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, + GSList *caps); +struct avdtp_remote_sep *avdtp_stream_get_remote_sep( + struct avdtp_stream *stream); + +int avdtp_set_configuration(struct avdtp *session, + struct avdtp_remote_sep *rsep, + struct avdtp_local_sep *lsep, + GSList *caps, + struct avdtp_stream **stream); + +int avdtp_get_configuration(struct avdtp *session, + struct avdtp_stream *stream); + +int avdtp_open(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_start(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, + gboolean immediate); +int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); +int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, + uint16_t delay); + +struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type, + uint8_t codec_type, + gboolean delay_reporting, + struct avdtp_sep_ind *ind, + struct avdtp_sep_cfm *cfm, + void *user_data); +void avdtp_sep_set_vendor_codec(struct avdtp_local_sep *sep, uint32_t vendor_id, + uint16_t codec_id); + +/* Find a matching pair of local and remote SEP ID's */ +struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, + struct avdtp_local_sep *lsep); + +int avdtp_unregister_sep(struct avdtp_local_sep *sep); + +avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep); + +void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id); +const char *avdtp_strerror(struct avdtp_error *err); +uint8_t avdtp_error_category(struct avdtp_error *err); +int avdtp_error_error_code(struct avdtp_error *err); +int avdtp_error_posix_errno(struct avdtp_error *err); diff -Nru bluez-4.101/android/avdtptest.c bluez-5.23/android/avdtptest.c --- bluez-4.101/android/avdtptest.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avdtptest.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,898 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "src/shared/util.h" +#include "btio/btio.h" +#include "avdtp.h" + +static GMainLoop *mainloop = NULL; +static int dev_role = AVDTP_SEP_TYPE_SOURCE; +static bool preconf = false; +static struct avdtp *avdtp = NULL; +struct avdtp_stream *avdtp_stream = NULL; +struct avdtp_local_sep *local_sep = NULL; +struct avdtp_remote_sep *remote_sep = NULL; +static GIOChannel *io = NULL; +static bool reject = false; +static bdaddr_t src; +static bdaddr_t dst; +static uint16_t version = 0x0103; +static guint media_player = 0; +static guint media_recorder = 0; +static guint idle_id = 0; + +static bool fragment = false; + +static enum { + CMD_GET_CONF, + CMD_OPEN, + CMD_START, + CMD_SUSPEND, + CMD_CLOSE, + CMD_ABORT, + CMD_DELAY, + CMD_NONE, +} command = CMD_NONE; + +static const char sbc_codec[] = {0x00, 0x00, 0x11, 0x15, 0x02, 0x40}; +static const char sbc_media_frame[] = { + 0x00, 0x60, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x9c, 0xfd, 0x40, 0xbd, 0xde, 0xa9, 0x75, 0x43, 0x20, 0x87, 0x64, + 0x44, 0x32, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe, + 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x7f, 0xbe, 0xf7, 0x76, 0xfe, 0xf7, + 0xbb, 0xbb, 0x80, 0x3e, 0xf7, 0x76, 0xfe, 0xf7, 0xbb, 0xbb, 0x83, 0x41, + 0x07, 0x77, 0x09, 0x07, 0x43, 0xb3, 0x81, 0xbc, 0xf8, 0x77, 0x02, 0xe5, + 0xa4, 0x3a, 0xa0, 0xcb, 0x38, 0xbb, 0x57, 0x90, 0xd9, 0x08, 0x9c, 0x1d, + 0x86, 0x59, 0x01, 0x0c, 0x21, 0x44, 0x68, 0x35, 0xa8, 0x57, 0x97, 0x0e, + 0x9b, 0xbb, 0x62, 0xc4, 0xca, 0x57, 0x04, 0xa1, 0xca, 0x3b, 0xa3, 0x48, + 0xd2, 0x66, 0x11, 0x33, 0x6a, 0x3b, 0xb4, 0xbb, 0x08, 0x77, 0x17, 0x03, + 0xb4, 0x3b, 0x79, 0x3b, 0x46, 0x97, 0x0e, 0xf7, 0x3d, 0xbb, 0x3d, 0x49, + 0x25, 0x86, 0x88, 0xb4, 0xad, 0x3b, 0x62, 0xbb, 0xa4, 0x47, 0x29, 0x99, + 0x3b, 0x3b, 0xaf, 0xc6, 0xd4, 0x37, 0x68, 0x94, 0x0a, 0xbb + }; + +static void parse_command(const char *cmd) +{ + if (!strncmp(cmd, "getconf", sizeof("getconf"))) { + command = CMD_GET_CONF; + } else if (!strncmp(cmd, "open", sizeof("open"))) { + command = CMD_OPEN; + } else if (!strncmp(cmd, "start", sizeof("start"))) { + command = CMD_START; + } else if (!strncmp(cmd, "suspend", sizeof("suspend"))) { + command = CMD_SUSPEND; + } else if (!strncmp(cmd, "close", sizeof("close"))) { + command = CMD_CLOSE; + } else if (!strncmp(cmd, "abort", sizeof("abort"))) { + command = CMD_ABORT; + } else if (!strncmp(cmd, "delay", sizeof("delay"))) { + command = CMD_DELAY; + } else { + printf("Unknown command '%s'\n", cmd); + printf("(getconf open start suspend close abort delay)\n"); + exit(1); + } +} + +static void send_command(void) +{ + avdtp_state_t state = avdtp_sep_get_state(local_sep); + + switch (command) { + case CMD_GET_CONF: + avdtp_get_configuration(avdtp, avdtp_stream); + break; + case CMD_OPEN: + if (state == AVDTP_STATE_CONFIGURED) + avdtp_open(avdtp, avdtp_stream); + break; + case CMD_START: + if (state == AVDTP_STATE_OPEN) + avdtp_start(avdtp, avdtp_stream); + break; + case CMD_SUSPEND: + if (state == AVDTP_STATE_STREAMING) + avdtp_suspend(avdtp , avdtp_stream); + break; + case CMD_CLOSE: + if (state == AVDTP_STATE_STREAMING) + avdtp_close(avdtp, avdtp_stream, FALSE); + break; + case CMD_ABORT: + avdtp_abort(avdtp , avdtp_stream); + break; + case CMD_DELAY: + avdtp_delay_report(avdtp , avdtp_stream , 250); + break; + default: + break; + } +} + +static gboolean media_writer(gpointer user_data) +{ + uint16_t omtu; + int fd; + int to_write; + + if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL)) + return TRUE; + + if (omtu < sizeof(sbc_media_frame)) + to_write = omtu; + else + to_write = sizeof(sbc_media_frame); + + if (write(fd, sbc_media_frame, to_write) < 0) + return TRUE; + + send_command(); + + return TRUE; +} + +static bool start_media_player(void) +{ + int fd; + uint16_t omtu; + + printf("Media streaming started\n"); + + if (media_player || !avdtp_stream) + return false; + + if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL)) + return false; + + media_player = g_timeout_add(200, media_writer, NULL); + if (!media_player) + return false; + + return true; +} + +static void stop_media_player(void) +{ + if (!media_player) + return; + + printf("Media streaming stopped\n"); + + g_source_remove(media_player); + media_player = 0; +} + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_header { + unsigned cc:4; + unsigned x:1; + unsigned p:1; + unsigned v:2; + + unsigned pt:7; + unsigned m:1; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_header { + unsigned v:2; + unsigned p:1; + unsigned x:1; + unsigned cc:4; + + unsigned m:1; + unsigned pt:7; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +static gboolean media_reader(GIOChannel *source, GIOCondition condition, + gpointer data) +{ + char buf[UINT16_MAX]; + struct rtp_header *rtp = (void *) buf; + static bool decode = false; + uint16_t imtu; + int fd, ret; + + if (!avdtp_stream_get_transport(avdtp_stream, &fd, &imtu, NULL, NULL)) + return TRUE; + + ret = read(fd, buf, imtu); + if (ret < 0) { + printf("Reading failed (%s)\n", strerror(errno)); + return TRUE; + } + + if (ret < (int) sizeof(*rtp)) { + printf("Not enough media data received (%u bytes)", ret); + return TRUE; + } + + if (!decode) { + printf("V=%u P=%u X=%u CC=%u M=%u PT=%u SeqNr=%d\n", + rtp->v, rtp->p, rtp->x, rtp->cc, rtp->m, rtp->pt, + be16_to_cpu(rtp->sequence_number)); + decode = true; + } + + send_command(); + + return TRUE; +} + +static bool start_media_recorder(void) +{ + int fd; + uint16_t omtu; + GIOChannel *chan; + + printf("Media recording started\n"); + + if (media_recorder || !avdtp_stream) + return false; + + if (!avdtp_stream_get_transport(avdtp_stream, &fd, NULL, &omtu, NULL)) + return false; + + chan = g_io_channel_unix_new(fd); + + media_recorder = g_io_add_watch(chan, G_IO_IN, media_reader, NULL); + g_io_channel_unref(chan); + + if (!media_recorder) + return false; + + return true; +} + +static void stop_media_recorder(void) +{ + if (!media_recorder) + return; + + printf("Media recording stopped\n"); + + g_source_remove(media_recorder); + media_recorder = 0; +} + +static void set_configuration_cfm(struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, + void *user_data) +{ + printf("%s\n", __func__); + + if (preconf) + avdtp_open(avdtp, avdtp_stream); +} + +static void get_configuration_cfm(struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, + void *user_data) + { + printf("%s\n", __func__); +} + +static void disconnect_cb(void *user_data) +{ + printf("Disconnected\n"); + + g_main_loop_quit(mainloop); +} + +static void discover_cb(struct avdtp *session, GSList *seps, + struct avdtp_error *err, void *user_data) +{ + struct avdtp_service_capability *service; + GSList *caps = NULL; + int ret; + + remote_sep = avdtp_find_remote_sep(avdtp, local_sep); + if (!remote_sep) { + printf("Unable to find matching endpoint\n"); + avdtp_shutdown(session); + return; + } + + printf("Matching endpoint found\n"); + + service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); + caps = g_slist_append(caps, service); + + service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec, + sizeof(sbc_codec)); + caps = g_slist_append(caps, service); + + ret = avdtp_set_configuration(avdtp, remote_sep, local_sep, caps, + &avdtp_stream); + + g_slist_free_full(caps, g_free); + + if (ret < 0) { + printf("Failed to set configuration (%s)\n", strerror(-ret)); + avdtp_shutdown(session); + } +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + uint16_t imtu, omtu; + GError *gerr = NULL; + int fd; + + if (err) { + printf("%s\n", err->message); + g_main_loop_quit(mainloop); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_INVALID); + if (gerr) { + printf("%s\n", gerr->message); + g_main_loop_quit(mainloop); + return; + } + + printf("Connected (imtu=%d omtu=%d)\n", imtu, omtu); + + fd = g_io_channel_unix_get_fd(chan); + + if (avdtp && avdtp_stream) { + if (!avdtp_stream_set_transport(avdtp_stream, fd, imtu, omtu)) { + printf("avdtp_stream_set_transport: failed\n"); + g_main_loop_quit(mainloop); + } + + g_io_channel_set_close_on_unref(chan, FALSE); + + send_command(); + + return; + } + + avdtp = avdtp_new(fd, imtu, omtu, version); + if (!avdtp) { + printf("Failed to create avdtp instance\n"); + g_main_loop_quit(mainloop); + return; + } + + avdtp_add_disconnect_cb(avdtp, disconnect_cb, NULL); + + if (preconf) { + int ret; + + ret = avdtp_discover(avdtp, discover_cb, NULL); + if (ret < 0) { + printf("avdtp_discover failed: %s", strerror(-ret)); + g_main_loop_quit(mainloop); + } + } +} + +static GIOChannel *do_connect(GError **err) +{ + if (fragment) + return bt_io_connect(connect_cb, NULL, NULL, err, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_MTU, 48, + BT_IO_OPT_INVALID); + + return bt_io_connect(connect_cb, NULL, NULL, err, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_INVALID); +} + +static void open_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + GError *gerr = NULL; + + printf("%s\n", __func__); + + do_connect(&gerr); + if (gerr) { + printf("connect failed: %s\n", gerr->message); + g_error_free(gerr); + g_main_loop_quit(mainloop); + } +} + +static void start_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, struct avdtp_error *err, + void *user_data) +{ + printf("%s\n", __func__); + + if (dev_role == AVDTP_SEP_TYPE_SOURCE) + start_media_player(); + else + start_media_recorder(); +} + +static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + printf("%s\n", __func__); + + if (dev_role == AVDTP_SEP_TYPE_SOURCE) + stop_media_player(); + else + stop_media_recorder(); +} + +static void close_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + printf("%s\n", __func__); + + if (dev_role == AVDTP_SEP_TYPE_SOURCE) + stop_media_player(); + else + stop_media_recorder(); + + avdtp_stream = NULL; +} + +static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + printf("%s\n", __func__); + + if (dev_role == AVDTP_SEP_TYPE_SOURCE) + stop_media_player(); + else + stop_media_recorder(); + + avdtp_stream = NULL; +} + +static void reconfigure_cfm(struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + printf("%s\n", __func__); +} + +static void delay_report_cfm(struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + struct avdtp_error *err, void *user_data) +{ + printf("%s\n", __func__); +} + +static struct avdtp_sep_cfm sep_cfm = { + .set_configuration = set_configuration_cfm, + .get_configuration = get_configuration_cfm, + .open = open_cfm, + .start = start_cfm, + .suspend = suspend_cfm, + .close = close_cfm, + .abort = abort_cfm, + .reconfigure = reconfigure_cfm, + .delay_report = delay_report_cfm, +}; + +static gboolean get_capability_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + GSList **caps, uint8_t *err, + void *user_data) +{ + struct avdtp_service_capability *service; + int i; + + printf("%s\n", __func__); + + if (idle_id > 0) { + g_source_remove(idle_id); + idle_id = 0; + } + + if (reject) + return FALSE; + + *caps = NULL; + + service = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, NULL, 0); + *caps = g_slist_append(*caps, service); + + service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, sbc_codec, + sizeof(sbc_codec)); + *caps = g_slist_append(*caps, service); + + if (fragment) + for (i = 0; i < 10; i++) { + service = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, + sbc_codec, + sizeof(sbc_codec)); + *caps = g_slist_append(*caps, service); + } + + return TRUE; +} + +static gboolean set_configuration_ind(struct avdtp *session, + struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, + GSList *caps, + avdtp_set_configuration_cb cb, + void *user_data) +{ + printf("%s\n", __func__); + + if (reject) + return FALSE; + + if (idle_id > 0) { + g_source_remove(idle_id); + idle_id = 0; + } + + avdtp_stream = stream; + + cb(session, stream, NULL); + + send_command(); + + return TRUE; +} + +static gboolean get_configuration_ind(struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t *err, void *user_data) +{ + printf("%s\n", __func__); + + if (reject) + return FALSE; + + return TRUE; +} + +static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data) +{ + printf("%s\n", __func__); + + if (reject) + return FALSE; + + send_command(); + + return TRUE; +} + +static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *lsep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data) +{ + printf("%s\n", __func__); + + if (reject) + return FALSE; + + if (dev_role == AVDTP_SEP_TYPE_SOURCE) + start_media_player(); + else + start_media_recorder(); + + send_command(); + + return TRUE; +} + +static gboolean suspend_ind(struct avdtp *session, + struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data) +{ + printf("%s\n", __func__); + + if (reject) + return FALSE; + + if (dev_role == AVDTP_SEP_TYPE_SOURCE) + stop_media_player(); + else + stop_media_recorder(); + + return TRUE; +} + +static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data) +{ + printf("%s\n", __func__); + + if (reject) + return FALSE; + + if (dev_role == AVDTP_SEP_TYPE_SOURCE) + stop_media_player(); + else + stop_media_recorder(); + + avdtp_stream = NULL; + + return TRUE; +} + +static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, + struct avdtp_stream *stream, uint8_t *err, + void *user_data) +{ + printf("%s\n", __func__); + + if (dev_role == AVDTP_SEP_TYPE_SOURCE) + stop_media_player(); + else + stop_media_recorder(); + + avdtp_stream = NULL; +} + +static gboolean reconfigure_ind(struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t *err, void *user_data) +{ + printf("%s\n", __func__); + + if (reject) + return FALSE; + + return TRUE; +} + +static gboolean delayreport_ind(struct avdtp *session, + struct avdtp_local_sep *lsep, + uint8_t rseid, uint16_t delay, + uint8_t *err, void *user_data) +{ + printf("%s\n", __func__); + + if (reject) + return FALSE; + + return TRUE; +} + +static struct avdtp_sep_ind sep_ind = { + .get_capability = get_capability_ind, + .set_configuration = set_configuration_ind, + .get_configuration = get_configuration_ind, + .open = open_ind, + .close = close_ind, + .start = start_ind, + .suspend = suspend_ind, + .abort = abort_ind, + .reconfigure = reconfigure_ind, + .delayreport = delayreport_ind, +}; + +static void usage(void) +{ + printf("avdtptest - AVDTP testing ver %s\n", VERSION); + printf("Usage:\n" + "\tavdtptest [options]\n"); + printf("options:\n" + "\t-d SRC (source) or SINK (sink)\n" + "\t-i HCI adapter\n" + "\t-c connect\n" + "\t-l listen\n" + "\t-r reject commands\n" + "\t-f fragment\n" + "\t-p configure stream\n" + "\t-s send command\n" + "\t-v set version (0x0100, 0x0102, 0x0103\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "device_role", 1, 0, 'd' }, + { "adapter", 1, 0, 'i' }, + { "connect", 1, 0, 'c' }, + { "listen", 0, 0, 'l' }, + { "reject", 0, 0, 'r' }, + { "fragment", 0, 0, 'f' }, + { "preconf", 0, 0, 'p' }, + { "send", 1, 0, 's' }, + { "version", 1, 0, 'v' }, + { 0, 0, 0, 0 } +}; + +static GIOChannel *do_listen(GError **err) +{ + if (fragment) + return bt_io_listen(connect_cb, NULL, NULL, NULL, err, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_MTU, 48, + BT_IO_OPT_INVALID); + + return bt_io_listen(connect_cb, NULL, NULL, NULL, err, + BT_IO_OPT_SOURCE_BDADDR, &src, + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); +} + +int main(int argc, char *argv[]) +{ + GError *err = NULL; + int opt; + + bacpy(&src, BDADDR_ANY); + bacpy(&dst, BDADDR_ANY); + + mainloop = g_main_loop_new(NULL, FALSE); + if (!mainloop) { + printf("Failed to create main loop\n"); + + exit(1); + } + + while ((opt = getopt_long(argc, argv, "d:hi:s:c:v:lrfp", + main_options, NULL)) != EOF) { + switch (opt) { + case 'i': + if (!strncmp(optarg, "hci", 3)) + hci_devba(atoi(optarg + 3), &src); + else + str2ba(optarg, &src); + break; + case 'd': + if (!strncasecmp(optarg, "SRC", sizeof("SRC"))) { + dev_role = AVDTP_SEP_TYPE_SOURCE; + } else if (!strncasecmp(optarg, "SINK", + sizeof("SINK"))) { + dev_role = AVDTP_SEP_TYPE_SINK; + } else { + usage(); + exit(0); + } + break; + case 'c': + if (str2ba(optarg, &dst) < 0) { + usage(); + exit(0); + } + break; + case 'l': + bacpy(&dst, BDADDR_ANY); + break; + case 'r': + reject = true; + break; + case 'f': + fragment = true; + break; + case 'p': + preconf = true; + break; + case 's': + parse_command(optarg); + break; + case 'v': + version = strtol(optarg, NULL, 0); + if (version != 0x0100 && version != 0x0102 && + version != 0x0103) { + printf("invalid version\n"); + exit(0); + } + + break; + case 'h': + default: + usage(); + exit(0); + } + } + + local_sep = avdtp_register_sep(dev_role, AVDTP_MEDIA_TYPE_AUDIO, + 0x00, TRUE, &sep_ind, &sep_cfm, NULL); + if (!local_sep) { + printf("Failed to register sep\n"); + exit(0); + } + + if (!bacmp(&dst, BDADDR_ANY)) { + printf("Listening...\n"); + io = do_listen(&err); + } else { + printf("Connecting...\n"); + io = do_connect(&err); + } + + if (!io) { + printf("Failed: %s\n", err->message); + g_error_free(err); + exit(0); + } + + g_main_loop_run(mainloop); + + printf("Done\n"); + + avdtp_unref(avdtp); + avdtp = NULL; + + g_main_loop_unref(mainloop); + mainloop = NULL; + + return 0; +} diff -Nru bluez-4.101/android/avrcp.c bluez-5.23/android/avrcp.c --- bluez-4.101/android/avrcp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avrcp.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,1189 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "btio/btio.h" +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/sdp-client.h" +#include "src/shared/util.h" +#include "src/log.h" + +#include "avctp.h" +#include "avrcp-lib.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "bluetooth.h" +#include "avrcp.h" +#include "utils.h" + +#define L2CAP_PSM_AVCTP 0x17 + +#define AVRCP_FEATURE_CATEGORY_1 0x0001 +#define AVRCP_FEATURE_CATEGORY_2 0x0002 +#define AVRCP_FEATURE_CATEGORY_3 0x0004 +#define AVRCP_FEATURE_CATEGORY_4 0x0008 + +static bdaddr_t adapter_addr; +static uint32_t record_tg_id = 0; +static uint32_t record_ct_id = 0; +static GSList *devices = NULL; +static GIOChannel *server = NULL; +static struct ipc *hal_ipc = NULL; + +struct avrcp_request { + struct avrcp_device *dev; + uint8_t pdu_id; + uint8_t event_id; + uint8_t transaction; +}; + +struct avrcp_device { + bdaddr_t dst; + uint16_t version; + uint16_t features; + struct avrcp *session; + GIOChannel *io; + GQueue *queue; +}; + +static struct avrcp_request *pop_request(uint8_t pdu_id, uint8_t event_id, + bool peek) +{ + GSList *l; + + for (l = devices; l; l = g_slist_next(l)) { + struct avrcp_device *dev = l->data; + GList *reqs = g_queue_peek_head_link(dev->queue); + int i; + + for (i = 0; reqs; reqs = g_list_next(reqs), i++) { + struct avrcp_request *req = reqs->data; + + if (req->pdu_id != pdu_id || req->event_id != event_id) + continue; + + if (!peek) + g_queue_pop_nth(dev->queue, i); + + return req; + } + } + + return NULL; +} + +static void handle_get_play_status(const void *buf, uint16_t len) +{ + const struct hal_cmd_avrcp_get_play_status *cmd = buf; + uint8_t status; + struct avrcp_request *req; + int ret; + + DBG(""); + + req = pop_request(AVRCP_GET_PLAY_STATUS, 0, false); + if (!req) { + status = HAL_STATUS_FAILED; + goto done; + } + + ret = avrcp_get_play_status_rsp(req->dev->session, req->transaction, + cmd->position, cmd->duration, + cmd->status); + if (ret < 0) { + status = HAL_STATUS_FAILED; + g_free(req); + goto done; + } + + status = HAL_STATUS_SUCCESS; + g_free(req); + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAY_STATUS, status); +} + +static void handle_list_player_attrs(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_LIST_PLAYER_ATTRS, HAL_STATUS_FAILED); +} + +static void handle_list_player_values(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_LIST_PLAYER_VALUES, HAL_STATUS_FAILED); +} + +static void handle_get_player_attrs(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_ATTRS, HAL_STATUS_FAILED); +} + +static void handle_get_player_attrs_text(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, HAL_STATUS_FAILED); +} + +static void handle_get_player_values_text(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, HAL_STATUS_FAILED); +} + +static size_t write_element_text(uint8_t id, uint8_t text_len, uint8_t *text, + uint8_t *pdu) +{ + uint16_t charset = 106; + size_t len = 0; + + put_be32(id, pdu); + pdu += 4; + len += 4; + + put_be16(charset, pdu); + pdu += 2; + len += 2; + + put_be16(text_len, pdu); + pdu += 2; + len += 2; + + memcpy(pdu, text, text_len); + len += text_len; + + return len; +} + +static void write_element_attrs(uint8_t *ptr, uint8_t number, uint8_t *pdu, + size_t *len) +{ + int i; + + *pdu = number; + pdu++; + *len += 1; + + for (i = 0; i < number; i++) { + struct hal_avrcp_player_setting_text *text = (void *) ptr; + size_t ret; + + ret = write_element_text(text->id, text->len, text->text, pdu); + + ptr += sizeof(*text) + text->len; + pdu += ret; + *len += ret; + } +} + +static void handle_get_element_attrs_text(const void *buf, uint16_t len) +{ + struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf; + uint8_t status; + struct avrcp_request *req; + uint8_t pdu[IPC_MTU]; + uint8_t *ptr; + size_t pdu_len; + int ret; + + DBG(""); + + req = pop_request(AVRCP_GET_ELEMENT_ATTRIBUTES, 0, false); + if (!req) { + status = HAL_STATUS_FAILED; + goto done; + } + + ptr = (uint8_t *) &cmd->values[0]; + pdu_len = 0; + write_element_attrs(ptr, cmd->number, pdu, &pdu_len); + + ret = avrcp_get_element_attrs_rsp(req->dev->session, req->transaction, + pdu, pdu_len); + if (ret < 0) { + status = HAL_STATUS_FAILED; + g_free(req); + goto done; + } + + status = HAL_STATUS_SUCCESS; + g_free(req); + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, status); +} + +static void handle_set_player_attrs_value(const void *buf, uint16_t len) +{ + DBG(""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, HAL_STATUS_FAILED); +} + +static void handle_register_notification(const void *buf, uint16_t len) +{ + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + uint8_t status; + struct avrcp_request *req; + uint8_t pdu[IPC_MTU]; + size_t pdu_len; + uint8_t code; + bool peek = false; + int ret; + + DBG(""); + + switch (cmd->type) { + case HAL_AVRCP_EVENT_TYPE_INTERIM: + code = AVC_CTYPE_INTERIM; + peek = true; + break; + case HAL_AVRCP_EVENT_TYPE_CHANGED: + code = AVC_CTYPE_CHANGED; + break; + default: + status = HAL_STATUS_FAILED; + goto done; + } + + req = pop_request(AVRCP_REGISTER_NOTIFICATION, cmd->event, peek); + if (!req) { + status = HAL_STATUS_FAILED; + goto done; + } + + pdu[0] = cmd->event; + pdu_len = 1; + + switch (cmd->event) { + case AVRCP_EVENT_STATUS_CHANGED: + case AVRCP_EVENT_TRACK_CHANGED: + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + memcpy(&pdu[1], cmd->data, cmd->len); + pdu_len += cmd->len; + break; + default: + status = HAL_STATUS_FAILED; + goto done; + } + + ret = avrcp_register_notification_rsp(req->dev->session, + req->transaction, code, + pdu, pdu_len); + if (ret < 0) { + status = HAL_STATUS_FAILED; + if (!peek) + g_free(req); + goto done; + } + + status = HAL_STATUS_SUCCESS; + if (!peek) + g_free(req); + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, status); +} + +static void handle_set_volume(const void *buf, uint16_t len) +{ + struct hal_cmd_avrcp_set_volume *cmd = (void *) buf; + struct avrcp_device *dev; + uint8_t status; + int ret; + + DBG(""); + + if (!devices) { + error("AVRCP: No device found to set volume"); + status = HAL_STATUS_FAILED; + goto done; + } + + /* + * Peek the first device since the HAL cannot really address a specific + * device it might mean there could only be one connected. + */ + dev = devices->data; + + ret = avrcp_set_volume(dev->session, cmd->value & 0x7f); + if (ret < 0) { + status = HAL_STATUS_FAILED; + goto done; + } + + status = HAL_STATUS_SUCCESS; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME, + status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_AVRCP_GET_PLAY_STATUS */ + { handle_get_play_status, false, + sizeof(struct hal_cmd_avrcp_get_play_status) }, + /* HAL_OP_AVRCP_LIST_PLAYER_ATTRS */ + { handle_list_player_attrs, true, + sizeof(struct hal_cmd_avrcp_list_player_attrs) }, + /* HAL_OP_AVRCP_LIST_PLAYER_VALUES */ + { handle_list_player_values, true, + sizeof(struct hal_cmd_avrcp_list_player_values) }, + /* HAL_OP_AVRCP_GET_PLAYER_ATTRS */ + { handle_get_player_attrs, true, + sizeof(struct hal_cmd_avrcp_get_player_attrs) }, + /* HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT */ + { handle_get_player_attrs_text, true, + sizeof(struct hal_cmd_avrcp_get_player_attrs_text) }, + /* HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT */ + { handle_get_player_values_text, true, + sizeof(struct hal_cmd_avrcp_get_player_values_text) }, + /* HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT */ + { handle_get_element_attrs_text, true, + sizeof(struct hal_cmd_avrcp_get_element_attrs_text) }, + /* HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE */ + { handle_set_player_attrs_value, true, + sizeof(struct hal_cmd_avrcp_set_player_attrs_value) }, + /* HAL_OP_AVRCP_REGISTER_NOTIFICATION */ + { handle_register_notification, true, + sizeof(struct hal_cmd_avrcp_register_notification) }, + /* HAL_OP_AVRCP_SET_VOLUME */ + { handle_set_volume, false, sizeof(struct hal_cmd_avrcp_set_volume) }, +}; + +static sdp_record_t *avrcp_tg_record(void) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, l2cap, avctp, avrtg; + sdp_profile_desc_t profile[1]; + sdp_list_t *aproto_control, *proto_control[2]; + sdp_record_t *record; + sdp_data_t *psm, *version, *features; + uint16_t lp = L2CAP_PSM_AVCTP; + uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104; + uint16_t feat = (AVRCP_FEATURE_CATEGORY_1 | + AVRCP_FEATURE_CATEGORY_2 | + AVRCP_FEATURE_CATEGORY_3 | + AVRCP_FEATURE_CATEGORY_4); + + record = sdp_record_alloc(); + if (!record) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + /* Service Class ID List */ + sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &avrtg); + sdp_set_service_classes(record, svclass_id); + + /* Protocol Descriptor List */ + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto_control[0] = sdp_list_append(NULL, &l2cap); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto_control[0] = sdp_list_append(proto_control[0], psm); + apseq = sdp_list_append(NULL, proto_control[0]); + + sdp_uuid16_create(&avctp, AVCTP_UUID); + proto_control[1] = sdp_list_append(NULL, &avctp); + version = sdp_data_alloc(SDP_UINT16, &avctp_ver); + proto_control[1] = sdp_list_append(proto_control[1], version); + apseq = sdp_list_append(apseq, proto_control[1]); + + aproto_control = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto_control); + + /* Bluetooth Profile Descriptor List */ + sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); + profile[0].version = avrcp_ver; + pfseq = sdp_list_append(NULL, &profile[0]); + sdp_set_profile_descs(record, pfseq); + + features = sdp_data_alloc(SDP_UINT16, &feat); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); + + sdp_set_info_attr(record, "AVRCP TG", NULL, NULL); + + sdp_data_free(psm); + sdp_data_free(version); + sdp_list_free(proto_control[0], NULL); + sdp_list_free(proto_control[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(aproto_control, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static sdp_record_t *avrcp_ct_record(void) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, l2cap, avctp, avrct, avrctr; + sdp_profile_desc_t profile[1]; + sdp_list_t *aproto, *proto[2]; + sdp_record_t *record; + sdp_data_t *psm, *version, *features; + uint16_t lp = AVCTP_CONTROL_PSM; + uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0104; + uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | + AVRCP_FEATURE_CATEGORY_2 | + AVRCP_FEATURE_CATEGORY_3 | + AVRCP_FEATURE_CATEGORY_4); + + record = sdp_record_alloc(); + if (!record) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + /* Service Class ID List */ + sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &avrct); + sdp_uuid16_create(&avrctr, AV_REMOTE_CONTROLLER_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &avrctr); + sdp_set_service_classes(record, svclass_id); + + /* Protocol Descriptor List */ + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto[0] = sdp_list_append(proto[0], psm); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&avctp, AVCTP_UUID); + proto[1] = sdp_list_append(NULL, &avctp); + version = sdp_data_alloc(SDP_UINT16, &avctp_ver); + proto[1] = sdp_list_append(proto[1], version); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + /* Bluetooth Profile Descriptor List */ + sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); + profile[0].version = avrcp_ver; + pfseq = sdp_list_append(NULL, &profile[0]); + sdp_set_profile_descs(record, pfseq); + + features = sdp_data_alloc(SDP_UINT16, &feat); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); + + sdp_set_info_attr(record, "AVRCP CT", NULL, NULL); + + free(psm); + free(version); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static void avrcp_device_free(void *data) +{ + struct avrcp_device *dev = data; + + if (dev->queue) { + g_queue_foreach(dev->queue, (GFunc) g_free, NULL); + g_queue_free(dev->queue); + } + + if (dev->session) + avrcp_shutdown(dev->session); + + if (dev->io) { + g_io_channel_shutdown(dev->io, FALSE, NULL); + g_io_channel_unref(dev->io); + } + + g_free(dev); +} + +static void avrcp_device_remove(struct avrcp_device *dev) +{ + devices = g_slist_remove(devices, dev); + avrcp_device_free(dev); +} + +static struct avrcp_device *avrcp_device_new(const bdaddr_t *dst) +{ + struct avrcp_device *dev; + + dev = g_new0(struct avrcp_device, 1); + bacpy(&dev->dst, dst); + devices = g_slist_prepend(devices, dev); + + return dev; +} + +static int device_cmp(gconstpointer s, gconstpointer user_data) +{ + const struct avrcp_device *dev = s; + const bdaddr_t *dst = user_data; + + return bacmp(&dev->dst, dst); +} + +static struct avrcp_device *avrcp_device_find(const bdaddr_t *dst) +{ + GSList *l; + + l = g_slist_find_custom(devices, dst, device_cmp); + if (!l) + return NULL; + + return l->data; +} + +static void disconnect_cb(void *data) +{ + struct avrcp_device *dev = data; + + DBG(""); + + dev->session = NULL; + + avrcp_device_remove(dev); +} + +static bool handle_fast_forward(struct avrcp *session, bool pressed, + void *user_data) +{ + struct hal_ev_avrcp_passthrough_cmd ev; + + DBG("pressed %s", pressed ? "true" : "false"); + + ev.id = AVC_FAST_FORWARD; + ev.state = pressed; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev); + + return true; +} + +static bool handle_rewind(struct avrcp *session, bool pressed, + void *user_data) +{ + struct hal_ev_avrcp_passthrough_cmd ev; + + DBG("pressed %s", pressed ? "true" : "false"); + + ev.id = AVC_REWIND; + ev.state = pressed; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_PASSTHROUGH_CMD, sizeof(ev), &ev); + + return true; +} + +static const struct avrcp_passthrough_handler passthrough_handlers[] = { + { AVC_FAST_FORWARD, handle_fast_forward }, + { AVC_REWIND, handle_rewind }, + { }, +}; + +static int handle_get_capabilities_cmd(struct avrcp *session, + uint8_t transaction, void *user_data) +{ + uint8_t events[] = { AVRCP_EVENT_STATUS_CHANGED, + AVRCP_EVENT_TRACK_CHANGED, + AVRCP_EVENT_PLAYBACK_POS_CHANGED }; + + DBG(""); + + /* + * Android do not provide this info via HAL so the list most + * be hardcoded according to what RegisterNotification can + * actually handle + */ + avrcp_get_capabilities_rsp(session, transaction, sizeof(events), + events); + + return -EAGAIN; +} + +static void push_request(struct avrcp_device *dev, uint8_t pdu_id, + uint8_t event_id, uint8_t transaction) +{ + struct avrcp_request *req; + + req = g_new0(struct avrcp_request, 1); + req->dev = dev; + req->pdu_id = pdu_id; + req->event_id = event_id; + req->transaction = transaction; + + g_queue_push_tail(dev->queue, req); +} + +static int handle_get_play_status_cmd(struct avrcp *session, + uint8_t transaction, void *user_data) +{ + struct avrcp_device *dev = user_data; + + DBG(""); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_GET_PLAY_STATUS, 0, NULL); + + push_request(dev, AVRCP_GET_PLAY_STATUS, 0, transaction); + + return -EAGAIN; +} + +static int handle_get_element_attrs_cmd(struct avrcp *session, + uint8_t transaction, uint64_t uid, + uint8_t number, uint32_t *attrs, + void *user_data) +{ + struct avrcp_device *dev = user_data; + uint8_t buf[IPC_MTU]; + struct hal_ev_avrcp_get_element_attrs *ev = (void *) buf; + int i; + + DBG(""); + + ev->number = number; + /* Set everything in case of empty list */ + if (ev->number == 0) { + for (i = 0; i < HAL_AVRCP_MEDIA_ATTR_DURATION; i++) { + /* Skip 0x00 as the attributes start with 0x01 */ + ev->attrs[i] = i + 1; + } + ev->number = i; + goto done; + } + + for (i = 0; i < number; i++) + ev->attrs[i] = attrs[i]; + +done: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_GET_ELEMENT_ATTRS, + sizeof(*ev) + ev->number, ev); + + push_request(dev, AVRCP_GET_ELEMENT_ATTRIBUTES, 0, transaction); + + return -EAGAIN; + +} + +static int handle_register_notification_cmd(struct avrcp *session, + uint8_t transaction, + uint8_t event, + uint32_t interval, + void *user_data) +{ + struct avrcp_device *dev = user_data; + struct hal_ev_avrcp_register_notification ev; + + DBG(""); + + /* TODO: Add any missing events supported by Android */ + switch (event) { + case AVRCP_EVENT_STATUS_CHANGED: + case AVRCP_EVENT_TRACK_CHANGED: + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + break; + default: + return -EINVAL; + } + + ev.event = event; + ev.param = interval; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_REGISTER_NOTIFICATION, + sizeof(ev), &ev); + + push_request(dev, AVRCP_REGISTER_NOTIFICATION, event, transaction); + + return -EAGAIN; +} + +static const struct avrcp_control_ind control_ind = { + .get_capabilities = handle_get_capabilities_cmd, + .get_play_status = handle_get_play_status_cmd, + .get_element_attributes = handle_get_element_attrs_cmd, + .register_notification = handle_register_notification_cmd, +}; + +static bool handle_register_notification_rsp(struct avrcp *session, int err, + uint8_t code, uint8_t event, + uint8_t *params, + void *user_data) +{ + struct avrcp_device *dev = user_data; + struct hal_ev_avrcp_volume_changed ev; + + if (err < 0) { + error("AVRCP: %s", strerror(-err)); + return false; + } + + if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) + return false; + + if (event != AVRCP_EVENT_VOLUME_CHANGED) + return false; + + ev.type = code; + ev.volume = params[0] & 0x7f; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_VOLUME_CHANGED, + sizeof(ev), &ev); + + if (code == AVC_CTYPE_INTERIM) + return true; + + avrcp_register_notification(dev->session, event, 0); + return false; +} + +static void handle_get_capabilities_rsp(struct avrcp *session, int err, + uint8_t number, uint8_t *events, + void *user_data) +{ + struct avrcp_device *dev = user_data; + int i; + + if (err < 0) { + error("AVRCP: %s", strerror(-err)); + return; + } + + for (i = 0; i < number; i++) { + if (events[i] != AVRCP_EVENT_VOLUME_CHANGED) + continue; + + avrcp_register_notification(dev->session, events[i], 0); + break; + } + + return; +} + +static void handle_set_volume_rsp(struct avrcp *session, int err, + uint8_t value, void *user_data) +{ + struct hal_ev_avrcp_volume_changed ev; + + if (err < 0) { + ev.volume = 0; + ev.type = AVC_CTYPE_REJECTED; + goto done; + } + + ev.volume = value; + ev.type = AVC_CTYPE_ACCEPTED; + +done: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_VOLUME_CHANGED, + sizeof(ev), &ev); +} + +static const struct avrcp_control_cfm control_cfm = { + .get_capabilities = handle_get_capabilities_rsp, + .register_notification = handle_register_notification_rsp, + .set_volume = handle_set_volume_rsp, +}; + +static int avrcp_device_add_session(struct avrcp_device *dev, int fd, + uint16_t imtu, uint16_t omtu) +{ + struct hal_ev_avrcp_remote_features ev; + char address[18]; + + dev->session = avrcp_new(fd, imtu, omtu, dev->version); + if (!dev->session) + return -EINVAL; + + avrcp_set_destroy_cb(dev->session, disconnect_cb, dev); + avrcp_set_passthrough_handlers(dev->session, passthrough_handlers, + dev); + avrcp_register_player(dev->session, &control_ind, &control_cfm, dev); + + dev->queue = g_queue_new(); + + ba2str(&dev->dst, address); + + /* FIXME: get the real name of the device */ + avrcp_init_uinput(dev->session, "bluetooth", address); + + bdaddr2android(&dev->dst, ev.bdaddr); + ev.features = HAL_AVRCP_FEATURE_NONE; + + DBG("version 0x%02x", dev->version); + + if (dev->version < 0x0103) + goto done; + + ev.features |= HAL_AVRCP_FEATURE_METADATA; + + if (dev->version < 0x0104) + goto done; + + ev.features |= HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME; + + avrcp_get_capabilities(dev->session, CAP_EVENTS_SUPPORTED); + +done: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_AVRCP, + HAL_EV_AVRCP_REMOTE_FEATURES, + sizeof(ev), &ev); + + return 0; +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + struct avrcp_device *dev = user_data; + uint16_t imtu, omtu; + char address[18]; + GError *gerr = NULL; + int fd; + + if (err) { + error("%s", err->message); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_DEST, address, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + fd = g_io_channel_unix_get_fd(chan); + if (avrcp_device_add_session(dev, fd, imtu, omtu) < 0) { + avrcp_device_free(dev); + return; + } + + g_io_channel_set_close_on_unref(chan, FALSE); + + if (dev->io) { + g_io_channel_unref(dev->io); + dev->io = NULL; + } + + DBG("%s connected", address); +} + +static bool avrcp_device_connect(struct avrcp_device *dev, BtIOConnect cb) +{ + GError *err = NULL; + + dev->io = bt_io_connect(cb, dev, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, L2CAP_PSM_AVCTP, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + return false; + } + + return true; +} + +static void search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct avrcp_device *dev = data; + sdp_list_t *list; + + DBG(""); + + if (!g_slist_find(devices, dev)) + return; + + if (err < 0) { + error("Unable to get AV_REMOTE_SVCLASS_ID SDP record: %s", + strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + error("No AVRCP records found"); + goto fail; + } + + for (list = recs; list; list = list->next) { + sdp_record_t *rec = list->data; + sdp_list_t *l; + sdp_profile_desc_t *desc; + int features; + + if (sdp_get_profile_descs(rec, &l) < 0) + continue; + + desc = l->data; + dev->version = desc->version; + + if (sdp_get_int_attr(rec, SDP_ATTR_SUPPORTED_FEATURES, + &features) == 0) + dev->features = features; + + sdp_list_free(l, free); + break; + } + + if (dev->io) { + GError *gerr = NULL; + if (!bt_io_accept(dev->io, connect_cb, dev, NULL, &gerr)) { + error("bt_io_accept: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + return; + } + + if (!avrcp_device_connect(dev, connect_cb)) { + error("Unable to connect to AVRCP"); + goto fail; + } + + return; + +fail: + avrcp_device_remove(dev); +} + +static int avrcp_device_search(struct avrcp_device *dev) +{ + uuid_t uuid; + + sdp_uuid16_create(&uuid, AV_REMOTE_SVCLASS_ID); + + return bt_search_service(&adapter_addr, &dev->dst, &uuid, search_cb, + dev, NULL, 0); +} + +static void confirm_cb(GIOChannel *chan, gpointer data) +{ + struct avrcp_device *dev; + char address[18]; + bdaddr_t dst; + GError *err = NULL; + + bt_io_get(chan, &err, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST, address, + BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + DBG("incoming connect from %s", address); + + dev = avrcp_device_find(&dst); + if (dev && dev->session) { + error("AVRCP: Refusing unexpected connect"); + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + dev = avrcp_device_new(&dst); + if (avrcp_device_search(dev) < 0) { + error("AVRCP: Failed to search SDP details"); + avrcp_device_free(dev); + g_io_channel_shutdown(chan, TRUE, NULL); + } + + dev->io = g_io_channel_ref(chan); +} + +bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + GError *err = NULL; + sdp_record_t *rec; + + DBG(""); + + bacpy(&adapter_addr, addr); + + server = bt_io_listen(NULL, confirm_cb, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, L2CAP_PSM_AVCTP, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (!server) { + error("Failed to listen on AVDTP channel: %s", err->message); + g_error_free(err); + return false; + } + + rec = avrcp_tg_record(); + if (!rec) { + error("Failed to allocate AVRCP TG record"); + goto fail; + } + + if (bt_adapter_add_record(rec, 0) < 0) { + error("Failed to register AVRCP TG record"); + sdp_record_free(rec); + goto fail; + } + record_tg_id = rec->handle; + + rec = avrcp_ct_record(); + if (!rec) { + error("Failed to allocate AVRCP CT record"); + bt_adapter_remove_record(record_tg_id); + goto fail; + } + + if (bt_adapter_add_record(rec, 0) < 0) { + error("Failed to register AVRCP CT record"); + bt_adapter_remove_record(record_tg_id); + sdp_record_free(rec); + goto fail; + } + record_ct_id = rec->handle; + + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_AVRCP, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + return true; +fail: + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + + return false; +} + +void bt_avrcp_unregister(void) +{ + DBG(""); + + g_slist_free_full(devices, avrcp_device_free); + devices = NULL; + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_AVRCP); + hal_ipc = NULL; + + bt_adapter_remove_record(record_tg_id); + record_tg_id = 0; + + bt_adapter_remove_record(record_ct_id); + record_ct_id = 0; + + if (server) { + g_io_channel_shutdown(server, TRUE, NULL); + g_io_channel_unref(server); + server = NULL; + } +} + +void bt_avrcp_connect(const bdaddr_t *dst) +{ + struct avrcp_device *dev; + char addr[18]; + + DBG(""); + + if (avrcp_device_find(dst)) + return; + + dev = avrcp_device_new(dst); + if (avrcp_device_search(dev) < 0) { + error("AVRCP: Failed to search SDP details"); + avrcp_device_free(dev); + } + + ba2str(&dev->dst, addr); + DBG("connecting to %s", addr); +} + +void bt_avrcp_disconnect(const bdaddr_t *dst) +{ + struct avrcp_device *dev; + + DBG(""); + + dev = avrcp_device_find(dst); + if (!dev) + return; + + if (dev->session) { + avrcp_shutdown(dev->session); + return; + } + + avrcp_device_remove(dev); +} diff -Nru bluez-4.101/android/avrcp.h bluez-5.23/android/avrcp.h --- bluez-4.101/android/avrcp.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avrcp.h 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,28 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +bool bt_avrcp_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_avrcp_unregister(void); + +void bt_avrcp_connect(const bdaddr_t *dst); +void bt_avrcp_disconnect(const bdaddr_t *dst); diff -Nru bluez-4.101/android/avrcp-lib.c bluez-5.23/android/avrcp-lib.c --- bluez-4.101/android/avrcp-lib.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avrcp-lib.c 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,3077 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "lib/bluetooth.h" + +#include "src/shared/util.h" +#include "src/log.h" + +#include "avctp.h" +#include "avrcp-lib.h" + + +/* Packet types */ +#define AVRCP_PACKET_TYPE_SINGLE 0x00 +#define AVRCP_PACKET_TYPE_START 0x01 +#define AVRCP_PACKET_TYPE_CONTINUING 0x02 +#define AVRCP_PACKET_TYPE_END 0x03 + +#define AVRCP_CHARSET_UTF8 0x006a + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct avrcp_header { + uint8_t company_id[3]; + uint8_t pdu_id; + uint8_t packet_type:2; + uint8_t rsvd:6; + uint16_t params_len; + uint8_t params[0]; +} __attribute__ ((packed)); +#define AVRCP_HEADER_LENGTH 7 + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct avrcp_header { + uint8_t company_id[3]; + uint8_t pdu_id; + uint8_t rsvd:6; + uint8_t packet_type:2; + uint16_t params_len; + uint8_t params[0]; +} __attribute__ ((packed)); +#define AVRCP_HEADER_LENGTH 7 + +#else +#error "Unknown byte order" +#endif + +struct avrcp_browsing_header { + uint8_t pdu_id; + uint16_t params_len; + uint8_t params[0]; +} __attribute__ ((packed)); +#define AVRCP_BROWSING_HEADER_LENGTH 3 + +struct avrcp_control_handler { + uint8_t id; + uint8_t code; + uint8_t rsp; + ssize_t (*func) (struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, void *user_data); +}; + +struct avrcp_browsing_handler { + uint8_t id; + ssize_t (*func) (struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, void *user_data); +}; + +struct avrcp_continuing { + uint8_t pdu_id; + struct iovec pdu; +}; + +struct avrcp { + struct avctp *conn; + struct avrcp_player *player; + + const struct avrcp_control_handler *control_handlers; + void *control_data; + unsigned int control_id; + uint16_t control_mtu; + + struct avrcp_continuing *continuing; + + const struct avrcp_passthrough_handler *passthrough_handlers; + void *passthrough_data; + unsigned int passthrough_id; + + const struct avrcp_browsing_handler *browsing_handlers; + void *browsing_data; + unsigned int browsing_id; + + avrcp_destroy_cb_t destroy; + void *destroy_data; +}; + +struct avrcp_player { + const struct avrcp_control_ind *ind; + const struct avrcp_control_cfm *cfm; + + void *user_data; +}; + +static inline uint32_t ntoh24(const uint8_t src[3]) +{ + return src[0] << 16 | src[1] << 8 | src[2]; +} + +static inline void hton24(uint8_t dst[3], uint32_t src) +{ + dst[0] = (src & 0xff0000) >> 16; + dst[1] = (src & 0x00ff00) >> 8; + dst[2] = (src & 0x0000ff); +} + +static void continuing_free(struct avrcp_continuing *continuing) +{ + g_free(continuing->pdu.iov_base); + g_free(continuing); +} + +void avrcp_shutdown(struct avrcp *session) +{ + if (session->conn) { + if (session->control_id > 0) + avctp_unregister_pdu_handler(session->conn, + session->control_id); + if (session->passthrough_id > 0) + avctp_unregister_passthrough_handler(session->conn, + session->passthrough_id); + + /* clear destroy callback that would call shutdown again */ + avctp_set_destroy_cb(session->conn, NULL, NULL); + avctp_shutdown(session->conn); + } + + if (session->destroy) + session->destroy(session->destroy_data); + + if (session->continuing) + continuing_free(session->continuing); + + g_free(session->player); + g_free(session); +} + +static struct avrcp_header *parse_pdu(uint8_t *operands, size_t operand_count) +{ + struct avrcp_header *pdu; + + if (!operands || operand_count < sizeof(*pdu)) { + error("AVRCP: packet too small (%zu bytes)", operand_count); + return NULL; + } + + pdu = (void *) operands; + pdu->params_len = ntohs(pdu->params_len); + + if (operand_count != pdu->params_len + sizeof(*pdu)) { + error("AVRCP: invalid parameter length (%u bytes)", + pdu->params_len); + return NULL; + } + + return pdu; +} + +static struct avrcp_browsing_header *parse_browsing_pdu(uint8_t *operands, + size_t operand_count) +{ + struct avrcp_browsing_header *pdu; + + if (!operands || operand_count < sizeof(*pdu)) { + error("AVRCP: packet too small (%zu bytes)", operand_count); + return NULL; + } + + pdu = (void *) operands; + pdu->params_len = ntohs(pdu->params_len); + + if (operand_count != pdu->params_len + sizeof(*pdu)) { + error("AVRCP: invalid parameter length (%u bytes)", + pdu->params_len); + return NULL; + } + + return pdu; +} + +static uint8_t errno2status(int err) +{ + switch (err) { + case -ENOSYS: + return AVRCP_STATUS_INVALID_COMMAND; + case -EINVAL: + return AVRCP_STATUS_INVALID_PARAM; + case 0: + return AVRCP_STATUS_SUCCESS; + case -ENOTDIR: + return AVRCP_STATUS_NOT_DIRECTORY; + case -EBADRQC: + return AVRCP_STATUS_INVALID_SCOPE; + case -ERANGE: + return AVRCP_STATUS_OUT_OF_BOUNDS; + case -ENOENT: + return AVRCP_STATUS_DOES_NOT_EXIST; + default: + return AVRCP_STATUS_INTERNAL_ERROR; + } +} + +static ssize_t handle_vendordep_pdu(struct avctp *conn, uint8_t transaction, + uint8_t *code, uint8_t *subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + const struct avrcp_control_handler *handler; + struct avrcp_header *pdu; + uint32_t company_id; + ssize_t ret; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + pdu = (void *) operands; + pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; + goto reject; + } + + company_id = ntoh24(pdu->company_id); + if (company_id != IEEEID_BTSIG) { + *code = AVC_CTYPE_NOT_IMPLEMENTED; + return 0; + } + + DBG("AVRCP PDU 0x%02X, len 0x%04X", pdu->pdu_id, pdu->params_len); + + pdu->packet_type = 0; + pdu->rsvd = 0; + + if (!session->control_handlers) + goto reject; + + for (handler = session->control_handlers; handler->id; handler++) { + if (handler->id == pdu->pdu_id) + break; + } + + if (handler->id != pdu->pdu_id || handler->code != *code) { + pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; + goto reject; + } + + if (!handler->func) { + pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; + goto reject; + } + + ret = handler->func(session, transaction, pdu->params_len, pdu->params, + session->control_data); + if (ret < 0) { + if (ret == -EAGAIN) + return ret; + pdu->params[0] = errno2status(ret); + goto reject; + } + + *code = handler->rsp; + pdu->params_len = htons(ret); + + return AVRCP_HEADER_LENGTH + ret; + +reject: + pdu->params_len = htons(1); + *code = AVC_CTYPE_REJECTED; + + return AVRCP_HEADER_LENGTH + 1; +} + +static bool handle_passthrough_pdu(struct avctp *conn, uint8_t op, + bool pressed, void *user_data) +{ + struct avrcp *session = user_data; + const struct avrcp_passthrough_handler *handler; + + if (!session->passthrough_handlers) + return false; + + for (handler = session->passthrough_handlers; handler->func; + handler++) { + if (handler->op == op) + break; + } + + if (handler->func == NULL) + return false; + + return handler->func(session, pressed, session->passthrough_data); +} + +static void disconnect_cb(void *data) +{ + struct avrcp *session = data; + + session->conn = NULL; + + avrcp_shutdown(session); +} + +struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version) +{ + struct avrcp *session; + + session = g_new0(struct avrcp, 1); + + session->conn = avctp_new(fd, imtu, omtu, version); + if (!session->conn) { + g_free(session); + return NULL; + } + + session->passthrough_id = avctp_register_passthrough_handler( + session->conn, + handle_passthrough_pdu, + session); + session->control_id = avctp_register_pdu_handler(session->conn, + AVC_OP_VENDORDEP, + handle_vendordep_pdu, + session); + session->control_mtu = omtu - AVC_DATA_OFFSET; + + /* + * 27.1.2 AV/C Command Frame + * An AV/C command frame contains up to 512 octets of data + */ + if (session->control_mtu > AVC_DATA_MTU) + session->control_mtu = AVC_DATA_MTU; + + avctp_set_destroy_cb(session->conn, disconnect_cb, session); + + return session; +} + +static ssize_t handle_browsing_pdu(struct avctp *conn, + uint8_t transaction, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + const struct avrcp_browsing_handler *handler; + struct avrcp_browsing_header *pdu; + int ret; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + pdu = (void *) operands; + pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; + goto reject; + } + + DBG("AVRCP Browsing PDU 0x%02X, len 0x%04X", pdu->pdu_id, + pdu->params_len); + + if (!session->browsing_handlers) { + pdu->pdu_id = AVRCP_GENERAL_REJECT; + pdu->params[0] = AVRCP_STATUS_INTERNAL_ERROR; + goto reject; + } + + for (handler = session->browsing_handlers; handler->id; handler++) { + if (handler->id == pdu->pdu_id) + break; + } + + if (handler->id != pdu->pdu_id) { + pdu->pdu_id = AVRCP_GENERAL_REJECT; + pdu->params[0] = AVRCP_STATUS_INVALID_COMMAND; + goto reject; + } + + if (!handler->func) { + pdu->params[0] = AVRCP_STATUS_INVALID_PARAM; + goto reject; + } + + ret = handler->func(session, transaction, pdu->params_len, pdu->params, + session->control_data); + if (ret < 0) { + if (ret == -EAGAIN) + return ret; + pdu->params[0] = errno2status(ret); + goto reject; + } + + pdu->params_len = htons(ret); + + return AVRCP_BROWSING_HEADER_LENGTH + ret; + +reject: + pdu->params_len = htons(1); + + return AVRCP_BROWSING_HEADER_LENGTH + 1; +} + +static void browsing_disconnect_cb(void *data) +{ + struct avrcp *session = data; + + session->browsing_id = 0; +} + +int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu, + size_t omtu) +{ + int err; + + err = avctp_connect_browsing(session->conn, fd, imtu, omtu); + if (err < 0) + return err; + + session->browsing_id = avctp_register_browsing_pdu_handler( + session->conn, + handle_browsing_pdu, + session, + browsing_disconnect_cb); + + return 0; +} + +void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb, + void *user_data) +{ + session->destroy = cb; + session->destroy_data = user_data; +} + +static ssize_t get_capabilities(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + if (!params || params_len != 1) + return -EINVAL; + + switch (params[0]) { + case CAP_COMPANY_ID: + params[1] = 1; + hton24(¶ms[2], IEEEID_BTSIG); + return 5; + case CAP_EVENTS_SUPPORTED: + if (!player->ind || !player->ind->get_capabilities) + return -ENOSYS; + return player->ind->get_capabilities(session, transaction, + player->user_data); + } + + return -EINVAL; +} + +static ssize_t list_attributes(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!player->ind || !player->ind->list_attributes) + return -ENOSYS; + + return player->ind->list_attributes(session, transaction, + player->user_data); +} + +static bool check_attributes(uint8_t number, const uint8_t *attrs) +{ + int i; + + for (i = 0; i < number; i++) { + if (attrs[i] > AVRCP_ATTRIBUTE_LAST || + attrs[i] == AVRCP_ATTRIBUTE_ILEGAL) + return false; + } + + return true; +} + +static ssize_t get_attribute_text(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!params || params_len != 1 + params[0]) + return -EINVAL; + + if (!check_attributes(params[0], ¶ms[1])) + return -EINVAL; + + if (!player->ind || !player->ind->get_attribute_text) + return -ENOSYS; + + return player->ind->get_attribute_text(session, transaction, params[0], + ¶ms[1], player->user_data); +} + +static ssize_t list_values(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!params || params_len != 1) + return -EINVAL; + + if (params[0] > AVRCP_ATTRIBUTE_LAST || + params[0] == AVRCP_ATTRIBUTE_ILEGAL) + return -EINVAL; + + if (!player->ind || !player->ind->list_values) + return -ENOSYS; + + return player->ind->list_values(session, transaction, params[0], + player->user_data); +} + +static bool check_value(uint8_t attr, uint8_t number, const uint8_t *values) +{ + int i; + + for (i = 0; i < number; i++) { + /* Check for invalid value */ + switch (attr) { + case AVRCP_ATTRIBUTE_EQUALIZER: + if (values[i] < AVRCP_EQUALIZER_OFF || + values[i] > AVRCP_EQUALIZER_ON) + return false; + break; + case AVRCP_ATTRIBUTE_REPEAT_MODE: + if (values[i] < AVRCP_REPEAT_MODE_OFF || + values[i] > AVRCP_REPEAT_MODE_GROUP) + return false; + break; + case AVRCP_ATTRIBUTE_SHUFFLE: + if (values[i] < AVRCP_SHUFFLE_OFF || + values[i] > AVRCP_SHUFFLE_GROUP) + return false; + break; + case AVRCP_ATTRIBUTE_SCAN: + if (values[i] < AVRCP_SCAN_OFF || + values[i] > AVRCP_SCAN_GROUP) + return false; + break; + } + } + + return true; +} + +static ssize_t get_value_text(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (params_len != 2 + params[1]) + return -EINVAL; + + if (params[0] > AVRCP_ATTRIBUTE_LAST || + params[0] == AVRCP_ATTRIBUTE_ILEGAL) + return -EINVAL; + + if (!check_value(params[0], params[1], ¶ms[2])) + return -EINVAL; + + if (!player->ind || !player->ind->get_value_text) + return -ENOSYS; + + return player->ind->get_value_text(session, transaction, params[0], + params[1], ¶ms[2], + player->user_data); +} + +static ssize_t get_value(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!params || params_len < 1 + params[0]) + return -EINVAL; + + if (!check_attributes(params[0], ¶ms[1])) + return -EINVAL; + + if (!player->ind || !player->ind->get_value) + return -ENOSYS; + + return player->ind->get_value(session, transaction, params[0], + ¶ms[1], player->user_data); +} + +static ssize_t set_value(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + int i; + + DBG(""); + + if (!params || params_len != params[0] * 2 + 1) + return -EINVAL; + + for (i = 0; i < params[0]; i++) { + uint8_t attr = params[i * 2 + 1]; + uint8_t val = params[i * 2 + 2]; + + if (!check_value(attr, 1, &val)) + return -EINVAL; + } + + if (!player->ind || !player->ind->set_value) + return -ENOSYS; + + return player->ind->set_value(session, transaction, params[0], + ¶ms[1], player->user_data); +} + +static ssize_t get_play_status(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + + DBG(""); + + if (!player->ind || !player->ind->get_play_status) + return -ENOSYS; + + return player->ind->get_play_status(session, transaction, + player->user_data); +} + +static ssize_t get_element_attributes(struct avrcp *session, + uint8_t transaction, + uint16_t params_len, + uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint64_t uid; + uint8_t number; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int i; + + DBG(""); + + if (!params || params_len != 9 + params[8] * 4) + return -EINVAL; + + uid = get_be64(params); + number = params[8]; + + for (i = 0; i < number; i++) { + attrs[i] = get_be32(¶ms[9 + i * 4]); + + if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || + attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + } + + if (!player->ind || !player->ind->get_element_attributes) + return -ENOSYS; + + return player->ind->get_element_attributes(session, transaction, uid, + number, attrs, + player->user_data); +} + +static ssize_t register_notification(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint32_t interval; + + DBG(""); + + if (!params || params_len != 5) + return -EINVAL; + + if (!player->ind || !player->ind->register_notification) + return -ENOSYS; + + interval = get_be32(¶ms[1]); + + return player->ind->register_notification(session, transaction, + params[0], interval, + player->user_data); +} + +static ssize_t set_volume(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t volume; + + DBG(""); + + if (!player->ind || !player->ind->set_volume) + return -ENOSYS; + + if (!params || params_len != sizeof(volume)) + return -EINVAL; + + volume = params[0] & 0x7f; + + return player->ind->set_volume(session, transaction, volume, + player->user_data); +} + +static ssize_t set_addressed(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint16_t id; + + DBG(""); + + if (!params || params_len != 2) + return -EINVAL; + + if (!player->ind || !player->ind->set_addressed) + return -ENOSYS; + + id = get_be16(params); + + return player->ind->set_addressed(session, transaction, id, + player->user_data); +} + +static void continuing_new(struct avrcp *session, uint8_t pdu_id, + const struct iovec *iov, int iov_cnt, + size_t offset) +{ + struct avrcp_continuing *continuing; + int i; + size_t len = 0; + + continuing = g_new0(struct avrcp_continuing, 1); + continuing->pdu_id = pdu_id; + + for (i = 0; i < iov_cnt; i++) { + if (i == 0 && offset) { + len += iov[i].iov_len - offset; + continue; + } + + len += iov[i].iov_len; + } + + continuing->pdu.iov_base = g_malloc0(len); + + DBG("len %zu", len); + + for (i = 0; i < iov_cnt; i++) { + if (i == 0 && offset) { + memcpy(continuing->pdu.iov_base, + iov[i].iov_base + offset, + iov[i].iov_len - offset); + continuing->pdu.iov_len += iov[i].iov_len - offset; + continue; + } + + memcpy(continuing->pdu.iov_base + continuing->pdu.iov_len, + iov[i].iov_base, iov[i].iov_len); + continuing->pdu.iov_len += iov[i].iov_len; + } + + session->continuing = continuing; +} + +static int avrcp_send_internal(struct avrcp *session, uint8_t transaction, + uint8_t code, uint8_t subunit, + uint8_t pdu_id, uint8_t type, + const struct iovec *iov, int iov_cnt) +{ + struct iovec pdu[iov_cnt + 1]; + struct avrcp_header hdr; + int i; + + /* + * If a receiver receives a start fragment or non-fragmented AVRCP + * Specific AV/C message when it already has an incomplete fragment + * from that sender then the receiver shall consider the first PDU + * aborted. + */ + if (session->continuing) { + continuing_free(session->continuing); + session->continuing = NULL; + } + + memset(&hdr, 0, sizeof(hdr)); + + pdu[0].iov_base = &hdr; + pdu[0].iov_len = sizeof(hdr); + + hdr.packet_type = type; + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + + if (pdu[0].iov_len + hdr.params_len + iov[i].iov_len <= + session->control_mtu) { + pdu[i + 1].iov_len = iov[i].iov_len; + hdr.params_len += iov[i].iov_len; + if (hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE) + hdr.packet_type = AVRCP_PACKET_TYPE_END; + continue; + } + + /* + * Only send what can fit and store the remaining in the + * continuing iovec + */ + pdu[i + 1].iov_len = session->control_mtu - + (pdu[0].iov_len + hdr.params_len); + hdr.params_len += pdu[i + 1].iov_len; + + continuing_new(session, pdu_id, &iov[i], iov_cnt - i, + pdu[i + 1].iov_len); + + hdr.packet_type = hdr.packet_type != AVRCP_PACKET_TYPE_SINGLE ? + AVRCP_PACKET_TYPE_CONTINUING : + AVRCP_PACKET_TYPE_START; + break; + } + + hton24(hdr.company_id, IEEEID_BTSIG); + hdr.pdu_id = pdu_id; + hdr.params_len = htons(hdr.params_len); + + return avctp_send_vendor(session->conn, transaction, code, subunit, + pdu, iov_cnt + 1); +} + +static ssize_t request_continuing(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct iovec iov; + int err; + + DBG(""); + + if (!params || params_len != 1 || !session->continuing || + session->continuing->pdu_id != params[0]) + return -EINVAL; + + iov.iov_base = session->continuing->pdu.iov_base; + iov.iov_len = session->continuing->pdu.iov_len; + + DBG("len %zu", iov.iov_len); + + session->continuing->pdu.iov_base = NULL; + + err = avrcp_send_internal(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, params[0], + AVRCP_PACKET_TYPE_CONTINUING, &iov, 1); + + g_free(iov.iov_base); + + if (err < 0) + return -EINVAL; + + return -EAGAIN; +} + +static ssize_t abort_continuing(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + DBG(""); + + if (!params || params_len != 1 || !session->continuing) + return -EINVAL; + + continuing_free(session->continuing); + session->continuing = NULL; + + return 0; +} + +static const struct avrcp_control_handler player_handlers[] = { + { AVRCP_GET_CAPABILITIES, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_capabilities }, + { AVRCP_LIST_PLAYER_ATTRIBUTES, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + list_attributes }, + { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_attribute_text }, + { AVRCP_LIST_PLAYER_VALUES, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + list_values }, + { AVRCP_GET_PLAYER_VALUE_TEXT, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_value_text }, + { AVRCP_GET_CURRENT_PLAYER_VALUE, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_value }, + { AVRCP_SET_PLAYER_VALUE, + AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, + set_value }, + { AVRCP_GET_PLAY_STATUS, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_play_status }, + { AVRCP_GET_ELEMENT_ATTRIBUTES, + AVC_CTYPE_STATUS, AVC_CTYPE_STABLE, + get_element_attributes }, + { AVRCP_REGISTER_NOTIFICATION, + AVC_CTYPE_NOTIFY, AVC_CTYPE_INTERIM, + register_notification }, + { AVRCP_SET_ABSOLUTE_VOLUME, + AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, + set_volume }, + { AVRCP_SET_ADDRESSED_PLAYER, + AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, + set_addressed }, + { AVRCP_REQUEST_CONTINUING, + AVC_CTYPE_CONTROL, AVC_CTYPE_STABLE, + request_continuing }, + { AVRCP_ABORT_CONTINUING, + AVC_CTYPE_CONTROL, AVC_CTYPE_ACCEPTED, + abort_continuing }, + { }, +}; + +static void avrcp_set_control_handlers(struct avrcp *session, + const struct avrcp_control_handler *handlers, + void *user_data) +{ + session->control_handlers = handlers; + session->control_data = user_data; +} + +static ssize_t get_folder_items(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t scope; + uint32_t start, end; + uint16_t number; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int i; + + DBG(""); + + if (!player->ind || !player->ind->get_folder_items) + return -ENOSYS; + + if (!params || params_len < 10) + return -EINVAL; + + scope = params[0]; + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EBADRQC; + + start = get_be32(¶ms[1]); + end = get_be32(¶ms[5]); + + if (start > end) + return -ERANGE; + + number = get_be16(¶ms[9]); + + for (i = 0; i < number; i++) { + attrs[i] = get_be32(¶ms[11 + i * 4]); + + if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || + attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + } + + return player->ind->get_folder_items(session, transaction, scope, start, + end, number, attrs, + player->user_data); +} + +static ssize_t change_path(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t direction; + uint16_t counter; + uint64_t uid; + + DBG(""); + + if (!player->ind || !player->ind->change_path) + return -ENOSYS; + + if (!params || params_len < 11) + return -EINVAL; + + counter = get_be16(¶ms[0]); + direction = params[2]; + uid = get_be64(¶ms[3]); + + return player->ind->change_path(session, transaction, counter, + direction, uid, player->user_data); +} + +static ssize_t get_item_attributes(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t scope; + uint64_t uid; + uint16_t counter; + uint8_t number; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int i; + + DBG(""); + + if (!player->ind || !player->ind->get_item_attributes) + return -ENOSYS; + + if (!params || params_len < 12) + return -EINVAL; + + scope = params[0]; + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EBADRQC; + + uid = get_be64(¶ms[1]); + counter = get_be16(¶ms[9]); + number = params[11]; + + for (i = 0; i < number; i++) { + attrs[i] = get_be32(¶ms[12 + i * 4]); + + if (attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || + attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + } + + return player->ind->get_item_attributes(session, transaction, scope, + uid, counter, number, attrs, + player->user_data); +} + +static ssize_t play_item(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t scope; + uint64_t uid; + uint16_t counter; + + DBG(""); + + if (!player->ind || !player->ind->play_item) + return -ENOSYS; + + if (!params || params_len < 11) + return -EINVAL; + + scope = params[0]; + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EBADRQC; + + uid = get_be64(¶ms[1]); + counter = get_be16(¶ms[9]); + + return player->ind->play_item(session, transaction, scope, uid, + counter, player->user_data); +} + +static ssize_t search(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + char *string; + uint16_t len; + int ret; + + DBG(""); + + if (!player->ind || !player->ind->search) + return -ENOSYS; + + if (!params || params_len < 4) + return -EINVAL; + + len = get_be16(¶ms[2]); + if (!len) + return -EINVAL; + + string = strndup((void *) ¶ms[4], len); + + ret = player->ind->search(session, transaction, string, + player->user_data); + + free(string); + + return ret; +} + +static ssize_t add_to_now_playing(struct avrcp *session, uint8_t transaction, + uint16_t params_len, uint8_t *params, + void *user_data) +{ + struct avrcp_player *player = user_data; + uint8_t scope; + uint64_t uid; + uint16_t counter; + + DBG(""); + + if (!player->ind || !player->ind->add_to_now_playing) + return -ENOSYS; + + if (!params || params_len < 11) + return -EINVAL; + + scope = params[0]; + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EBADRQC; + + uid = get_be64(¶ms[1]); + counter = get_be16(¶ms[9]); + + return player->ind->add_to_now_playing(session, transaction, scope, uid, + counter, player->user_data); +} + +static const struct avrcp_browsing_handler browsing_handlers[] = { + { AVRCP_GET_FOLDER_ITEMS, get_folder_items }, + { AVRCP_CHANGE_PATH, change_path }, + { AVRCP_GET_ITEM_ATTRIBUTES, get_item_attributes }, + { AVRCP_PLAY_ITEM, play_item }, + { AVRCP_SEARCH, search }, + { AVRCP_ADD_TO_NOW_PLAYING, add_to_now_playing }, + { }, +}; + +static void avrcp_set_browsing_handlers(struct avrcp *session, + const struct avrcp_browsing_handler *handlers, + void *user_data) +{ + session->browsing_handlers = handlers; + session->browsing_data = user_data; +} + +void avrcp_register_player(struct avrcp *session, + const struct avrcp_control_ind *ind, + const struct avrcp_control_cfm *cfm, + void *user_data) +{ + struct avrcp_player *player; + + player = g_new0(struct avrcp_player, 1); + player->ind = ind; + player->cfm = cfm; + player->user_data = user_data; + + avrcp_set_control_handlers(session, player_handlers, player); + avrcp_set_browsing_handlers(session, browsing_handlers, player); + session->player = player; +} + +void avrcp_set_passthrough_handlers(struct avrcp *session, + const struct avrcp_passthrough_handler *handlers, + void *user_data) +{ + session->passthrough_handlers = handlers; + session->passthrough_data = user_data; +} + +int avrcp_init_uinput(struct avrcp *session, const char *name, + const char *address) +{ + return avctp_init_uinput(session->conn, name, address); +} + +int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code, + uint8_t subunit, uint8_t pdu_id, + const struct iovec *iov, int iov_cnt) +{ + return avrcp_send_internal(session, transaction, code, subunit, pdu_id, + AVRCP_PACKET_TYPE_SINGLE, iov, iov_cnt); +} + +static int status2errno(uint8_t status) +{ + switch (status) { + case AVRCP_STATUS_INVALID_COMMAND: + return -ENOSYS; + case AVRCP_STATUS_INVALID_PARAM: + return -EINVAL; + case AVRCP_STATUS_SUCCESS: + return 0; + case AVRCP_STATUS_NOT_DIRECTORY: + return -ENOTDIR; + case AVRCP_STATUS_INVALID_SCOPE: + return -EBADRQC; + case AVRCP_STATUS_OUT_OF_BOUNDS: + return -ERANGE; + case AVRCP_STATUS_INTERNAL_ERROR: + case AVRCP_STATUS_INVALID_PLAYER_ID: + case AVRCP_STATUS_PLAYER_NOT_BROWSABLE: + case AVRCP_STATUS_NO_AVAILABLE_PLAYERS: + case AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED: + return -EPERM; + default: + return -EPROTO; + } +} + +static int parse_status(struct avrcp_header *pdu) +{ + if (pdu->params_len < 1) + return -EPROTO; + + return status2errno(pdu->params[0]); +} + +static int parse_browsing_status(struct avrcp_browsing_header *pdu) +{ + if (pdu->params_len < 1) + return -EPROTO; + + return status2errno(pdu->params[0]); +} + +static int avrcp_send_req(struct avrcp *session, uint8_t code, uint8_t subunit, + uint8_t pdu_id, const struct iovec *iov, + int iov_cnt, avctp_rsp_cb func, + void *user_data) +{ + struct iovec pdu[iov_cnt + 1]; + struct avrcp_header hdr; + int i; + + memset(&hdr, 0, sizeof(hdr)); + + pdu[0].iov_base = &hdr; + pdu[0].iov_len = sizeof(hdr); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + hdr.params_len += iov[i].iov_len; + } + + hton24(hdr.company_id, IEEEID_BTSIG); + hdr.pdu_id = pdu_id; + hdr.packet_type = AVRCP_PACKET_TYPE_SINGLE; + hdr.params_len = htons(hdr.params_len); + + return avctp_send_vendor_req(session->conn, code, subunit, pdu, + iov_cnt + 1, func, user_data); +} + +static int avrcp_send_browsing_req(struct avrcp *session, uint8_t pdu_id, + const struct iovec *iov, int iov_cnt, + avctp_browsing_rsp_cb func, + void *user_data) +{ + struct iovec pdu[iov_cnt + 1]; + struct avrcp_browsing_header hdr; + int i; + + memset(&hdr, 0, sizeof(hdr)); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + hdr.params_len += iov[i].iov_len; + } + + hdr.pdu_id = pdu_id; + hdr.params_len = htons(hdr.params_len); + + pdu[0].iov_base = &hdr; + pdu[0].iov_len = sizeof(hdr); + + return avctp_send_browsing_req(session->conn, pdu, iov_cnt + 1, + func, user_data); +} + +static int avrcp_send_browsing(struct avrcp *session, uint8_t transaction, + uint8_t pdu_id, const struct iovec *iov, + int iov_cnt) +{ + struct iovec pdu[iov_cnt + 1]; + struct avrcp_browsing_header hdr; + int i; + + memset(&hdr, 0, sizeof(hdr)); + + for (i = 0; i < iov_cnt; i++) { + pdu[i + 1].iov_base = iov[i].iov_base; + pdu[i + 1].iov_len = iov[i].iov_len; + hdr.params_len += iov[i].iov_len; + } + + hdr.pdu_id = pdu_id; + hdr.params_len = htons(hdr.params_len); + + pdu[0].iov_base = &hdr; + pdu[0].iov_len = sizeof(hdr); + + return avctp_send_browsing(session->conn, transaction, pdu, + iov_cnt + 1); +} + +static gboolean get_capabilities_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint8_t *params = NULL; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_capabilities) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + if (pdu->params_len < 2) { + err = -EPROTO; + goto done; + } + + switch (pdu->params[0]) { + case CAP_COMPANY_ID: + case CAP_EVENTS_SUPPORTED: + break; + default: + err = -EPROTO; + goto done; + } + + number = pdu->params[1]; + + if (number > 0) + params = &pdu->params[2]; + + err = 0; + +done: + player->cfm->get_capabilities(session, err, number, params, + player->user_data); + + return FALSE; +} + + +int avrcp_get_capabilities(struct avrcp *session, uint8_t param) +{ + struct iovec iov; + + iov.iov_base = ¶m; + iov.iov_len = sizeof(param); + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_CAPABILITIES, &iov, 1, + get_capabilities_rsp, session); +} + +static gboolean register_notification_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t event = 0; + uint16_t value16; + uint32_t value32; + uint64_t value64; + uint8_t *params = NULL; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->register_notification) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + if (pdu->params_len < 1) { + err = -EPROTO; + goto done; + } + + event = pdu->params[0]; + + switch (event) { + case AVRCP_EVENT_STATUS_CHANGED: + case AVRCP_EVENT_VOLUME_CHANGED: + if (pdu->params_len != 2) { + err = -EPROTO; + goto done; + } + params = &pdu->params[1]; + break; + case AVRCP_EVENT_TRACK_CHANGED: + if (pdu->params_len != 9) { + err = -EPROTO; + goto done; + } + value64 = get_be64(&pdu->params[1]); + params = (uint8_t *) &value64; + break; + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + if (pdu->params_len != 5) { + err = -EPROTO; + goto done; + } + value32 = get_be32(&pdu->params[1]); + params = (uint8_t *) &value32; + break; + case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: + case AVRCP_EVENT_SETTINGS_CHANGED: + if (pdu->params_len < 2) { + err = -EPROTO; + goto done; + } + params = &pdu->params[1]; + break; + case AVRCP_EVENT_UIDS_CHANGED: + if (pdu->params_len != 3) { + err = -EPROTO; + goto done; + } + value16 = get_be16(&pdu->params[1]); + params = (uint8_t *) &value16; + break; + } + + err = 0; + +done: + return player->cfm->register_notification(session, err, code, event, + params, player->user_data); +} + +int avrcp_register_notification(struct avrcp *session, uint8_t event, + uint32_t interval) +{ + struct iovec iov; + uint8_t pdu[5]; + + pdu[0] = event; + put_be32(interval, &pdu[1]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_req(session, AVC_CTYPE_NOTIFY, AVC_SUBUNIT_PANEL, + AVRCP_REGISTER_NOTIFICATION, &iov, 1, + register_notification_rsp, session); +} + +static gboolean list_attributes_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu = (void *) operands; + uint8_t number = 0; + uint8_t *attrs = NULL; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->list_attributes) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + number = pdu->params[0]; + if (number > 0) + attrs = &pdu->params[1]; + + err = 0; + +done: + player->cfm->list_attributes(session, err, number, attrs, + player->user_data); + + return FALSE; +} + +int avrcp_list_player_attributes(struct avrcp *session) +{ + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_LIST_PLAYER_ATTRIBUTES, NULL, 0, + list_attributes_rsp, session); +} + +static int parse_text_rsp(struct avrcp_header *pdu, uint8_t *number, + uint8_t *attrs, char **text) +{ + uint8_t *ptr; + uint16_t params_len; + int i; + + if (pdu->params_len < 1) + return -EPROTO; + + *number = pdu->params[0]; + if (*number > AVRCP_ATTRIBUTE_LAST) { + *number = 0; + return -EPROTO; + } + + params_len = pdu->params_len - 1; + for (i = 0, ptr = &pdu->params[1]; i < *number && params_len > 0; i++) { + uint8_t len; + + if (params_len < 4) + goto fail; + + attrs[i] = ptr[0]; + len = ptr[3]; + + params_len -= 4; + ptr += 4; + + if (len > params_len) + goto fail; + + if (len > 0) { + text[i] = g_strndup((const char *) &ptr[4], len); + params_len -= len; + ptr += len; + } + } + + if (i != *number) + goto fail; + + return 0; + +fail: + for (i -= 1; i >= 0; i--) + g_free(text[i]); + + *number = 0; + + return -EPROTO; +} + +static gboolean get_attribute_text_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; + char *text[AVRCP_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_attribute_text) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = parse_text_rsp(pdu, &number, attrs, text); + +done: + player->cfm->get_attribute_text(session, err, number, attrs, text, + player->user_data); + + return FALSE; +} + +int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number, + uint8_t *attrs) +{ + struct iovec iov[2]; + + if (!number || number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + iov[1].iov_base = attrs; + iov[1].iov_len = number; + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, iov, 2, + get_attribute_text_rsp, session); +} + +static gboolean list_values_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint8_t *values = NULL; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->list_values) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + number = pdu->params[0]; + if (number > 0) + values = &pdu->params[1]; + + err = 0; + +done: + player->cfm->list_values(session, err, number, values, + player->user_data); + + return FALSE; +} + +int avrcp_list_player_values(struct avrcp *session, uint8_t attr) +{ + struct iovec iov; + + iov.iov_base = &attr; + iov.iov_len = sizeof(attr); + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_LIST_PLAYER_VALUES, &iov, 1, + list_values_rsp, session); +} + +static gboolean get_value_text_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint8_t values[AVRCP_ATTRIBUTE_LAST]; + char *text[AVRCP_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_value_text) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = parse_text_rsp(pdu, &number, values, text); + +done: + player->cfm->get_value_text(session, err, number, values, text, + player->user_data); + + return FALSE; +} + +int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr, + uint8_t number, uint8_t *values) +{ + struct iovec iov[2]; + uint8_t pdu[2]; + + if (!number) + return -EINVAL; + + pdu[0] = attr; + pdu[1] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + iov[1].iov_base = values; + iov[1].iov_len = number; + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_PLAYER_VALUE_TEXT, iov, 2, + get_value_text_rsp, session); +} + +static int parse_value(struct avrcp_header *pdu, uint8_t *number, + uint8_t *attrs, uint8_t *values) +{ + int i; + + if (pdu->params_len < 1) + return -EPROTO; + + *number = pdu->params[0]; + + /* + * Check if PDU is big enough to hold the number of (attribute, value) + * tuples. + */ + if (*number > AVRCP_ATTRIBUTE_LAST || + 1 + *number * 2 != pdu->params_len) { + *number = 0; + return -EPROTO; + } + + for (i = 0; i < *number; i++) { + attrs[i] = pdu->params[i * 2 + 1]; + values[i] = pdu->params[i * 2 + 2]; + } + + return 0; +} + +static gboolean get_value_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu = (void *) operands; + uint8_t number = 0; + uint8_t attrs[AVRCP_ATTRIBUTE_LAST]; + uint8_t values[AVRCP_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_value) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = parse_value(pdu, &number, attrs, values); + +done: + player->cfm->get_value(session, err, number, attrs, values, + player->user_data); + + return FALSE; +} + +int avrcp_get_current_player_value(struct avrcp *session, uint8_t number, + uint8_t *attrs) + +{ + struct iovec iov[2]; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + iov[1].iov_base = attrs; + iov[1].iov_len = number; + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_CURRENT_PLAYER_VALUE, iov, 2, + get_value_rsp, session); +} + +static gboolean set_value_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->set_value) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = 0; + +done: + player->cfm->set_value(session, err, player->user_data); + + return FALSE; +} + +int avrcp_set_player_value(struct avrcp *session, uint8_t number, + uint8_t *attrs, uint8_t *values) +{ + struct iovec iov; + uint8_t pdu[2 * AVRCP_ATTRIBUTE_LAST + 1]; + int i; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + pdu[0] = number; + + for (i = 0; i < number; i++) { + pdu[i * 2 + 1] = attrs[i]; + pdu[i * 2 + 2] = values[i]; + } + + iov.iov_base = pdu; + iov.iov_len = 1 + number * 2; + + return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, + AVRCP_SET_PLAYER_VALUE, &iov, 1, + set_value_rsp, session); +} + +static gboolean get_play_status_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t status = 0; + uint32_t position = 0; + uint32_t duration = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_play_status) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + if (pdu->params_len < 5) { + err = -EPROTO; + goto done; + } + + duration = get_be32(&pdu->params[0]); + position = get_be32(&pdu->params[4]); + status = pdu->params[8]; + err = 0; + +done: + player->cfm->get_play_status(session, err, status, position, duration, + player->user_data); + + return FALSE; +} + +int avrcp_get_play_status(struct avrcp *session) +{ + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_PLAY_STATUS, NULL, 0, + get_play_status_rsp, session); +} + +static gboolean set_volume_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t value = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->set_volume) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + if (pdu->params_len < 1) { + err = -EPROTO; + goto done; + } + + value = pdu->params[0] & 0x7f; + err = 0; + +done: + player->cfm->set_volume(session, err, value, player->user_data); + + return FALSE; +} + +int avrcp_set_volume(struct avrcp *session, uint8_t volume) +{ + struct iovec iov; + + iov.iov_base = &volume; + iov.iov_len = sizeof(volume); + + return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, + AVRCP_SET_ABSOLUTE_VOLUME, &iov, 1, + set_volume_rsp, session); +} + +static int parse_attribute_list(uint8_t *params, uint16_t params_len, + uint8_t number, uint32_t *attrs, char **text) +{ + int i; + + if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EPROTO; + + for (i = 0; number > 0 && params_len > i; number--) { + uint16_t charset, len; + + if (params_len < 8) + goto fail; + + attrs[i] = get_be32(¶ms[i]); + i += sizeof(uint32_t); + + charset = get_be16(¶ms[i]); + i += sizeof(uint16_t); + + len = get_be16(¶ms[i]); + i += sizeof(uint16_t); + + if (len > params_len) + goto fail; + + if (charset == AVRCP_CHARSET_UTF8) + text[i] = g_strndup((const char *) ¶ms[i], len); + + i += len; + } + + return 0; + +fail: + for (i -= 1; i >= 0; i--) + g_free(text[i]); + + return -EPROTO; +} + +static int parse_elements(struct avrcp_header *pdu, uint8_t *number, + uint32_t *attrs, char **text) +{ + if (pdu->params_len < 1) + return -EPROTO; + + *number = pdu->params[0]; + if (*number > AVRCP_MEDIA_ATTRIBUTE_LAST) { + *number = 0; + return -EPROTO; + } + + return parse_attribute_list(&pdu->params[1], pdu->params_len - 1, + *number, attrs, text); +} + +static int parse_items(struct avrcp_browsing_header *pdu, uint8_t *number, + uint32_t *attrs, char **text) +{ + if (pdu->params_len < 2) + return -EPROTO; + + *number = pdu->params[1]; + if (*number > AVRCP_MEDIA_ATTRIBUTE_LAST) { + *number = 0; + return -EPROTO; + } + + return parse_attribute_list(&pdu->params[2], pdu->params_len - 2, + *number, attrs, text); +} + +static gboolean get_element_attributes_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + uint8_t number = 0; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + char *text[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_element_attributes) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + if (code == AVC_CTYPE_REJECTED) { + err = parse_status(pdu); + goto done; + } + + err = parse_elements(pdu, &number, attrs, text); + +done: + player->cfm->get_element_attributes(session, err, number, attrs, text, + player->user_data); + + return FALSE; +} + +int avrcp_get_element_attributes(struct avrcp *session) +{ + struct iovec iov; + uint8_t pdu[9]; + + /* This returns all attributes */ + memset(pdu, 0, sizeof(pdu)); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_req(session, AVC_CTYPE_STATUS, AVC_SUBUNIT_PANEL, + AVRCP_GET_ELEMENT_ATTRIBUTES, &iov, 1, + get_element_attributes_rsp, session); +} + +static gboolean set_addressed_rsp(struct avctp *conn, + uint8_t code, uint8_t subunit, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_header *pdu; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->set_addressed) + return FALSE; + + pdu = parse_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_status(pdu); + +done: + player->cfm->set_addressed(session, err, player->user_data); + + return FALSE; +} + +int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id) +{ + struct iovec iov; + uint8_t pdu[2]; + + put_be16(player_id, pdu); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_req(session, AVC_CTYPE_CONTROL, AVC_SUBUNIT_PANEL, + AVRCP_SET_ADDRESSED_PLAYER, &iov, 1, + set_addressed_rsp, session); +} + +static gboolean set_browsed_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint16_t counter = 0; + uint32_t items = 0; + uint8_t depth = 0, count; + char **folders, *path = NULL; + int err; + size_t i; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->set_browsed) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + if (pdu->params_len < 10) { + err = -EPROTO; + goto done; + } + + counter = get_be16(&pdu->params[1]); + items = get_be32(&pdu->params[3]); + depth = pdu->params[9]; + + folders = g_new0(char *, depth + 2); + folders[0] = g_strdup("/Filesystem"); + + for (i = 10, count = 1; count - 1 < depth && i < pdu->params_len; + count++) { + uint8_t len; + + len = pdu->params[i++]; + + if (i + len > pdu->params_len || len == 0) { + g_strfreev(folders); + err = -EPROTO; + goto done; + } + + folders[count] = g_memdup(&pdu->params[i], len); + i += len; + } + + path = g_build_pathv("/", folders); + g_strfreev(folders); + +done: + player->cfm->set_browsed(session, err, counter, items, path, + player->user_data); + + return FALSE; +} + +int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id) +{ + struct iovec iov; + uint8_t pdu[2]; + + put_be16(player_id, pdu); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing_req(session, AVRCP_SET_BROWSED_PLAYER, + &iov, 1, set_browsed_rsp, session); +} + +static gboolean get_folder_items_rsp(struct avctp *conn, + uint8_t *operands, size_t operand_count, + void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint16_t counter = 0, number = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_folder_items) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + if (pdu->params_len < 5) { + err = -EPROTO; + goto done; + } + + counter = get_be16(&pdu->params[1]); + number = get_be16(&pdu->params[3]); + + /* FIXME: Add proper parsing for each item type */ + +done: + player->cfm->get_folder_items(session, err, counter, number, + &pdu->params[5], player->user_data); + + return FALSE; +} + +int avrcp_get_folder_items(struct avrcp *session, uint8_t scope, + uint32_t start, uint32_t end, uint8_t number, + uint32_t *attrs) +{ + + struct iovec iov[2]; + uint8_t pdu[10]; + int i; + + pdu[0] = scope; + put_be32(start, &pdu[1]); + put_be32(end, &pdu[5]); + pdu[9] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + if (!number) + return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS, + iov, 1, get_folder_items_rsp, + session); + + for (i = 0; i < number; i++) + put_be32(attrs[i], &attrs[i]); + + iov[1].iov_base = attrs; + iov[1].iov_len = number * sizeof(*attrs); + + return avrcp_send_browsing_req(session, AVRCP_GET_FOLDER_ITEMS, + iov, 2, get_folder_items_rsp, session); +} + +static gboolean change_path_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint32_t items = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->change_path) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + if (pdu->params_len < 5) { + err = -EPROTO; + goto done; + } + + items = get_be32(&pdu->params[1]); + +done: + player->cfm->change_path(session, err, items, player->user_data); + + return FALSE; +} + +int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid, + uint16_t counter) +{ + struct iovec iov; + uint8_t pdu[11]; + + put_be16(counter, &pdu[0]); + pdu[2] = direction; + put_be64(uid, &pdu[3]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing_req(session, AVRCP_CHANGE_PATH, + &iov, 1, change_path_rsp, session); +} + +static gboolean get_item_attributes_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint8_t number = 0; + uint32_t attrs[AVRCP_MEDIA_ATTRIBUTE_LAST]; + char *text[AVRCP_MEDIA_ATTRIBUTE_LAST]; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->get_item_attributes) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + err = parse_items(pdu, &number, attrs, text); + +done: + player->cfm->get_item_attributes(session, err, number, attrs, text, + player->user_data); + + return FALSE; +} + +int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope, + uint64_t uid, uint16_t counter, uint8_t number, + uint32_t *attrs) +{ + struct iovec iov[2]; + uint8_t pdu[12]; + int i; + + pdu[0] = scope; + put_be64(uid, &pdu[1]); + put_be16(counter, &pdu[9]); + pdu[11] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + if (!number) + return avrcp_send_browsing_req(session, + AVRCP_GET_ITEM_ATTRIBUTES, + iov, 1, get_item_attributes_rsp, + session); + + if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + + for (i = 0; i < number; i++) { + if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST || + attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL) + return -EINVAL; + put_be32(attrs[i], &attrs[i]); + } + + iov[1].iov_base = attrs; + iov[1].iov_len = number * sizeof(*attrs); + + return avrcp_send_browsing_req(session, AVRCP_GET_ITEM_ATTRIBUTES, + iov, 2, get_item_attributes_rsp, + session); +} + +static gboolean play_item_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->play_item) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + +done: + player->cfm->play_item(session, err, player->user_data); + + return FALSE; +} + +int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid, + uint16_t counter) +{ + struct iovec iov; + uint8_t pdu[11]; + + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EINVAL; + + pdu[0] = scope; + put_be64(uid, &pdu[1]); + put_be16(counter, &pdu[9]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing_req(session, AVRCP_PLAY_ITEM, &iov, 1, + play_item_rsp, session); +} + +static gboolean search_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + uint16_t counter = 0; + uint32_t items = 0; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->search) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + if (err < 0) + goto done; + + if (pdu->params_len < 7) { + err = -EPROTO; + goto done; + } + + counter = get_be16(&pdu->params[1]); + items = get_be32(&pdu->params[3]); + + err = 0; + +done: + player->cfm->search(session, err, counter, items, player->user_data); + + return FALSE; +} + +int avrcp_search(struct avrcp *session, const char *string) +{ + struct iovec iov[2]; + uint8_t pdu[4]; + size_t len; + + if (!string) + return -EINVAL; + + len = strnlen(string, UINT8_MAX); + + put_be16(AVRCP_CHARSET_UTF8, &pdu[0]); + put_be16(len, &pdu[2]); + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + iov[1].iov_base = (void *) string; + iov[1].iov_len = len; + + return avrcp_send_browsing_req(session, AVRCP_SEARCH, iov, 2, + search_rsp, session); +} + +static gboolean add_to_now_playing_rsp(struct avctp *conn, uint8_t *operands, + size_t operand_count, void *user_data) +{ + struct avrcp *session = user_data; + struct avrcp_player *player = session->player; + struct avrcp_browsing_header *pdu; + int err; + + DBG(""); + + if (!player || !player->cfm || !player->cfm->add_to_now_playing) + return FALSE; + + pdu = parse_browsing_pdu(operands, operand_count); + if (!pdu) { + err = -EPROTO; + goto done; + } + + err = parse_browsing_status(pdu); + +done: + player->cfm->add_to_now_playing(session, err, player->user_data); + + return FALSE; +} + +int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid, + uint16_t counter) +{ + struct iovec iov; + uint8_t pdu[11]; + + if (scope > AVRCP_MEDIA_NOW_PLAYING) + return -EINVAL; + + pdu[0] = scope; + put_be64(uid, &pdu[1]); + put_be16(counter, &pdu[9]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing_req(session, AVRCP_ADD_TO_NOW_PLAYING, + &iov, 1, add_to_now_playing_rsp, + session); +} + +int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *events) +{ + uint8_t pdu[2]; + struct iovec iov[2]; + + if (number > AVRCP_EVENT_LAST) + return -EINVAL; + + pdu[0] = CAP_EVENTS_SUPPORTED; + pdu[1] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + iov[1].iov_base = events; + iov[1].iov_len = number; + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_CAPABILITIES, + iov, 2); +} + +int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs) +{ + struct iovec iov[2]; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + if (!number) + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES, + iov, 1); + + iov[1].iov_base = attrs; + iov[1].iov_len = number; + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_ATTRIBUTES, + iov, 2); +} + +int avrcp_get_player_attribute_text_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *attrs, const char **text) +{ + struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2]; + uint8_t val[AVRCP_ATTRIBUTE_LAST][4]; + int i; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + for (i = 0; i < number; i++) { + uint8_t len = 0; + + if (attrs[i] > AVRCP_ATTRIBUTE_LAST || + attrs[i] == AVRCP_ATTRIBUTE_ILEGAL) + return -EINVAL; + + if (text[i]) + len = strlen(text[i]); + + val[i][0] = attrs[i]; + put_be16(AVRCP_CHARSET_UTF8, &val[i][1]); + val[i][3] = len; + + iov[i + 1].iov_base = val[i]; + iov[i + 1].iov_len = sizeof(val[i]); + + iov[i + 2].iov_base = (void *) text[i]; + iov[i + 2].iov_len = len; + } + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, + iov, 1 + i * 2); +} + +int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *values) +{ + struct iovec iov[2]; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + iov[1].iov_base = values; + iov[1].iov_len = number; + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_LIST_PLAYER_VALUES, + iov, 2); +} + +int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction, + uint32_t position, uint32_t duration, + uint8_t status) +{ + struct iovec iov; + uint8_t pdu[9]; + + put_be32(duration, &pdu[0]); + put_be32(position, &pdu[4]); + pdu[8] = status; + + iov.iov_base = &pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_PLAY_STATUS, + &iov, 1); +} + +int avrcp_get_player_values_text_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *values, const char **text) +{ + struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST * 2]; + uint8_t val[AVRCP_ATTRIBUTE_LAST][4]; + int i; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + for (i = 0; i < number; i++) { + uint8_t len = 0; + + if (text[i]) + len = strlen(text[i]); + + val[i][0] = values[i]; + put_be16(AVRCP_CHARSET_UTF8, &val[i][1]); + val[i][3] = len; + + iov[i + 1].iov_base = val[i]; + iov[i + 1].iov_len = sizeof(val[i]); + + iov[i + 2].iov_base = (void *) text[i]; + iov[i + 2].iov_len = len; + } + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_PLAYER_VALUE_TEXT, + iov, 1 + i * 2); +} + +int avrcp_get_current_player_value_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *attrs, uint8_t *values) +{ + struct iovec iov[1 + AVRCP_ATTRIBUTE_LAST]; + uint8_t val[AVRCP_ATTRIBUTE_LAST][2]; + int i; + + if (number > AVRCP_ATTRIBUTE_LAST) + return -EINVAL; + + iov[0].iov_base = &number; + iov[0].iov_len = sizeof(number); + + for (i = 0; i < number; i++) { + val[i][0] = attrs[i]; + val[i][1] = values[i]; + + iov[i + 1].iov_base = val[i]; + iov[i + 1].iov_len = sizeof(val[i]); + } + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_CURRENT_PLAYER_VALUE, + iov, 1 + i); +} + +int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction) +{ + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_SET_PLAYER_VALUE, NULL, 0); +} + +int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction, + uint8_t *params, size_t params_len) +{ + struct iovec iov; + + iov.iov_base = params; + iov.iov_len = params_len; + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_GET_ELEMENT_ATTRIBUTES, + &iov, 1); +} + +int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction, + uint8_t code, uint8_t *params, + size_t params_len) +{ + struct iovec iov; + + iov.iov_base = params; + iov.iov_len = params_len; + + return avrcp_send(session, transaction, code, + AVC_SUBUNIT_PANEL, AVRCP_REGISTER_NOTIFICATION, + &iov, 1); +} + +int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction, + uint8_t volume) +{ + struct iovec iov; + + if (volume > 127) + return -EINVAL; + + iov.iov_base = &volume; + iov.iov_len = sizeof(volume); + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_SET_ABSOLUTE_VOLUME, + &iov, 1); +} + +int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction, + uint8_t status) +{ + struct iovec iov; + + iov.iov_base = &status; + iov.iov_len = sizeof(status); + + return avrcp_send(session, transaction, AVC_CTYPE_STABLE, + AVC_SUBUNIT_PANEL, AVRCP_SET_ADDRESSED_PLAYER, + &iov, 1); +} + +int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint8_t number, + uint8_t *type, uint16_t *len, + uint8_t **params) +{ + struct iovec iov[UINT8_MAX * 2 + 1]; + uint8_t pdu[5]; + uint8_t item[UINT8_MAX][3]; + int i; + + pdu[0] = AVRCP_STATUS_SUCCESS; + put_be16(counter, &pdu[1]); + put_be16(number, &pdu[3]); + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + for (i = 0; i < number; i++) { + item[i][0] = type[i]; + put_be16(len[i], &item[i][1]); + + iov[i * 2 + 1].iov_base = item[i]; + iov[i * 2 + 1].iov_len = sizeof(item[i]); + + iov[i * 2 + 2].iov_base = params[i]; + iov[i * 2 + 2].iov_len = len[i]; + } + + return avrcp_send_browsing(session, transaction, AVRCP_GET_FOLDER_ITEMS, + iov, number * 2 + 1); +} + +int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction, + uint32_t items) +{ + struct iovec iov; + uint8_t pdu[5]; + + pdu[0] = AVRCP_STATUS_SUCCESS; + put_be32(items, &pdu[1]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing(session, transaction, AVRCP_CHANGE_PATH, + &iov, 1); +} + +int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint32_t *attrs, + const char **text) +{ + struct iovec iov[AVRCP_MEDIA_ATTRIBUTE_LAST * 2 + 1]; + uint8_t val[AVRCP_MEDIA_ATTRIBUTE_LAST][8]; + uint8_t pdu[2]; + int i; + + if (number > AVRCP_MEDIA_ATTRIBUTE_LAST) + return -EINVAL; + + pdu[0] = AVRCP_STATUS_SUCCESS; + pdu[1] = number; + + iov[0].iov_base = pdu; + iov[0].iov_len = sizeof(pdu); + + for (i = 0; i < number; i++) { + uint16_t len = 0; + + if (attrs[i] > AVRCP_MEDIA_ATTRIBUTE_LAST || + attrs[i] == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL) + return -EINVAL; + + if (text[i]) + len = strlen(text[i]); + + put_be32(attrs[i], &val[i][0]); + put_be16(AVRCP_CHARSET_UTF8, &val[i][4]); + put_be16(len, &val[i][6]); + + iov[i + 1].iov_base = val[i]; + iov[i + 1].iov_len = sizeof(val[i]); + + iov[i + 2].iov_base = (void *) text[i]; + iov[i + 2].iov_len = len; + } + + return avrcp_send_browsing(session, transaction, + AVRCP_GET_ITEM_ATTRIBUTES, iov, + number * 2 + 1); +} + +int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction) +{ + struct iovec iov; + uint8_t pdu; + + pdu = AVRCP_STATUS_SUCCESS; + + iov.iov_base = &pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing(session, transaction, AVRCP_PLAY_ITEM, + &iov, 1); +} + +int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint32_t items) +{ + struct iovec iov; + uint8_t pdu[7]; + + pdu[0] = AVRCP_STATUS_SUCCESS; + put_be16(counter, &pdu[1]); + put_be32(items, &pdu[3]); + + iov.iov_base = pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing(session, transaction, AVRCP_SEARCH, + &iov, 1); +} + +int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction) +{ + struct iovec iov; + uint8_t pdu; + + pdu = AVRCP_STATUS_SUCCESS; + + iov.iov_base = &pdu; + iov.iov_len = sizeof(pdu); + + return avrcp_send_browsing(session, transaction, + AVRCP_ADD_TO_NOW_PLAYING, &iov, 1); +} + +int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op) +{ + uint8_t params[5]; + + if (!vendor) + return avctp_send_passthrough(session->conn, op, NULL, 0); + + hton24(params, vendor); + put_be16(op, ¶ms[3]); + + return avctp_send_passthrough(session->conn, AVC_VENDOR_UNIQUE, params, + sizeof(params)); +} diff -Nru bluez-4.101/android/avrcp-lib.h bluez-5.23/android/avrcp-lib.h --- bluez-4.101/android/avrcp-lib.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/avrcp-lib.h 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,336 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* Control PDU ids */ +#define AVRCP_GET_CAPABILITIES 0x10 +#define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11 +#define AVRCP_LIST_PLAYER_VALUES 0x12 +#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13 +#define AVRCP_SET_PLAYER_VALUE 0x14 +#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15 +#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16 +#define AVRCP_DISPLAYABLE_CHARSET 0x17 +#define AVRCP_CT_BATTERY_STATUS 0x18 +#define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20 +#define AVRCP_GET_PLAY_STATUS 0x30 +#define AVRCP_REGISTER_NOTIFICATION 0x31 +#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 +#define AVRCP_GET_ITEM_ATTRIBUTES 0x73 +#define AVRCP_PLAY_ITEM 0x74 +#define AVRCP_SEARCH 0x80 +#define AVRCP_ADD_TO_NOW_PLAYING 0x90 +#define AVRCP_GENERAL_REJECT 0xA0 + +/* Notification events */ +#define AVRCP_EVENT_STATUS_CHANGED 0x01 +#define AVRCP_EVENT_TRACK_CHANGED 0x02 +#define AVRCP_EVENT_TRACK_REACHED_END 0x03 +#define AVRCP_EVENT_TRACK_REACHED_START 0x04 +#define AVRCP_EVENT_PLAYBACK_POS_CHANGED 0x05 +#define AVRCP_EVENT_SETTINGS_CHANGED 0x08 +#define AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED 0x0a +#define AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED 0x0b +#define AVRCP_EVENT_UIDS_CHANGED 0x0c +#define AVRCP_EVENT_VOLUME_CHANGED 0x0d +#define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED + +/* Status codes */ +#define AVRCP_STATUS_INVALID_COMMAND 0x00 +#define AVRCP_STATUS_INVALID_PARAM 0x01 +#define AVRCP_STATUS_PARAM_NOT_FOUND 0x02 +#define AVRCP_STATUS_INTERNAL_ERROR 0x03 +#define AVRCP_STATUS_SUCCESS 0x04 +#define AVRCP_STATUS_NOT_DIRECTORY 0x08 +#define AVRCP_STATUS_DOES_NOT_EXIST 0x09 +#define AVRCP_STATUS_INVALID_SCOPE 0x0a +#define AVRCP_STATUS_OUT_OF_BOUNDS 0x0b +#define AVRCP_STATUS_INVALID_PLAYER_ID 0x11 +#define AVRCP_STATUS_PLAYER_NOT_BROWSABLE 0x12 +#define AVRCP_STATUS_NO_AVAILABLE_PLAYERS 0x15 +#define AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED 0x16 + +/* Capabilities for AVRCP_GET_CAPABILITIES pdu */ +#define CAP_COMPANY_ID 0x02 +#define CAP_EVENTS_SUPPORTED 0x03 + +/* Player Attributes */ +#define AVRCP_ATTRIBUTE_ILEGAL 0x00 +#define AVRCP_ATTRIBUTE_EQUALIZER 0x01 +#define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02 +#define AVRCP_ATTRIBUTE_SHUFFLE 0x03 +#define AVRCP_ATTRIBUTE_SCAN 0x04 +#define AVRCP_ATTRIBUTE_LAST AVRCP_ATTRIBUTE_SCAN + +/* equalizer values */ +#define AVRCP_EQUALIZER_OFF 0x01 +#define AVRCP_EQUALIZER_ON 0x02 + +/* repeat mode values */ +#define AVRCP_REPEAT_MODE_OFF 0x01 +#define AVRCP_REPEAT_MODE_SINGLE 0x02 +#define AVRCP_REPEAT_MODE_ALL 0x03 +#define AVRCP_REPEAT_MODE_GROUP 0x04 + +/* shuffle values */ +#define AVRCP_SHUFFLE_OFF 0x01 +#define AVRCP_SHUFFLE_ALL 0x02 +#define AVRCP_SHUFFLE_GROUP 0x03 + +/* scan values */ +#define AVRCP_SCAN_OFF 0x01 +#define AVRCP_SCAN_ALL 0x02 +#define AVRCP_SCAN_GROUP 0x03 + +/* media attributes */ +#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00 +#define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01 +#define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02 +#define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03 +#define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04 +#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05 +#define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06 +#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 +#define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_DURATION + +/* Media Scope */ +#define AVRCP_MEDIA_PLAYER_LIST 0x00 +#define AVRCP_MEDIA_PLAYER_VFS 0x01 +#define AVRCP_MEDIA_SEARCH 0x02 +#define AVRCP_MEDIA_NOW_PLAYING 0x03 + +/* Company IDs for vendor dependent commands */ +#define IEEEID_BTSIG 0x001958 + +struct avrcp; + +struct avrcp_control_ind { + int (*get_capabilities) (struct avrcp *session, uint8_t transaction, + void *user_data); + int (*list_attributes) (struct avrcp *session, uint8_t transaction, + void *user_data); + int (*get_attribute_text) (struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, + void *user_data); + int (*list_values) (struct avrcp *session, uint8_t transaction, + uint8_t attr, void *user_data); + int (*get_value_text) (struct avrcp *session, uint8_t transaction, + uint8_t attr, uint8_t number, + uint8_t *values, void *user_data); + int (*get_value) (struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, + void *user_data); + int (*set_value) (struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs, + void *user_data); + int (*get_play_status) (struct avrcp *session, uint8_t transaction, + void *user_data); + int (*get_element_attributes) (struct avrcp *session, + uint8_t transaction, uint64_t uid, + uint8_t number, uint32_t *attrs, + void *user_data); + int (*register_notification) (struct avrcp *session, + uint8_t transaction, uint8_t event, + uint32_t interval, void *user_data); + int (*set_volume) (struct avrcp *session, uint8_t transaction, + uint8_t volume, void *user_data); + int (*set_addressed) (struct avrcp *session, uint8_t transaction, + uint16_t id, void *user_data); + int (*get_folder_items) (struct avrcp *session, uint8_t transaction, + uint8_t scope, uint32_t start, + uint32_t end, uint16_t number, + uint32_t *attrs, void *user_data); + int (*change_path) (struct avrcp *session, uint8_t transaction, + uint16_t counter, uint8_t direction, + uint64_t uid, void *user_data); + int (*get_item_attributes) (struct avrcp *session, uint8_t transaction, + uint8_t scope, uint64_t uid, + uint16_t counter, uint8_t number, + uint32_t *attrs, void *user_data); + int (*play_item) (struct avrcp *session, uint8_t transaction, + uint8_t scope, uint64_t uid, + uint16_t counter, void *user_data); + int (*search) (struct avrcp *session, uint8_t transaction, + const char *string, void *user_data); + int (*add_to_now_playing) (struct avrcp *session, uint8_t transaction, + uint8_t scope, uint64_t uid, + uint16_t counter, void *user_data); +}; + +struct avrcp_control_cfm { + void (*get_capabilities) (struct avrcp *session, int err, + uint8_t number, uint8_t *params, + void *user_data); + void (*list_attributes) (struct avrcp *session, int err, + uint8_t number, uint8_t *attrs, + void *user_data); + void (*get_attribute_text) (struct avrcp *session, int err, + uint8_t number, uint8_t *attrs, + char **text, void *user_data); + void (*list_values) (struct avrcp *session, int err, + uint8_t number, uint8_t *values, + void *user_data); + void (*get_value_text) (struct avrcp *session, int err, + uint8_t number, uint8_t *values, + char **text, void *user_data); + void (*get_value) (struct avrcp *session, int err, + uint8_t number, uint8_t *attrs, + uint8_t *values, void *user_data); + void (*set_value) (struct avrcp *session, int err, void *user_data); + void (*get_play_status) (struct avrcp *session, int err, + uint8_t status, uint32_t position, + uint32_t duration, void *user_data); + void (*get_element_attributes) (struct avrcp *session, int err, + uint8_t number, uint32_t *attrs, + char **text, void *user_data); + bool (*register_notification) (struct avrcp *session, int err, + uint8_t code, uint8_t event, + uint8_t *params, void *user_data); + void (*set_volume) (struct avrcp *session, int err, uint8_t volume, + void *user_data); + void (*set_addressed) (struct avrcp *session, int err, + void *user_data); + void (*set_browsed) (struct avrcp *session, int err, + uint16_t counter, uint32_t items, + char *path, void *user_data); + void (*get_folder_items) (struct avrcp *session, int err, + uint16_t counter, uint16_t number, + uint8_t *params, void *user_data); + void (*change_path) (struct avrcp *session, int err, + uint32_t items, void *user_data); + void (*get_item_attributes) (struct avrcp *session, int err, + uint8_t number, uint32_t *attrs, + char **text, void *user_data); + void (*play_item) (struct avrcp *session, int err, void *user_data); + void (*search) (struct avrcp *session, int err, uint16_t counter, + uint32_t items, void *user_data); + void (*add_to_now_playing) (struct avrcp *session, int err, + void *user_data); +}; + +struct avrcp_passthrough_handler { + uint8_t op; + bool (*func) (struct avrcp *session, bool pressed, void *user_data); +}; + +typedef void (*avrcp_destroy_cb_t) (void *user_data); + +struct avrcp *avrcp_new(int fd, size_t imtu, size_t omtu, uint16_t version); +void avrcp_shutdown(struct avrcp *session); +void avrcp_set_destroy_cb(struct avrcp *session, avrcp_destroy_cb_t cb, + void *user_data); +int avrcp_connect_browsing(struct avrcp *session, int fd, size_t imtu, + size_t omtu); + +void avrcp_register_player(struct avrcp *session, + const struct avrcp_control_ind *ind, + const struct avrcp_control_cfm *cfm, + void *user_data); +void avrcp_set_passthrough_handlers(struct avrcp *session, + const struct avrcp_passthrough_handler *handlers, + void *user_data); +int avrcp_init_uinput(struct avrcp *session, const char *name, + const char *address); +int avrcp_send(struct avrcp *session, uint8_t transaction, uint8_t code, + uint8_t subunit, uint8_t pdu_id, + const struct iovec *iov, int iov_cnt); +int avrcp_get_capabilities(struct avrcp *session, uint8_t param); +int avrcp_register_notification(struct avrcp *session, uint8_t event, + uint32_t interval); +int avrcp_list_player_attributes(struct avrcp *session); +int avrcp_get_player_attribute_text(struct avrcp *session, uint8_t number, + uint8_t *attrs); +int avrcp_list_player_values(struct avrcp *session, uint8_t attr); +int avrcp_get_player_value_text(struct avrcp *session, uint8_t attr, + uint8_t number, uint8_t *values); +int avrcp_set_player_value(struct avrcp *session, uint8_t number, + uint8_t *attrs, uint8_t *values); +int avrcp_get_current_player_value(struct avrcp *session, uint8_t number, + uint8_t *attrs); +int avrcp_get_play_status(struct avrcp *session); +int avrcp_set_volume(struct avrcp *session, uint8_t volume); +int avrcp_get_element_attributes(struct avrcp *session); +int avrcp_set_addressed_player(struct avrcp *session, uint16_t player_id); +int avrcp_set_browsed_player(struct avrcp *session, uint16_t player_id); +int avrcp_get_folder_items(struct avrcp *session, uint8_t scope, + uint32_t start, uint32_t end, uint8_t number, + uint32_t *attrs); +int avrcp_change_path(struct avrcp *session, uint8_t direction, uint64_t uid, + uint16_t counter); +int avrcp_get_item_attributes(struct avrcp *session, uint8_t scope, + uint64_t uid, uint16_t counter, uint8_t number, + uint32_t *attrs); +int avrcp_play_item(struct avrcp *session, uint8_t scope, uint64_t uid, + uint16_t counter); +int avrcp_search(struct avrcp *session, const char *string); +int avrcp_add_to_now_playing(struct avrcp *session, uint8_t scope, uint64_t uid, + uint16_t counter); + +int avrcp_get_capabilities_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *events); +int avrcp_list_player_attributes_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *attrs); +int avrcp_get_player_attribute_text_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *attrs, const char **text); +int avrcp_list_player_values_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint8_t *values); +int avrcp_get_play_status_rsp(struct avrcp *session, uint8_t transaction, + uint32_t position, uint32_t duration, + uint8_t status); +int avrcp_get_player_values_text_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *values, const char **text); +int avrcp_get_current_player_value_rsp(struct avrcp *session, + uint8_t transaction, uint8_t number, + uint8_t *attrs, uint8_t *values); +int avrcp_set_player_value_rsp(struct avrcp *session, uint8_t transaction); +int avrcp_get_element_attrs_rsp(struct avrcp *session, uint8_t transaction, + uint8_t *params, size_t params_len); +int avrcp_register_notification_rsp(struct avrcp *session, uint8_t transaction, + uint8_t code, uint8_t *params, + size_t params_len); +int avrcp_set_volume_rsp(struct avrcp *session, uint8_t transaction, + uint8_t volume); +int avrcp_set_addressed_player_rsp(struct avrcp *session, uint8_t transaction, + uint8_t status); +int avrcp_get_folder_items_rsp(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint8_t number, + uint8_t *type, uint16_t *len, + uint8_t **params); +int avrcp_change_path_rsp(struct avrcp *session, uint8_t transaction, + uint32_t items); +int avrcp_get_item_attributes_rsp(struct avrcp *session, uint8_t transaction, + uint8_t number, uint32_t *attrs, + const char **text); +int avrcp_play_item_rsp(struct avrcp *session, uint8_t transaction); +int avrcp_search_rsp(struct avrcp *session, uint8_t transaction, + uint16_t counter, uint32_t items); +int avrcp_add_to_now_playing_rsp(struct avrcp *session, uint8_t transaction); + +int avrcp_send_passthrough(struct avrcp *session, uint32_t vendor, uint8_t op); diff -Nru bluez-4.101/android/bas.c bluez-5.23/android/bas.c --- bluez-4.101/android/bas.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/bas.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,217 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can rebastribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is bastributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "src/log.h" + +#include "lib/uuid.h" +#include "src/shared/util.h" + +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" + +#include "android/bas.h" + +#define ATT_NOTIFICATION_HEADER_SIZE 3 + +struct bt_bas { + int ref_count; + GAttrib *attrib; + struct gatt_primary *primary; + uint16_t handle; + uint16_t ccc_handle; + guint id; +}; + +static void bas_free(struct bt_bas *bas) +{ + bt_bas_detach(bas); + + g_free(bas->primary); + g_free(bas); +} + +struct bt_bas *bt_bas_new(void *primary) +{ + struct bt_bas *bas; + + bas = g_try_new0(struct bt_bas, 1); + if (!bas) + return NULL; + + if (primary) + bas->primary = g_memdup(primary, sizeof(*bas->primary)); + + return bt_bas_ref(bas); +} + +struct bt_bas *bt_bas_ref(struct bt_bas *bas) +{ + if (!bas) + return NULL; + + __sync_fetch_and_add(&bas->ref_count, 1); + + return bas; +} + +void bt_bas_unref(struct bt_bas *bas) +{ + if (!bas) + return; + + if (__sync_sub_and_fetch(&bas->ref_count, 1)) + return; + + bas_free(bas); +} + +static void value_cb(const guint8 *pdu, guint16 len, gpointer user_data) +{ + DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]); +} + +static void ccc_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct bt_bas *bas = user_data; + + if (status != 0) { + error("Write Scan Refresh CCC failed: %s", + att_ecode2str(status)); + return; + } + + DBG("Battery Level: notification enabled"); + + bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY, + bas->handle, value_cb, bas, NULL); +} + +static void write_ccc(GAttrib *attrib, uint16_t handle, void *user_data) +{ + uint8_t value[2]; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + + gatt_write_char(attrib, handle, value, sizeof(value), ccc_written_cb, + user_data); +} + + +static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct bt_bas *bas = user_data; + + if (status != 0) { + error("Error reading CCC value: %s", att_ecode2str(status)); + return; + } + + write_ccc(bas->attrib, bas->ccc_handle, bas); +} + +static void discover_descriptor_cb(uint8_t status, GSList *descs, + void *user_data) +{ + struct bt_bas *bas = user_data; + struct gatt_desc *desc; + + if (status != 0) { + error("Discover descriptors failed: %s", att_ecode2str(status)); + return; + } + + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; + bas->ccc_handle = desc->handle; + + gatt_read_char(bas->attrib, desc->handle, ccc_read_cb, bas); +} + +static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct bt_bas *bas = user_data; + struct gatt_char *chr; + uint16_t start, end; + bt_uuid_t uuid; + + if (status) { + error("Battery: %s", att_ecode2str(status)); + return; + } + + chr = chars->data; + bas->handle = chr->value_handle; + + DBG("Battery handle: 0x%04x", bas->handle); + + start = chr->value_handle + 1; + end = bas->primary->range.end; + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + gatt_discover_desc(bas->attrib, start, end, &uuid, + discover_descriptor_cb, bas); +} + +bool bt_bas_attach(struct bt_bas *bas, void *attrib) +{ + if (!bas || bas->attrib || !bas->primary) + return false; + + bas->attrib = g_attrib_ref(attrib); + + if (bas->handle > 0) + return true; + + gatt_discover_char(bas->attrib, bas->primary->range.start, + bas->primary->range.end, NULL, + bas_discovered_cb, bas); + + return true; +} + +void bt_bas_detach(struct bt_bas *bas) +{ + if (!bas || !bas->attrib) + return; + + if (bas->id > 0) { + g_attrib_unregister(bas->attrib, bas->id); + bas->id = 0; + } + + g_attrib_unref(bas->attrib); + bas->attrib = NULL; +} diff -Nru bluez-4.101/android/bas.h bluez-5.23/android/bas.h --- bluez-4.101/android/bas.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/bas.h 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can rebastribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is bastributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct bt_bas; + +struct bt_bas *bt_bas_new(void *primary); + +struct bt_bas *bt_bas_ref(struct bt_bas *bas); +void bt_bas_unref(struct bt_bas *bas); + +bool bt_bas_attach(struct bt_bas *bas, void *gatt); +void bt_bas_detach(struct bt_bas *bas); diff -Nru bluez-4.101/android/bluetooth.c bluez-5.23/android/bluetooth.c --- bluez-4.101/android/bluetooth.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/bluetooth.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,5280 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/mgmt.h" +#include "lib/uuid.h" +#include "src/shared/util.h" +#include "src/shared/mgmt.h" +#include "src/shared/queue.h" +#include "src/eir.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/sdp-client.h" +#include "src/sdpd.h" +#include "src/log.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "utils.h" +#include "bluetooth.h" + +/* + * bits in bitmask as defined in table 6.3 of Multi Profile Specification + * HFP AG + A2DP SRC + AVRCP TG + PAN (NAP/PANU) + PBAP PSE + */ +#define MPS_DEFAULT_MPSD ((1ULL << 0) | (1ULL << 2) | (1ULL << 4) | \ + (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \ + (1ULL << 12) | (1ULL << 26) | (1ULL << 28) | \ + (1ULL << 30) | (1ULL << 32) | (1ULL << 34) | \ + (1ULL << 36)) + +/* + * bits in bitmask as defined in table 6.4 of Multi Profile Specification + * HFP AG + A2DP SRC + AVRCP TG + PAN (NAP/PANU) + PBAP PSE + */ +#define MPS_DEFAULT_MPMD ((1ULL << 1) | (1ULL << 3) | (1ULL << 5) | \ + (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \ + (1ULL << 12) | (1ULL << 15) | (1ULL << 18)) + +/* + * bits in bitmask as defined in table 6.5 of Multi Profile Specification + * Note that in this table spec starts bit positions from 1 (bit 0 unused?) + */ +#define MPS_DEFAULT_DEPS ((1 << 1) | (1 << 2) | (1 << 3)) + +/* MPSD bit dependent on HFP AG support */ +#define MPS_MPSD_HFP_AG_DEP ((1ULL << 0) | (1ULL << 2) | (1ULL << 4) | \ + (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \ + (1ULL << 12) | (1ULL << 14) | (1ULL << 16) | \ + (1ULL << 18) | (1ULL << 26) | (1ULL << 28) | \ + (1ULL << 30)) + +/* MPMD bit dependent on HFP AG support */ +#define MPS_MPMD_HFP_AG_DEP (1ULL << 6) + +#define DEFAULT_ADAPTER_NAME "BlueZ for Android" + +#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode" + +#define SETTINGS_FILE ANDROID_STORAGEDIR"/settings" +#define DEVICES_FILE ANDROID_STORAGEDIR"/devices" +#define CACHE_FILE ANDROID_STORAGEDIR"/cache" + +#define DEVICE_ID_SOURCE 0x0002 /* USB */ +#define DEVICE_ID_VENDOR 0x1d6b /* Linux Foundation */ +#define DEVICE_ID_PRODUCT 0x0247 /* BlueZ for Android */ + +#define ADAPTER_MAJOR_CLASS 0x02 /* Phone */ +#define ADAPTER_MINOR_CLASS 0x03 /* Smartphone */ + +/* Default to DisplayYesNo */ +#define DEFAULT_IO_CAPABILITY 0x01 + +/* Default discoverable timeout 120sec as in Android */ +#define DEFAULT_DISCOVERABLE_TIMEOUT 120 + +#define DEVICES_CACHE_MAX 300 + +#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \ + + sizeof(struct hal_property)) + +#define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \ + + sizeof(struct hal_property)) + +#define SCAN_TYPE_NONE 0 +#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR) +#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM)) +#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE) + +#define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC) + +struct device { + bdaddr_t bdaddr; + uint8_t bdaddr_type; + + bdaddr_t rpa; + uint8_t rpa_type; + + bool le; + bool bredr; + + bool pairing; + + bool bredr_paired; + bool bredr_bonded; + bool le_paired; + bool le_bonded; + + bool in_white_list; + + char *name; + char *friendly_name; + + uint32_t class; + int32_t rssi; + + time_t bredr_seen; + time_t le_seen; + + GSList *uuids; + + bool found; /* if device is found in current discovery session */ + unsigned int confirm_id; /* mgtm command id if command pending */ + + bool valid_remote_csrk; + uint8_t remote_csrk[16]; + uint32_t remote_sign_cnt; + + bool valid_local_csrk; + uint8_t local_csrk[16]; + uint32_t local_sign_cnt; + uint16_t gatt_ccc; +}; + +struct browse_req { + bdaddr_t bdaddr; + GSList *uuids; + int search_uuid; + int reconnect_attempt; +}; + +static struct { + uint16_t index; + + bdaddr_t bdaddr; + uint32_t dev_class; + + char *name; + + uint32_t current_settings; + uint32_t supported_settings; + + bool le_scanning; + uint8_t cur_discovery_type; + uint8_t exp_discovery_type; + uint32_t discoverable_timeout; + + GSList *uuids; +} adapter = { + .index = MGMT_INDEX_NONE, + .dev_class = 0, + .name = NULL, + .current_settings = 0, + .supported_settings = 0, + .cur_discovery_type = SCAN_TYPE_NONE, + .exp_discovery_type = SCAN_TYPE_NONE, + .discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT, + .uuids = NULL, +}; + +static const uint16_t uuid_list[] = { + L2CAP_UUID, + PNP_INFO_SVCLASS_ID, + PUBLIC_BROWSE_GROUP, + 0 +}; + +static uint16_t option_index = MGMT_INDEX_NONE; +static struct mgmt *mgmt_if = NULL; + +static GSList *bonded_devices = NULL; +static GSList *cached_devices = NULL; + +static bt_le_device_found gatt_device_found_cb = NULL; +static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL; + +/* This list contains addresses which are asked for records */ +static GSList *browse_reqs; + +static struct ipc *hal_ipc = NULL; + +static bool kernel_conn_control = false; + +static struct queue *unpaired_cb_list = NULL; + +static void get_device_android_addr(struct device *dev, uint8_t *addr) +{ + /* + * If RPA is set it means that IRK was received and ID address is being + * used. Android Framework is still using old RPA and it needs to be + * used in notifications. + */ + if (bacmp(&dev->rpa, BDADDR_ANY)) + bdaddr2android(&dev->rpa, addr); + else + bdaddr2android(&dev->bdaddr, addr); +} + +static void mgmt_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + info("%s%s", prefix, str); +} + +static void store_adapter_config(void) +{ + GKeyFile *key_file; + gsize length = 0; + char addr[18]; + char *data; + + key_file = g_key_file_new(); + + g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL); + + ba2str(&adapter.bdaddr, addr); + + g_key_file_set_string(key_file, "General", "Address", addr); + g_key_file_set_string(key_file, "General", "Name", adapter.name); + g_key_file_set_integer(key_file, "General", "DiscoverableTimeout", + adapter.discoverable_timeout); + + data = g_key_file_to_data(key_file, &length, NULL); + + g_file_set_contents(SETTINGS_FILE, data, length, NULL); + + g_free(data); + g_key_file_free(key_file); +} + +static void load_adapter_config(void) +{ + GError *gerr = NULL; + GKeyFile *key_file; + char *str; + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL); + + str = g_key_file_get_string(key_file, "General", "Address", NULL); + if (!str) { + g_key_file_free(key_file); + return; + } + + str2ba(str, &adapter.bdaddr); + g_free(str); + + adapter.name = g_key_file_get_string(key_file, "General", "Name", NULL); + + adapter.discoverable_timeout = g_key_file_get_integer(key_file, + "General", "DiscoverableTimeout", &gerr); + if (gerr) { + adapter.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT; + g_clear_error(&gerr); + } + + g_key_file_free(key_file); +} + +static void store_device_info(struct device *dev, const char *path) +{ + GKeyFile *key_file; + char addr[18]; + gsize length = 0; + char **uuids = NULL; + char *str; + + ba2str(&dev->bdaddr, addr); + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, path, 0, NULL); + + g_key_file_set_boolean(key_file, addr, "BREDR", dev->bredr); + + if (dev->le) + g_key_file_set_integer(key_file, addr, "AddressType", + dev->bdaddr_type); + + g_key_file_set_string(key_file, addr, "Name", dev->name); + + if (dev->friendly_name) + g_key_file_set_string(key_file, addr, "FriendlyName", + dev->friendly_name); + else + g_key_file_remove_key(key_file, addr, "FriendlyName", NULL); + + if (dev->class) + g_key_file_set_integer(key_file, addr, "Class", dev->class); + else + g_key_file_remove_key(key_file, addr, "Class", NULL); + + if (dev->bredr_seen > dev->le_seen) + g_key_file_set_integer(key_file, addr, "Timestamp", + dev->bredr_seen); + else + g_key_file_set_integer(key_file, addr, "Timestamp", + dev->le_seen); + + if (dev->uuids) { + GSList *l; + int i; + + uuids = g_new0(char *, g_slist_length(dev->uuids) + 1); + + for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) { + int j; + uint8_t *u = l->data; + char *uuid_str = g_malloc0(33); + + for (j = 0; j < 16; j++) + sprintf(uuid_str + (j * 2), "%2.2X", u[j]); + + uuids[i] = uuid_str; + } + + g_key_file_set_string_list(key_file, addr, "Services", + (const char **)uuids, i); + } else { + g_key_file_remove_key(key_file, addr, "Services", NULL); + } + + str = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(path, str, length, NULL); + g_free(str); + + g_key_file_free(key_file); + g_strfreev(uuids); +} + +static void remove_device_info(struct device *dev, const char *path) +{ + GKeyFile *key_file; + gsize length = 0; + char addr[18]; + char *str; + + ba2str(&dev->bdaddr, addr); + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, path, 0, NULL); + + g_key_file_remove_group(key_file, addr, NULL); + + str = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(path, str, length, NULL); + g_free(str); + + g_key_file_free(key_file); +} + +static int device_match(gconstpointer a, gconstpointer b) +{ + const struct device *dev = a; + const bdaddr_t *bdaddr = b; + + /* Android is using RPA even if IRK was received and ID addr resolved */ + if (!bacmp(&dev->rpa, bdaddr)) + return 0; + + return bacmp(&dev->bdaddr, bdaddr); +} + +static struct device *find_device(const bdaddr_t *bdaddr) +{ + GSList *l; + + l = g_slist_find_custom(bonded_devices, bdaddr, device_match); + if (l) + return l->data; + + l = g_slist_find_custom(cached_devices, bdaddr, device_match); + if (l) + return l->data; + + return NULL; +} + +static void free_device(struct device *dev) +{ + if (dev->confirm_id) + mgmt_cancel(mgmt_if, dev->confirm_id); + + g_free(dev->name); + g_free(dev->friendly_name); + g_slist_free_full(dev->uuids, g_free); + g_free(dev); +} + +static void cache_device(struct device *new_dev) +{ + struct device *dev; + GSList *l; + + l = g_slist_find(cached_devices, new_dev); + if (l) { + cached_devices = g_slist_remove(cached_devices, new_dev); + goto cache; + } + + if (g_slist_length(cached_devices) < DEVICES_CACHE_MAX) + goto cache; + + l = g_slist_last(cached_devices); + dev = l->data; + + cached_devices = g_slist_remove(cached_devices, dev); + remove_device_info(dev, CACHE_FILE); + free_device(dev); + +cache: + cached_devices = g_slist_prepend(cached_devices, new_dev); + store_device_info(new_dev, CACHE_FILE); +} + +static struct device *create_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type) +{ + struct device *dev; + char addr[18]; + + ba2str(bdaddr, addr); + DBG("%s", addr); + + dev = g_new0(struct device, 1); + + bacpy(&dev->bdaddr, bdaddr); + + if (bdaddr_type == BDADDR_BREDR) { + dev->bredr = true; + dev->bredr_seen = time(NULL); + } else { + dev->le = true; + dev->bdaddr_type = bdaddr_type; + dev->le_seen = time(NULL); + } + + /* + * Use address for name, will be change if one is present + * eg. in EIR or set by set_property. + */ + dev->name = g_strdup(addr); + + return dev; +} + +static struct device *get_device(const bdaddr_t *bdaddr, uint8_t type) +{ + struct device *dev; + + dev = find_device(bdaddr); + if (dev) + return dev; + + dev = create_device(bdaddr, type); + + cache_device(dev); + + return dev; +} + +static struct device *find_device_android(const uint8_t *addr) +{ + bdaddr_t bdaddr; + + android2bdaddr(addr, &bdaddr); + + return find_device(&bdaddr); +} + +static struct device *get_device_android(const uint8_t *addr) +{ + bdaddr_t bdaddr; + + android2bdaddr(addr, &bdaddr); + + return get_device(&bdaddr, BDADDR_BREDR); +} + +static void send_adapter_property(uint8_t type, uint16_t len, const void *val) +{ + uint8_t buf[BASELEN_PROP_CHANGED + len]; + struct hal_ev_adapter_props_changed *ev = (void *) buf; + + ev->status = HAL_STATUS_SUCCESS; + ev->num_props = 1; + ev->props[0].type = type; + ev->props[0].len = len; + memcpy(ev->props[0].val, val, len); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf); +} + +static void adapter_name_changed(const uint8_t *name) +{ + /* Android expects string value without NULL terminator */ + send_adapter_property(HAL_PROP_ADAPTER_NAME, + strlen((const char *) name), name); +} + +static void adapter_set_name(const uint8_t *name) +{ + if (!g_strcmp0(adapter.name, (const char *) name)) + return; + + DBG("%s", name); + + g_free(adapter.name); + adapter.name = g_strdup((const char *) name); + + store_adapter_config(); + + adapter_name_changed(name); +} + +static void mgmt_local_name_changed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_cp_set_local_name *rp = param; + + if (length < sizeof(*rp)) { + error("Wrong size of local name changed parameters"); + return; + } + + adapter_set_name(rp->name); + + /* TODO Update services if needed */ +} + +static void powered_changed(void) +{ + struct hal_ev_adapter_state_changed ev; + + ev.state = (adapter.current_settings & MGMT_SETTING_POWERED) ? + HAL_POWER_ON : HAL_POWER_OFF; + + DBG("%u", ev.state); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev); +} + +static uint8_t settings2scan_mode(void) +{ + bool connectable, discoverable; + + connectable = adapter.current_settings & MGMT_SETTING_CONNECTABLE; + discoverable = adapter.current_settings & MGMT_SETTING_DISCOVERABLE; + + if (connectable && discoverable) + return HAL_ADAPTER_SCAN_MODE_CONN_DISC; + + if (connectable) + return HAL_ADAPTER_SCAN_MODE_CONN; + + return HAL_ADAPTER_SCAN_MODE_NONE; +} + +static void scan_mode_changed(void) +{ + uint8_t mode; + + mode = settings2scan_mode(); + + DBG("mode %u", mode); + + send_adapter_property(HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode); +} + +static void adapter_class_changed(void) +{ + send_adapter_property(HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class), + &adapter.dev_class); +} + +static void settings_changed(uint32_t settings) +{ + uint32_t changed_mask; + uint32_t scan_mode_mask; + + changed_mask = adapter.current_settings ^ settings; + + adapter.current_settings = settings; + + DBG("0x%08x", changed_mask); + + if (changed_mask & MGMT_SETTING_POWERED) + powered_changed(); + + scan_mode_mask = MGMT_SETTING_CONNECTABLE | + MGMT_SETTING_DISCOVERABLE; + + /* + * Only when powered, the connectable and discoverable + * state changes should be communicated. + */ + if (adapter.current_settings & MGMT_SETTING_POWERED) + if (changed_mask & scan_mode_mask) + scan_mode_changed(); +} + +static void new_settings_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + uint32_t settings; + + if (length < sizeof(settings)) { + error("Wrong size of new settings parameters"); + return; + } + + settings = get_le32(param); + + DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings, + settings); + + if (settings == adapter.current_settings) + return; + + settings_changed(settings); +} + +static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_cod *rp = param; + uint32_t dev_class; + + if (length < sizeof(*rp)) { + error("Wrong size of class of device changed parameters"); + return; + } + + dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16); + + if (dev_class == adapter.dev_class) + return; + + DBG("Class: 0x%06x", dev_class); + + adapter.dev_class = dev_class; + + adapter_class_changed(); + + /* TODO: Gatt attrib set*/ +} + +void bt_store_gatt_ccc(const bdaddr_t *dst, uint16_t value) +{ + struct device *dev; + GKeyFile *key_file; + gsize length = 0; + char addr[18]; + char *data; + + dev = find_device(dst); + if (!dev) + return; + + key_file = g_key_file_new(); + + if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { + g_key_file_free(key_file); + return; + } + + ba2str(&dev->bdaddr, addr); + + DBG("%s Gatt CCC %d", addr, value); + + g_key_file_set_integer(key_file, addr, "GattCCC", value); + + data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(DEVICES_FILE, data, length, NULL); + g_free(data); + + g_key_file_free(key_file); + + dev->gatt_ccc = value; +} + +uint16_t bt_get_gatt_ccc(const bdaddr_t *addr) +{ + struct device *dev; + + dev = find_device(addr); + if (!dev) + return 0; + + return dev->gatt_ccc; +} + +static void store_link_key(const bdaddr_t *dst, const uint8_t *key, + uint8_t type, uint8_t pin_length) +{ + GKeyFile *key_file; + char key_str[33]; + gsize length = 0; + char addr[18]; + char *data; + int i; + + key_file = g_key_file_new(); + + if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { + g_key_file_free(key_file); + return; + } + + ba2str(dst, addr); + + DBG("%s type %u pin_len %u", addr, type, pin_length); + + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", key[i]); + + g_key_file_set_string(key_file, addr, "LinkKey", key_str); + g_key_file_set_integer(key_file, addr, "LinkKeyType", type); + g_key_file_set_integer(key_file, addr, "LinkKeyPinLength", pin_length); + + data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(DEVICES_FILE, data, length, NULL); + g_free(data); + + g_key_file_free(key_file); +} + +static void send_bond_state_change(struct device *dev, uint8_t status, + uint8_t state) +{ + struct hal_ev_bond_state_changed ev; + + ev.status = status; + ev.state = state; + get_device_android_addr(dev, ev.bdaddr); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev); +} + +static void update_bredr_state(struct device *dev, bool pairing, bool paired, + bool bonded) +{ + if (pairing == dev->pairing && paired == dev->bredr_paired && + bonded == dev->bredr_bonded) + return; + + /* avoid unpairing device on incoming pairing request */ + if (pairing && dev->bredr_paired) + goto done; + + /* avoid unpairing device if pairing failed */ + if (!pairing && !paired && dev->pairing && dev->bredr_paired) + goto done; + + if (paired && !dev->le_paired && !dev->bredr_paired) { + cached_devices = g_slist_remove(cached_devices, dev); + bonded_devices = g_slist_prepend(bonded_devices, dev); + remove_device_info(dev, CACHE_FILE); + store_device_info(dev, DEVICES_FILE); + } else if (!paired && !dev->le_paired) { + bonded_devices = g_slist_remove(bonded_devices, dev); + remove_device_info(dev, DEVICES_FILE); + cache_device(dev); + } + + dev->bredr_paired = paired; + + if (dev->bredr_paired) + dev->bredr_bonded = dev->bredr_bonded || bonded; + else + dev->bredr_bonded = false; + +done: + dev->pairing = pairing; +} + +static void update_le_state(struct device *dev, bool pairing, bool paired, + bool bonded) +{ + if (pairing == dev->pairing && paired == dev->le_paired && + bonded == dev->le_bonded) + return; + + /* avoid unpairing device on incoming pairing request */ + if (pairing && dev->le_paired) + goto done; + + /* avoid unpairing device if pairing failed */ + if (!pairing && !paired && dev->pairing && dev->le_paired) + goto done; + + if (paired && !dev->bredr_paired && !dev->le_paired) { + cached_devices = g_slist_remove(cached_devices, dev); + bonded_devices = g_slist_prepend(bonded_devices, dev); + remove_device_info(dev, CACHE_FILE); + store_device_info(dev, DEVICES_FILE); + } else if (!paired && !dev->bredr_paired) { + bonded_devices = g_slist_remove(bonded_devices, dev); + remove_device_info(dev, DEVICES_FILE); + dev->valid_local_csrk = false; + dev->valid_remote_csrk = false; + dev->local_sign_cnt = 0; + dev->remote_sign_cnt = 0; + memset(dev->local_csrk, 0, sizeof(dev->local_csrk)); + memset(dev->remote_csrk, 0, sizeof(dev->remote_csrk)); + cache_device(dev); + } + + dev->le_paired = paired; + + if (dev->le_paired) + dev->le_bonded = dev->le_bonded || bonded; + else + dev->le_bonded = false; + +done: + dev->pairing = pairing; +} + +static uint8_t device_bond_state(struct device *dev) +{ + if (dev->pairing) + return HAL_BOND_STATE_BONDING; + + /* + * We are checking for paired here instead of bonded as HAL API is + * using BOND state also if there was no bonding pairing. + */ + if (dev->bredr_paired || dev->le_paired) + return HAL_BOND_STATE_BONDED; + + return HAL_BOND_STATE_NONE; +} + +static void update_bond_state(struct device *dev, uint8_t status, + uint8_t old_bond, uint8_t new_bond) +{ + if (old_bond == new_bond) + return; + + /* + * When internal bond state changes from bond to non-bond or other way, + * BfA needs to send bonding state to Android in the middle. Otherwise + * Android will not handle it correctly + */ + if ((old_bond == HAL_BOND_STATE_NONE && + new_bond == HAL_BOND_STATE_BONDED) || + (old_bond == HAL_BOND_STATE_BONDED && + new_bond == HAL_BOND_STATE_NONE)) + send_bond_state_change(dev, HAL_STATUS_SUCCESS, + HAL_BOND_STATE_BONDING); + + send_bond_state_change(dev, status, new_bond); + +} + +static void update_device_state(struct device *dev, uint8_t addr_type, + uint8_t status, bool pairing, bool paired, + bool bonded) +{ + uint8_t old_bond, new_bond; + + old_bond = device_bond_state(dev); + + if (addr_type == BDADDR_BREDR) + update_bredr_state(dev, pairing, paired, bonded); + else + update_le_state(dev, pairing, paired, bonded); + + new_bond = device_bond_state(dev); + + update_bond_state(dev, status, old_bond, new_bond); +} + +static void send_device_property(struct device *dev, uint8_t type, + uint16_t len, const void *val) +{ + uint8_t buf[BASELEN_REMOTE_DEV_PROP + len]; + struct hal_ev_remote_device_props *ev = (void *) buf; + + ev->status = HAL_STATUS_SUCCESS; + get_device_android_addr(dev, ev->bdaddr); + ev->num_props = 1; + ev->props[0].type = type; + ev->props[0].len = len; + memcpy(ev->props[0].val, val, len); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf); +} + +static void send_device_uuids_notif(struct device *dev) +{ + uint8_t buf[sizeof(uint128_t) * g_slist_length(dev->uuids)]; + uint8_t *ptr = buf; + GSList *l; + + for (l = dev->uuids; l; l = g_slist_next(l)) { + memcpy(ptr, l->data, sizeof(uint128_t)); + ptr += sizeof(uint128_t); + } + + send_device_property(dev, HAL_PROP_DEVICE_UUIDS, sizeof(buf), buf); +} + +static void set_device_uuids(struct device *dev, GSList *uuids) +{ + g_slist_free_full(dev->uuids, g_free); + dev->uuids = uuids; + + if (dev->le_paired || dev->bredr_paired) + store_device_info(dev, DEVICES_FILE); + else + store_device_info(dev, CACHE_FILE); + + send_device_uuids_notif(dev); +} + +static void browse_req_free(struct browse_req *req) +{ + g_slist_free_full(req->uuids, g_free); + g_free(req); +} + +static int uuid_128_cmp(gconstpointer a, gconstpointer b) +{ + return memcmp(a, b, sizeof(uint128_t)); +} + +static void update_records(struct browse_req *req, sdp_list_t *recs) +{ + for (; recs; recs = recs->next) { + sdp_record_t *rec = (sdp_record_t *) recs->data; + sdp_list_t *svcclass = NULL; + uuid_t uuid128; + uuid_t *tmp; + uint8_t *new_uuid; + + if (!rec) + break; + + if (sdp_get_service_classes(rec, &svcclass) < 0) + continue; + + if (!svcclass) + continue; + + tmp = svcclass->data; + + switch (tmp->type) { + case SDP_UUID16: + sdp_uuid16_to_uuid128(&uuid128, tmp); + break; + case SDP_UUID32: + sdp_uuid32_to_uuid128(&uuid128, tmp); + break; + case SDP_UUID128: + memcpy(&uuid128, tmp, sizeof(uuid_t)); + break; + default: + sdp_list_free(svcclass, free); + continue; + } + + new_uuid = g_malloc(16);/* size of 128 bit uuid */ + memcpy(new_uuid, &uuid128.value.uuid128, + sizeof(uuid128.value.uuid128)); + + /* Check if uuid is already added */ + if (g_slist_find_custom(req->uuids, new_uuid, uuid_128_cmp)) + g_free(new_uuid); + else + req->uuids = g_slist_append(req->uuids, new_uuid); + + sdp_list_free(svcclass, free); + } +} + +static void browse_cb(sdp_list_t *recs, int err, gpointer user_data) +{ + struct browse_req *req = user_data; + struct device *dev; + uuid_t uuid; + + /* + * If we have a valid response and req->search_uuid == 2, then L2CAP + * UUID & PNP searching was successful -- we are done + */ + if (err < 0 || req->search_uuid == 2) { + if (err == -ECONNRESET && req->reconnect_attempt < 1) { + req->search_uuid--; + req->reconnect_attempt++; + } else { + goto done; + } + } + + update_records(req, recs); + + /* Search for mandatory uuids */ + if (uuid_list[req->search_uuid]) { + sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); + bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid, + browse_cb, user_data, NULL, 0); + return; + } + +done: + dev = find_device(&req->bdaddr); + if (dev) { + set_device_uuids(dev, req->uuids); + req->uuids = NULL; + } + + browse_reqs = g_slist_remove(browse_reqs, req); + browse_req_free(req); +} + +static int req_cmp(gconstpointer a, gconstpointer b) +{ + const struct browse_req *req = a; + const bdaddr_t *bdaddr = b; + + return bacmp(&req->bdaddr, bdaddr); +} + +static uint8_t browse_remote_sdp(const bdaddr_t *addr) +{ + struct browse_req *req; + uuid_t uuid; + + if (g_slist_find_custom(browse_reqs, addr, req_cmp)) + return HAL_STATUS_SUCCESS; + + req = g_new0(struct browse_req, 1); + bacpy(&req->bdaddr, addr); + sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]); + + if (bt_search_service(&adapter.bdaddr, + &req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) { + browse_req_free(req); + return HAL_STATUS_FAILED; + } + + browse_reqs = g_slist_append(browse_reqs, req); + + return HAL_STATUS_SUCCESS; +} + +static void send_remote_sdp_rec_notify(bt_uuid_t *uuid, int channel, + char *name, uint8_t name_len, + uint8_t status, bdaddr_t *bdaddr) +{ + struct hal_prop_device_service_rec *prop; + uint8_t buf[BASELEN_REMOTE_DEV_PROP + name_len + sizeof(*prop)]; + struct hal_ev_remote_device_props *ev = (void *) buf; + size_t prop_len = sizeof(*prop) + name_len; + + memset(buf, 0, sizeof(buf)); + + if (uuid && status == HAL_STATUS_SUCCESS) { + prop = (void *) &ev->props[0].val; + prop->name_len = name_len; + prop->channel = (uint16_t)channel; + memcpy(prop->name, name, name_len); + memcpy(prop->uuid, &uuid->value.u128, sizeof(prop->uuid)); + } + + ev->num_props = 1; + ev->status = status; + ev->props[0].len = prop_len; + bdaddr2android(bdaddr, ev->bdaddr); + ev->props[0].type = HAL_PROP_DEVICE_SERVICE_REC; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, + sizeof(buf), buf); +} + +static void find_remote_sdp_rec_cb(sdp_list_t *recs, int err, + gpointer user_data) +{ + bdaddr_t *addr = user_data; + uint8_t name_len; + uint8_t status; + char name_buf[256]; + int channel; + bt_uuid_t uuid; + uuid_t uuid128_sdp; + sdp_list_t *protos; + sdp_record_t *sdp_rec; + + if (err < 0) { + error("error while search remote sdp records"); + status = HAL_STATUS_FAILED; + send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr); + goto done; + } + + if (!recs) { + info("No service records found on remote"); + status = HAL_STATUS_SUCCESS; + send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr); + goto done; + } + + for ( ; recs; recs = recs->next) { + sdp_rec = recs->data; + + switch (sdp_rec->svclass.type) { + case SDP_UUID16: + sdp_uuid16_to_uuid128(&uuid128_sdp, + &sdp_rec->svclass); + break; + case SDP_UUID32: + sdp_uuid32_to_uuid128(&uuid128_sdp, + &sdp_rec->svclass); + break; + case SDP_UUID128: + break; + default: + error("wrong sdp uuid type"); + goto done; + } + + if (!sdp_get_access_protos(sdp_rec, &protos)) { + channel = sdp_get_proto_port(protos, RFCOMM_UUID); + + sdp_list_foreach(protos, + (sdp_list_func_t) sdp_list_free, + NULL); + sdp_list_free(protos, NULL); + } else + channel = -1; + + if (channel < 0) { + error("can't get channel for sdp record"); + channel = 0; + } + + if (!sdp_get_service_name(sdp_rec, name_buf, sizeof(name_buf))) + name_len = strlen(name_buf); + else + name_len = 0; + + uuid.type = BT_UUID128; + memcpy(&uuid.value.u128, uuid128_sdp.value.uuid128.data, + sizeof(uuid.value.u128)); + status = HAL_STATUS_SUCCESS; + + send_remote_sdp_rec_notify(&uuid, channel, name_buf, name_len, + status, addr); + } + +done: + g_free(addr); +} + +static uint8_t find_remote_sdp_rec(const bdaddr_t *addr, + const uint8_t *find_uuid) +{ + bdaddr_t *bdaddr; + uuid_t uuid; + + /* from android we always get full 128bit length uuid */ + sdp_uuid128_create(&uuid, find_uuid); + + bdaddr = g_new(bdaddr_t, 1); + if (!bdaddr) + return HAL_STATUS_NOMEM; + + memcpy(bdaddr, addr, sizeof(*bdaddr)); + + if (bt_search_service(&adapter.bdaddr, addr, &uuid, + find_remote_sdp_rec_cb, bdaddr, NULL, 0) < 0) { + g_free(bdaddr); + return HAL_STATUS_FAILED; + } + + return HAL_STATUS_SUCCESS; +} + +static void new_link_key_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_link_key *ev = param; + const struct mgmt_addr_info *addr = &ev->key.addr; + struct device *dev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small new link key event"); + return; + } + + ba2str(&addr->bdaddr, dst); + + DBG("new key for %s type %u pin_len %u", + dst, ev->key.type, ev->key.pin_len); + + if (ev->key.pin_len > 16) { + error("Invalid PIN length (%u) in new_key event", + ev->key.pin_len); + return; + } + + dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type); + if (!dev) + return; + + update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, + true, !!ev->store_hint); + + if (ev->store_hint) { + const struct mgmt_link_key_info *key = &ev->key; + + store_link_key(&addr->bdaddr, key->val, key->type, + key->pin_len); + } + + browse_remote_sdp(&addr->bdaddr); +} + +static uint8_t get_device_name(struct device *dev) +{ + send_device_property(dev, HAL_PROP_DEVICE_NAME, + strlen(dev->name), dev->name); + + return HAL_STATUS_SUCCESS; +} + +static void pin_code_request_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_pin_code_request *ev = param; + struct hal_ev_pin_request hal_ev; + struct device *dev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small PIN code request event"); + return; + } + + ba2str(&ev->addr.bdaddr, dst); + + dev = get_device(&ev->addr.bdaddr, BDADDR_BREDR); + + /* + * Workaround for Android Bluetooth.apk issue: send remote + * device property + */ + get_device_name(dev); + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, + false, false); + + DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure); + + /* Name already sent in remote device prop */ + memset(&hal_ev, 0, sizeof(hal_ev)); + get_device_android_addr(dev, hal_ev.bdaddr); + hal_ev.class_of_dev = dev->class; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST, + sizeof(hal_ev), &hal_ev); +} + +static void send_ssp_request(struct device *dev, uint8_t variant, + uint32_t passkey) +{ + struct hal_ev_ssp_request ev; + + memset(&ev, 0, sizeof(ev)); + + get_device_android_addr(dev, ev.bdaddr); + memcpy(ev.name, dev->name, strlen(dev->name)); + ev.class_of_dev = dev->class; + + ev.pairing_variant = variant; + ev.passkey = passkey; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST, + sizeof(ev), &ev); +} + +static void user_confirm_request_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_user_confirm_request *ev = param; + struct device *dev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small user confirm request event"); + return; + } + + ba2str(&ev->addr.bdaddr, dst); + DBG("%s confirm_hint %u", dst, ev->confirm_hint); + + dev = get_device(&ev->addr.bdaddr, ev->addr.type); + if (!dev) + return; + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, + false, false); + + if (ev->confirm_hint) + send_ssp_request(dev, HAL_SSP_VARIANT_CONSENT, 0); + else + send_ssp_request(dev, HAL_SSP_VARIANT_CONFIRM, ev->value); +} + +static void user_passkey_request_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_user_passkey_request *ev = param; + struct device *dev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small passkey request event"); + return; + } + + ba2str(&ev->addr.bdaddr, dst); + DBG("%s", dst); + + dev = get_device(&ev->addr.bdaddr, ev->addr.type); + if (!dev) + return; + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, + false, false); + + send_ssp_request(dev, HAL_SSP_VARIANT_ENTRY, 0); +} + +static void user_passkey_notify_callback(uint16_t index, uint16_t length, + const void *param, + void *user_data) +{ + const struct mgmt_ev_passkey_notify *ev = param; + struct device *dev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small passkey notify event"); + return; + } + + ba2str(&ev->addr.bdaddr, dst); + DBG("%s entered %u", dst, ev->entered); + + /* HAL seems to not support entered characters */ + if (ev->entered) + return; + + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true, + false, false); + + send_ssp_request(dev, HAL_SSP_VARIANT_NOTIF, ev->passkey); +} + +static void clear_device_found(gpointer data, gpointer user_data) +{ + struct device *dev = data; + + dev->found = false; +} + +static uint8_t get_supported_discovery_type(void) +{ + uint8_t type = SCAN_TYPE_NONE; + + if (adapter.current_settings & MGMT_SETTING_BREDR) + type |= SCAN_TYPE_BREDR; + + if (adapter.current_settings & MGMT_SETTING_LE) + type |= SCAN_TYPE_LE; + + return type; +} + +static bool start_discovery(uint8_t type) +{ + struct mgmt_cp_start_discovery cp; + + cp.type = get_supported_discovery_type() & type; + + DBG("type=0x%x", cp.type); + + if (cp.type == SCAN_TYPE_NONE) + return false; + + if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) > 0) + return true; + + error("Failed to start discovery"); + + return false; +} + +/* + * Send discovery state change event only if it is related to dual type + * discovery session (triggered by start/cancel discovery commands) + */ +static void check_discovery_state(uint8_t new_type, uint8_t old_type) +{ + struct hal_ev_discovery_state_changed ev; + + DBG("%u %u", new_type, old_type); + + if (new_type == get_supported_discovery_type()) { + g_slist_foreach(bonded_devices, clear_device_found, NULL); + g_slist_foreach(cached_devices, clear_device_found, NULL); + ev.state = HAL_DISCOVERY_STATE_STARTED; + goto done; + } + + if (old_type != get_supported_discovery_type()) + return; + + ev.state = HAL_DISCOVERY_STATE_STOPPED; + +done: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(ev), &ev); +} + +static void mgmt_discovering_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_discovering *ev = param; + uint8_t type; + + if (length < sizeof(*ev)) { + error("Too small discovering event"); + return; + } + + DBG("type %u discovering %u", ev->type, ev->discovering); + + if (!!adapter.cur_discovery_type == !!ev->discovering) + return; + + type = ev->discovering ? ev->type : SCAN_TYPE_NONE; + + check_discovery_state(type, adapter.cur_discovery_type); + + adapter.cur_discovery_type = type; + + if (ev->discovering) { + adapter.exp_discovery_type = adapter.le_scanning ? + SCAN_TYPE_LE : SCAN_TYPE_NONE; + return; + } + + /* One shot notification about discovery stopped */ + if (gatt_discovery_stopped_cb) { + gatt_discovery_stopped_cb(); + gatt_discovery_stopped_cb = NULL; + } + + type = adapter.exp_discovery_type; + adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE : + SCAN_TYPE_NONE; + + if (type != SCAN_TYPE_NONE) + start_discovery(type); +} + +static void confirm_device_name_cb(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_confirm_name *rp = param; + struct device *dev; + + DBG("Confirm name status: %s (0x%02x)", mgmt_errstr(status), status); + + if (length < sizeof(*rp)) { + error("Wrong size of confirm name response"); + return; + } + + dev = find_device(&rp->addr.bdaddr); + if (!dev) + return; + + dev->confirm_id = 0; +} + +static unsigned int confirm_device_name(const bdaddr_t *addr, uint8_t addr_type, + bool resolve_name) +{ + struct mgmt_cp_confirm_name cp; + unsigned int res; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, addr); + cp.addr.type = addr_type; + + if (!resolve_name) + cp.name_known = 1; + + res = mgmt_send(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index, + sizeof(cp), &cp, confirm_device_name_cb, + NULL, NULL); + if (!res) + error("Failed to send confirm name request"); + + return res; +} + +static int fill_hal_prop(void *buf, uint8_t type, uint16_t len, + const void *val) +{ + struct hal_property *prop = buf; + + prop->type = type; + prop->len = len; + + if (len) + memcpy(prop->val, val, len); + + return sizeof(*prop) + len; +} + +static uint8_t get_device_android_type(struct device *dev) +{ + if (dev->bredr && dev->le) + return HAL_TYPE_DUAL; + + if (dev->le) + return HAL_TYPE_LE; + + return HAL_TYPE_BREDR; +} + +uint8_t bt_get_device_android_type(const bdaddr_t *addr) +{ + struct device *dev; + + dev = get_device(addr, BDADDR_BREDR); + + return get_device_android_type(dev); +} + +bool bt_is_device_le(const bdaddr_t *addr) +{ + struct device *dev; + + dev = find_device(addr); + if (!dev) + return false; + + return dev->le; +} + +const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type) +{ + struct device *dev; + + dev = find_device(addr); + if (!dev) + return NULL; + + if (type) + *type = dev->bdaddr_type; + + return &dev->bdaddr; +} + +const char *bt_get_adapter_name(void) +{ + return adapter.name; +} + +bool bt_device_is_bonded(const bdaddr_t *bdaddr) +{ + if (g_slist_find_custom(bonded_devices, bdaddr, device_match)) + return true; + + return false; +} + +bool bt_device_set_uuids(const bdaddr_t *addr, GSList *uuids) +{ + struct device *dev; + + dev = find_device(addr); + if (!dev) + return false; + + set_device_uuids(dev, uuids); + + return true; +} + +bool bt_kernel_conn_control(void) +{ + return kernel_conn_control; +} + +bool bt_auto_connect_add(const bdaddr_t *addr) +{ + struct mgmt_cp_add_device cp; + struct device *dev; + + if (!kernel_conn_control) + return false; + + dev = find_device(addr); + if (!dev) + return false; + + if (dev->bdaddr_type == BDADDR_BREDR) { + DBG("auto-connection feature is not available for BR/EDR"); + return false; + } + + if (dev->in_white_list) { + DBG("Device already in white list"); + return true; + } + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, addr); + cp.addr.type = dev->bdaddr_type; + cp.action = 0x02; + + if (mgmt_send(mgmt_if, MGMT_OP_ADD_DEVICE, adapter.index, sizeof(cp), + &cp, NULL, NULL, NULL) > 0) { + dev->in_white_list = true; + return true; + } + + error("Failed to add device"); + + return false; +} + +void bt_auto_connect_remove(const bdaddr_t *addr) +{ + struct mgmt_cp_remove_device cp; + struct device *dev; + + if (!kernel_conn_control) + return; + + dev = find_device(addr); + if (!dev) + return; + + if (dev->bdaddr_type == BDADDR_BREDR) { + DBG("auto-connection feature is not available for BR/EDR"); + return; + } + + if (!dev->in_white_list) { + DBG("Device already removed from white list"); + return; + } + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, addr); + cp.addr.type = dev->bdaddr_type; + + if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) > 0) { + dev->in_white_list = false; + return; + } + + error("Failed to remove device"); +} + +static bool match_by_value(const void *data, const void *user_data) +{ + return data == user_data; +} + +bool bt_unpaired_register(bt_unpaired_device_cb cb) +{ + if (queue_find(unpaired_cb_list, match_by_value, cb)) + return false; + + return queue_push_head(unpaired_cb_list, cb); +} + +void bt_unpaired_unregister(bt_unpaired_device_cb cb) +{ + queue_remove(unpaired_cb_list, cb); +} + +static bool rssi_above_threshold(int old, int new) +{ + /* only 8 dBm or more */ + return abs(old - new) >= 8; +} + +static void update_new_device(struct device *dev, int8_t rssi, + const struct eir_data *eir) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_device_found *ev = (void *) buf; + uint8_t android_bdaddr[6]; + uint8_t android_type; + size_t size; + + memset(buf, 0, sizeof(buf)); + + if (adapter.cur_discovery_type) + dev->found = true; + + size = sizeof(*ev); + + get_device_android_addr(dev, android_bdaddr); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR, + sizeof(android_bdaddr), android_bdaddr); + ev->num_props++; + + android_type = get_device_android_type(dev); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, + sizeof(android_type), &android_type); + ev->num_props++; + + if (eir->class) + dev->class = eir->class; + + if (dev->class) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, + sizeof(dev->class), &dev->class); + ev->num_props++; + } + + if (rssi && rssi_above_threshold(dev->rssi, rssi)) + dev->rssi = rssi; + + if (dev->rssi) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, + sizeof(dev->rssi), &dev->rssi); + ev->num_props++; + } + + if (eir->name && strlen(eir->name)) { + g_free(dev->name); + dev->name = g_strdup(eir->name); + } + + if (dev->name) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, + strlen(dev->name), dev->name); + ev->num_props++; + + /* when updating name also send stored friendly name */ + if (dev->friendly_name) { + size += fill_hal_prop(buf + size, + HAL_PROP_DEVICE_FRIENDLY_NAME, + strlen(dev->friendly_name), + dev->friendly_name); + ev->num_props++; + } + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND, + size, buf); +} + +static void update_device(struct device *dev, int8_t rssi, + const struct eir_data *eir, uint8_t bdaddr_type) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_remote_device_props *ev = (void *) buf; + uint8_t old_type, new_type; + size_t size; + + memset(buf, 0, sizeof(buf)); + + size = sizeof(*ev); + + ev->status = HAL_STATUS_SUCCESS; + get_device_android_addr(dev, ev->bdaddr); + + old_type = get_device_android_type(dev); + + if (bdaddr_type == BDADDR_BREDR) { + dev->bredr = true; + } else { + dev->le = true; + dev->bdaddr_type = bdaddr_type; + } + + new_type = get_device_android_type(dev); + + if (old_type != new_type) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, + sizeof(new_type), &new_type); + ev->num_props++; + } + + if (eir->class && dev->class != eir->class) { + dev->class = eir->class; + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, + sizeof(dev->class), &dev->class); + ev->num_props++; + } + + if (rssi && rssi_above_threshold(dev->rssi, rssi)) { + dev->rssi = rssi; + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, + sizeof(dev->rssi), &dev->rssi); + ev->num_props++; + } + + if (eir->name && strlen(eir->name) && strcmp(dev->name, eir->name)) { + g_free(dev->name); + dev->name = g_strdup(eir->name); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, + strlen(dev->name), dev->name); + ev->num_props++; + + /* when updating name also send stored friendly name */ + if (dev->friendly_name) { + size += fill_hal_prop(buf + size, + HAL_PROP_DEVICE_FRIENDLY_NAME, + strlen(dev->friendly_name), + dev->friendly_name); + ev->num_props++; + } + } + + if (ev->num_props) + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, size, buf); +} + +static bool is_new_device(const struct device *dev, unsigned int flags, + uint8_t bdaddr_type) +{ + if (dev->found) + return false; + + if (dev->bredr_paired || dev->le_paired) + return false; + + if (bdaddr_type != BDADDR_BREDR && + !(flags & (EIR_LIM_DISC | EIR_GEN_DISC))) + return false; + + return true; +} + +static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type, + int8_t rssi, bool confirm, + const uint8_t *data, uint8_t data_len) +{ + struct eir_data eir; + struct device *dev; + + memset(&eir, 0, sizeof(eir)); + + eir_parse(&eir, data, data_len); + + dev = get_device(bdaddr, bdaddr_type); + + if (bdaddr_type == BDADDR_BREDR) + dev->bredr_seen = time(NULL); + else + dev->le_seen = time(NULL); + + /* + * Device found event needs to be send also for known device if this is + * new discovery session. Otherwise framework will ignore it. + */ + if (is_new_device(dev, eir.flags, bdaddr_type)) + update_new_device(dev, rssi, &eir); + else + update_device(dev, rssi, &eir, bdaddr_type); + + eir_data_free(&eir); + + /* Notify Gatt if its registered for LE events */ + if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) { + bool discoverable; + bdaddr_t *addr; + uint8_t addr_type; + + discoverable = eir.flags & (EIR_LIM_DISC | EIR_GEN_DISC); + + /* + * If RPA is set it means that IRK was received and ID address + * is being used. Android Framework is still using old RPA and + * it needs to be used also in GATT notifications. Also GATT + * HAL implementation is using RPA for devices matching. + */ + if (bacmp(&dev->rpa, BDADDR_ANY)) { + addr = &dev->rpa; + addr_type = dev->rpa_type; + } else { + addr = &dev->bdaddr; + addr_type = dev->bdaddr_type; + } + + gatt_device_found_cb(addr, addr_type, rssi, data_len, data, + discoverable, dev->le_bonded); + } + + if (!dev->bredr_paired && !dev->le_paired) + cache_device(dev); + + if (confirm) { + char addr[18]; + bool resolve_name = true; + + ba2str(bdaddr, addr); + + /* + * Don't need to confirm name if we have it already in cache + * Just check if device name is different than bdaddr + */ + if (g_strcmp0(dev->name, addr)) { + get_device_name(dev); + resolve_name = false; + } + + info("Device %s needs name confirmation (resolve_name=%d)", + addr, resolve_name); + dev->confirm_id = confirm_device_name(bdaddr, bdaddr_type, + resolve_name); + } +} + +static void mgmt_device_found_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_device_found *ev = param; + const uint8_t *eir; + uint16_t eir_len; + uint32_t flags; + bool confirm_name; + char addr[18]; + + if (length < sizeof(*ev)) { + error("Too short device found event (%u bytes)", length); + return; + } + + eir_len = btohs(ev->eir_len); + if (length != sizeof(*ev) + eir_len) { + error("Device found event size mismatch (%u != %zu)", + length, sizeof(*ev) + eir_len); + return; + } + + if (eir_len == 0) + eir = NULL; + else + eir = ev->eir; + + flags = btohl(ev->flags); + + ba2str(&ev->addr.bdaddr, addr); + DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u", + index, addr, ev->rssi, flags, eir_len); + + confirm_name = flags & MGMT_DEV_FOUND_CONFIRM_NAME; + + update_found_device(&ev->addr.bdaddr, ev->addr.type, ev->rssi, + confirm_name, eir, eir_len); +} + +static void mgmt_device_connected_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_device_connected *ev = param; + struct hal_ev_acl_state_changed hal_ev; + struct device *dev; + + if (length < sizeof(*ev)) { + error("Too short device connected event (%u bytes)", length); + return; + } + + update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false, + &ev->eir[0], btohs(ev->eir_len)); + + hal_ev.status = HAL_STATUS_SUCCESS; + hal_ev.state = HAL_ACL_STATE_CONNECTED; + + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + get_device_android_addr(dev, hal_ev.bdaddr); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev); +} + +static bool device_is_paired(struct device *dev, uint8_t addr_type) +{ + if (addr_type == BDADDR_BREDR) + return dev->bredr_paired; + + return dev->le_paired; +} + +static bool device_is_bonded(struct device *dev) +{ + return dev->bredr_bonded || dev->le_bonded; +} + +static void mgmt_device_disconnected_event(uint16_t index, uint16_t length, + const void *param, + void *user_data) +{ + const struct mgmt_ev_device_disconnected *ev = param; + struct hal_ev_acl_state_changed hal_ev; + struct device *dev; + uint8_t type = ev->addr.type; + + if (length < sizeof(*ev)) { + error("Too short device disconnected event (%u bytes)", length); + return; + } + + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + hal_ev.status = HAL_STATUS_SUCCESS; + hal_ev.state = HAL_ACL_STATE_DISCONNECTED; + get_device_android_addr(dev, hal_ev.bdaddr); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev); + + if (device_is_paired(dev, type) && !device_is_bonded(dev)) + update_device_state(dev, type, HAL_STATUS_SUCCESS, false, + false, false); +} + +static uint8_t status_mgmt2hal(uint8_t mgmt) +{ + switch (mgmt) { + case MGMT_STATUS_SUCCESS: + return HAL_STATUS_SUCCESS; + case MGMT_STATUS_NO_RESOURCES: + return HAL_STATUS_NOMEM; + case MGMT_STATUS_BUSY: + return HAL_STATUS_BUSY; + case MGMT_STATUS_NOT_SUPPORTED: + return HAL_STATUS_UNSUPPORTED; + case MGMT_STATUS_INVALID_PARAMS: + return HAL_STATUS_INVALID; + case MGMT_STATUS_AUTH_FAILED: + return HAL_STATUS_AUTH_FAILURE; + case MGMT_STATUS_NOT_CONNECTED: + return HAL_STATUS_REMOTE_DEVICE_DOWN; + default: + return HAL_STATUS_FAILED; + } +} + +static void mgmt_connect_failed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_connect_failed *ev = param; + struct device *dev; + + if (length < sizeof(*ev)) { + error("Too short connect failed event (%u bytes)", length); + return; + } + + DBG(""); + + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + /* + * In case security mode 3 pairing we will get connect failed event + * in case e.g wrong PIN code entered. Let's check if device is + * bonding, if so update bond state + */ + + if (!dev->pairing) + return; + + update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status), + false, false, false); +} + +static void mgmt_auth_failed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_auth_failed *ev = param; + struct device *dev; + + if (length < sizeof(*ev)) { + error("Too small auth failed mgmt event (%u bytes)", length); + return; + } + + DBG(""); + + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + if (!dev->pairing) + return; + + update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status), + false, false, false); +} + +static void mgmt_device_unpaired_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_device_unpaired *ev = param; + struct device *dev; + + if (length < sizeof(*ev)) { + error("Too small device unpaired event (%u bytes)", length); + return; + } + + DBG(""); + + /* TODO should device be disconnected ? */ + + dev = find_device(&ev->addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, false, + false, false); + + /* Unpaired device is removed from the white list */ + dev->in_white_list = false; +} + +static void store_ltk(const bdaddr_t *dst, uint8_t bdaddr_type, bool master, + const uint8_t *key, uint8_t key_type, uint8_t enc_size, + uint16_t ediv, uint64_t rand) +{ + const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s; + GKeyFile *key_file; + char key_str[33]; + gsize length = 0; + char addr[18]; + char *data; + int i; + + key_file = g_key_file_new(); + if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { + g_key_file_free(key_file); + return; + } + + ba2str(dst, addr); + + key_s = master ? "LongTermKey" : "SlaveLongTermKey"; + keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType"; + encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize"; + ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv"; + rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand"; + + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", key[i]); + + g_key_file_set_string(key_file, addr, key_s, key_str); + + g_key_file_set_integer(key_file, addr, keytype_s, key_type); + + g_key_file_set_integer(key_file, addr, encsize_s, enc_size); + + g_key_file_set_integer(key_file, addr, ediv_s, ediv); + + g_key_file_set_uint64(key_file, addr, rand_s, rand); + + data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(DEVICES_FILE, data, length, NULL); + g_free(data); + + g_key_file_free(key_file); +} + +static void new_long_term_key_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_long_term_key *ev = param; + struct device *dev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small long term key event (%u bytes)", length); + return; + } + + ba2str(&ev->key.addr.bdaddr, dst); + + DBG("new LTK for %s type %u enc_size %u store_hint %u", + dst, ev->key.type, ev->key.enc_size, ev->store_hint); + + dev = find_device(&ev->key.addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, + true, !!ev->store_hint); + + if (ev->store_hint) { + const struct mgmt_ltk_info *key = &ev->key; + uint16_t ediv; + uint64_t rand; + + ediv = le16_to_cpu(key->ediv); + rand = le64_to_cpu(key->rand); + + store_ltk(&key->addr.bdaddr, key->addr.type, key->master, + key->val, key->type, key->enc_size, ediv, rand); + } + + /* TODO browse services here? */ +} + +static void store_csrk(struct device *dev) +{ + GKeyFile *key_file; + char key_str[33]; + char addr[18]; + int i; + gsize length = 0; + char *data; + + ba2str(&dev->bdaddr, addr); + + key_file = g_key_file_new(); + if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { + g_key_file_free(key_file); + return; + } + + if (dev->valid_local_csrk) { + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", + dev->local_csrk[i]); + + g_key_file_set_string(key_file, addr, "LocalCSRK", key_str); + } + + if (dev->valid_remote_csrk) { + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", + dev->remote_csrk[i]); + + g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str); + } + + data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(DEVICES_FILE, data, length, NULL); + g_free(data); + + g_key_file_free(key_file); +} + +static void new_csrk_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_csrk *ev = param; + struct device *dev; + char dst[18]; + + if (length < sizeof(*ev)) { + error("Too small csrk event (%u bytes)", length); + return; + } + + ba2str(&ev->key.addr.bdaddr, dst); + dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type); + if (!dev) + return; + + switch (ev->key.master) { + case 0x00: + memcpy(dev->local_csrk, ev->key.val, 16); + dev->local_sign_cnt = 0; + dev->valid_local_csrk = true; + break; + case 0x01: + memcpy(dev->remote_csrk, ev->key.val, 16); + dev->remote_sign_cnt = 0; + dev->valid_remote_csrk = true; + break; + default: + error("Unknown CSRK key type 02%02x", ev->key.master); + return; + } + + update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, + true, !!ev->store_hint); + + if (ev->store_hint) + store_csrk(dev); +} + +static void store_irk(struct device *dev, const uint8_t *val) +{ + GKeyFile *key_file; + char key_str[33]; + char addr[18]; + int i; + gsize length = 0; + char *data; + + ba2str(&dev->bdaddr, addr); + + key_file = g_key_file_new(); + if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { + g_key_file_free(key_file); + return; + } + + for (i = 0; i < 16; i++) + sprintf(key_str + (i * 2), "%2.2X", val[i]); + + g_key_file_set_string(key_file, addr, "IdentityResolvingKey", key_str); + + data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(DEVICES_FILE, data, length, NULL); + g_free(data); + + g_key_file_free(key_file); +} + +static void new_irk_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_new_irk *ev = param; + const struct mgmt_addr_info *addr = &ev->key.addr; + struct device *dev; + char dst[18], rpa[18]; + + if (length < sizeof(*ev)) { + error("To small New Irk Event (%u bytes)", length); + return; + } + + ba2str(&ev->key.addr.bdaddr, dst); + ba2str(&ev->rpa, rpa); + + DBG("new IRK for %s, RPA %s", dst, rpa); + + if (!bacmp(&ev->rpa, BDADDR_ANY)) { + dev = get_device(&addr->bdaddr, addr->type); + if (!dev) + return; + } else { + dev = find_device(&addr->bdaddr); + + if (dev && dev->bredr_paired) { + bacpy(&dev->rpa, &addr->bdaddr); + dev->rpa_type = addr->type; + + /* TODO merge properties ie. UUIDs */ + } else { + dev = find_device(&ev->rpa); + if (!dev) + return; + + /* don't leave garbage in cache file */ + remove_device_info(dev, CACHE_FILE); + + /* + * RPA resolution is transparent for Android Framework + * ie. device is still access by RPA so it need to be + * keep. After bluetoothd restart device is advertised + * to Android with IDA and RPA is not set. + */ + bacpy(&dev->rpa, &dev->bdaddr); + dev->rpa_type = dev->bdaddr_type; + + bacpy(&dev->bdaddr, &addr->bdaddr); + dev->bdaddr_type = addr->type; + } + } + + update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false, + true, !!ev->store_hint); + + if (ev->store_hint) + store_irk(dev, ev->key.val); +} + +static void register_mgmt_handlers(void) +{ + mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index, + new_settings_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index, + mgmt_dev_class_changed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index, + mgmt_local_name_changed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index, + new_link_key_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index, + pin_code_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index, + user_confirm_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index, + user_passkey_request_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index, + user_passkey_notify_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index, + mgmt_discovering_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index, + mgmt_device_found_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index, + mgmt_device_connected_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index, + mgmt_device_disconnected_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index, + mgmt_connect_failed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index, + mgmt_auth_failed_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index, + mgmt_device_unpaired_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index, + new_long_term_key_event, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_NEW_CSRK, adapter.index, + new_csrk_callback, NULL, NULL); + + mgmt_register(mgmt_if, MGMT_EV_NEW_IRK, adapter.index, new_irk_callback, + NULL, NULL); +} + +static void load_link_keys_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + bt_bluetooth_ready cb = user_data; + int err; + + if (status) { + error("Failed to load link keys for index %u: %s (0x%02x)", + adapter.index, mgmt_errstr(status), status); + err = -EIO; + goto failed; + } + + DBG("status %u", status); + + cb(0, &adapter.bdaddr); + return; + +failed: + cb(err, NULL); +} + +static void load_link_keys(GSList *keys, bt_bluetooth_ready cb) +{ + struct mgmt_cp_load_link_keys *cp; + struct mgmt_link_key_info *key; + size_t key_count, cp_size; + unsigned int id; + + key_count = g_slist_length(keys); + + DBG("keys %zu ", key_count); + + cp_size = sizeof(*cp) + (key_count * sizeof(*key)); + + cp = g_malloc0(cp_size); + + /* + * Even if the list of stored keys is empty, it is important to + * load an empty list into the kernel. That way it is ensured + * that no old keys from a previous daemon are present. + */ + cp->key_count = htobs(key_count); + + for (key = cp->keys; keys != NULL; keys = g_slist_next(keys), key++) + memcpy(key, keys->data, sizeof(*key)); + + id = mgmt_send(mgmt_if, MGMT_OP_LOAD_LINK_KEYS, adapter.index, + cp_size, cp, load_link_keys_complete, cb, NULL); + + g_free(cp); + + if (id == 0) { + error("Failed to load link keys"); + cb(-EIO, NULL); + } +} + +static void load_ltk_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status == MGMT_STATUS_SUCCESS) + return; + + info("Failed to load LTKs: %s (0x%02x)", mgmt_errstr(status), status); +} + +static void load_ltks(GSList *ltks) +{ + struct mgmt_cp_load_long_term_keys *cp; + struct mgmt_ltk_info *ltk; + size_t ltk_count, cp_size; + GSList *l; + + ltk_count = g_slist_length(ltks); + + DBG("ltks %zu", ltk_count); + + cp_size = sizeof(*cp) + (ltk_count * sizeof(*ltk)); + + cp = g_malloc0(cp_size); + + /* + * Even if the list of stored keys is empty, it is important to load + * an empty list into the kernel. That way it is ensured that no old + * keys from a previous daemon are present. + */ + cp->key_count = htobs(ltk_count); + + for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++) + memcpy(ltk, l->data, sizeof(*ltk)); + + if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index, + cp_size, cp, load_ltk_complete, NULL, NULL) == 0) + error("Failed to load LTKs"); + + g_free(cp); +} + +static void load_irk_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status == MGMT_STATUS_SUCCESS) + return; + + info("Failed to load IRKs: %s (0x%02x)", mgmt_errstr(status), status); +} + +static void load_irks(GSList *irks) +{ + struct mgmt_cp_load_irks *cp; + struct mgmt_irk_info *irk; + size_t irk_count, cp_size; + GSList *l; + + irk_count = g_slist_length(irks); + + DBG("irks %zu", irk_count); + + cp_size = sizeof(*cp) + (irk_count * sizeof(*irk)); + + cp = g_malloc0(cp_size); + + cp->irk_count = htobs(irk_count); + + for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++) + memcpy(irk, irks->data, sizeof(*irk)); + + if (mgmt_send(mgmt_if, MGMT_OP_LOAD_IRKS, adapter.index, cp_size, cp, + load_irk_complete, NULL, NULL) == 0) + error("Failed to load IRKs"); + + g_free(cp); +} + +static uint8_t get_adapter_uuids(void) +{ + struct hal_ev_adapter_props_changed *ev; + GSList *list = adapter.uuids; + size_t uuid_count = g_slist_length(list); + size_t len = uuid_count * sizeof(uint128_t); + uint8_t buf[BASELEN_PROP_CHANGED + len]; + uint8_t *p; + + memset(buf, 0, sizeof(buf)); + ev = (void *) buf; + + ev->num_props = 1; + ev->status = HAL_STATUS_SUCCESS; + + ev->props[0].type = HAL_PROP_ADAPTER_UUIDS; + ev->props[0].len = len; + p = ev->props->val; + + for (; list; list = g_slist_next(list)) { + uuid_t *uuid = list->data; + + memcpy(p, &uuid->value.uuid128, sizeof(uint128_t)); + + p += sizeof(uint128_t); + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev); + + return HAL_STATUS_SUCCESS; +} + +static void remove_uuid_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to remove UUID: %s (0x%02x)", mgmt_errstr(status), + status); + return; + } + + mgmt_dev_class_changed_event(adapter.index, length, param, NULL); + + get_adapter_uuids(); +} + +static void remove_uuid(uuid_t *uuid) +{ + uint128_t uint128; + struct mgmt_cp_remove_uuid cp; + + ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128); + htob128(&uint128, (uint128_t *) cp.uuid); + + mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp, + remove_uuid_complete, NULL, NULL); +} + +static void add_uuid_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to add UUID: %s (0x%02x)", mgmt_errstr(status), + status); + return; + } + + mgmt_dev_class_changed_event(adapter.index, length, param, NULL); + + get_adapter_uuids(); +} + +static void add_uuid(uint8_t svc_hint, uuid_t *uuid) +{ + uint128_t uint128; + struct mgmt_cp_add_uuid cp; + + ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128); + htob128(&uint128, (uint128_t *) cp.uuid); + + cp.svc_hint = svc_hint; + + mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp, + add_uuid_complete, NULL, NULL); +} + +int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint) +{ + uuid_t *uuid; + + uuid = sdp_uuid_to_uuid128(&rec->svclass); + + if (g_slist_find_custom(adapter.uuids, uuid, sdp_uuid_cmp)) { + char uuid_str[32]; + + sdp_uuid2strn(uuid, uuid_str, sizeof(uuid_str)); + DBG("UUID %s already added", uuid_str); + + bt_free(uuid); + return -EALREADY; + } + + adapter.uuids = g_slist_prepend(adapter.uuids, uuid); + + add_uuid(svc_hint, uuid); + + return add_record_to_server(&adapter.bdaddr, rec); +} + +void bt_adapter_remove_record(uint32_t handle) +{ + sdp_record_t *rec; + GSList *uuid_found; + + rec = sdp_record_find(handle); + if (!rec) + return; + + uuid_found = g_slist_find_custom(adapter.uuids, &rec->svclass, + sdp_uuid_cmp); + if (uuid_found) { + uuid_t *uuid = uuid_found->data; + + remove_uuid(uuid); + + adapter.uuids = g_slist_remove(adapter.uuids, uuid); + + free(uuid); + } + + remove_record_from_server(handle); +} + +static void set_mode_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to set mode: %s (0x%02x)", + mgmt_errstr(status), status); + return; + } + + /* + * The parameters are identical and also the task that is + * required in both cases. So it is safe to just call the + * event handling functions here. + */ + new_settings_callback(adapter.index, length, param, NULL); +} + +static bool set_mode(uint16_t opcode, uint8_t mode) +{ + struct mgmt_mode cp; + + memset(&cp, 0, sizeof(cp)); + cp.val = mode; + + DBG("opcode=0x%x mode=0x%x", opcode, mode); + + if (mgmt_send(mgmt_if, opcode, adapter.index, sizeof(cp), &cp, + set_mode_complete, NULL, NULL) > 0) + return true; + + error("Failed to set mode"); + + return false; +} + +static void set_io_capability(void) +{ + struct mgmt_cp_set_io_capability cp; + + memset(&cp, 0, sizeof(cp)); + cp.io_capability = DEFAULT_IO_CAPABILITY; + + if (mgmt_send(mgmt_if, MGMT_OP_SET_IO_CAPABILITY, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) == 0) + error("Failed to set IO capability"); +} + +static void set_device_id(void) +{ + struct mgmt_cp_set_device_id cp; + uint8_t major, minor; + uint16_t version; + + if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2) + return; + + version = major << 8 | minor; + + memset(&cp, 0, sizeof(cp)); + cp.source = htobs(DEVICE_ID_SOURCE); + cp.vendor = htobs(DEVICE_ID_VENDOR); + cp.product = htobs(DEVICE_ID_PRODUCT); + cp.version = htobs(version); + + if (mgmt_send(mgmt_if, MGMT_OP_SET_DEVICE_ID, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) == 0) + error("Failed to set device id"); + + register_device_id(DEVICE_ID_SOURCE, DEVICE_ID_VENDOR, + DEVICE_ID_PRODUCT, version); + + bt_adapter_add_record(sdp_record_find(0x10000), 0x00); +} + +static void set_adapter_name_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_cp_set_local_name *rp = param; + + if (status != MGMT_STATUS_SUCCESS) { + error("Failed to set name: %s (0x%02x)", mgmt_errstr(status), + status); + return; + } + + adapter_set_name(rp->name); +} + +static uint8_t set_adapter_name(const uint8_t *name, uint16_t len) +{ + struct mgmt_cp_set_local_name cp; + + memset(&cp, 0, sizeof(cp)); + memcpy(cp.name, name, len); + + if (mgmt_send(mgmt_if, MGMT_OP_SET_LOCAL_NAME, adapter.index, + sizeof(cp), &cp, set_adapter_name_complete, + NULL, NULL) > 0) + return HAL_STATUS_SUCCESS; + + error("Failed to set name"); + + return HAL_STATUS_FAILED; +} + +static uint8_t set_adapter_discoverable_timeout(const void *buf, uint16_t len) +{ + const uint32_t *timeout = buf; + + if (len != sizeof(*timeout)) { + error("Invalid set disc timeout size (%u bytes), terminating", + len); + raise(SIGTERM); + return HAL_STATUS_FAILED; + } + + /* + * Android handles discoverable timeout in Settings app. + * There is no need to use kernel feature for that. + * Just need to store this value here + */ + + memcpy(&adapter.discoverable_timeout, timeout, sizeof(uint32_t)); + + store_adapter_config(); + + send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT, + sizeof(adapter.discoverable_timeout), + &adapter.discoverable_timeout); + + return HAL_STATUS_SUCCESS; +} + +static void clear_uuids(void) +{ + struct mgmt_cp_remove_uuid cp; + + memset(&cp, 0, sizeof(cp)); + + mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), + &cp, NULL, NULL, NULL); +} + +static struct device *create_device_from_info(GKeyFile *key_file, + const char *peer) +{ + struct device *dev; + uint8_t type; + bdaddr_t bdaddr; + char **uuids; + char *str; + + /* BREDR if not present */ + type = g_key_file_get_integer(key_file, peer, "AddressType", NULL); + + str2ba(peer, &bdaddr); + dev = create_device(&bdaddr, type); + + if (type != BDADDR_BREDR) + dev->bredr = g_key_file_get_boolean(key_file, peer, "BREDR", + NULL); + + str = g_key_file_get_string(key_file, peer, "LinkKey", NULL); + if (str) { + g_free(str); + dev->bredr_paired = true; + dev->bredr_bonded = true; + } + + str = g_key_file_get_string(key_file, peer, "LongTermKey", NULL); + if (str) { + g_free(str); + dev->le_paired = true; + dev->le_bonded = true; + } + + str = g_key_file_get_string(key_file, peer, "SlaveLongTermKey", NULL); + if (str) { + g_free(str); + dev->le_paired = true; + dev->le_bonded = true; + } + + str = g_key_file_get_string(key_file, peer, "LocalCSRK", NULL); + if (str) { + int i; + + dev->valid_local_csrk = true; + for (i = 0; i < 16; i++) + sscanf(str + (i * 2), "%02hhX", &dev->local_csrk[i]); + + g_free(str); + + dev->local_sign_cnt = g_key_file_get_integer(key_file, peer, + "LocalCSRKSignCounter", NULL); + } + + str = g_key_file_get_string(key_file, peer, "RemoteCSRK", NULL); + if (str) { + int i; + + dev->valid_remote_csrk = true; + for (i = 0; i < 16; i++) + sscanf(str + (i * 2), "%02hhX", &dev->remote_csrk[i]); + + g_free(str); + + dev->remote_sign_cnt = g_key_file_get_integer(key_file, peer, + "RemoteCSRKSignCounter", NULL); + } + + str = g_key_file_get_string(key_file, peer, "GattCCC", NULL); + if (str) { + dev->gatt_ccc = atoi(str); + g_free(str); + } + + str = g_key_file_get_string(key_file, peer, "Name", NULL); + if (str) { + g_free(dev->name); + dev->name = str; + } + + str = g_key_file_get_string(key_file, peer, "FriendlyName", NULL); + if (str) { + g_free(dev->friendly_name); + dev->friendly_name = str; + } + + dev->class = g_key_file_get_integer(key_file, peer, "Class", NULL); + + if (dev->bredr) + dev->bredr_seen = g_key_file_get_integer(key_file, peer, + "Timestamp", + NULL); + else + dev->le_seen = g_key_file_get_integer(key_file, peer, + "Timestamp", NULL); + + uuids = g_key_file_get_string_list(key_file, peer, "Services", NULL, + NULL); + if (uuids) { + char **uuid; + + for (uuid = uuids; *uuid; uuid++) { + uint8_t *u = g_malloc0(16); + int i; + + for (i = 0; i < 16; i++) + sscanf((*uuid) + (i * 2), "%02hhX", &u[i]); + + dev->uuids = g_slist_append(dev->uuids, u); + } + + g_strfreev(uuids); + } + + return dev; +} + +static struct mgmt_link_key_info *get_key_info(GKeyFile *key_file, + const char *peer) +{ + struct mgmt_link_key_info *info = NULL; + char *str; + unsigned int i; + + str = g_key_file_get_string(key_file, peer, "LinkKey", NULL); + if (!str || strlen(str) != 32) + goto failed; + + info = g_new0(struct mgmt_link_key_info, 1); + + str2ba(peer, &info->addr.bdaddr); + + for (i = 0; i < sizeof(info->val); i++) + sscanf(str + (i * 2), "%02hhX", &info->val[i]); + + info->type = g_key_file_get_integer(key_file, peer, "LinkKeyType", + NULL); + info->pin_len = g_key_file_get_integer(key_file, peer, + "LinkKeyPinLength", NULL); + +failed: + g_free(str); + + return info; +} + +static struct mgmt_ltk_info *get_ltk_info(GKeyFile *key_file, const char *peer, + bool master) +{ + const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s; + struct mgmt_ltk_info *info = NULL; + char *key; + unsigned int i; + + key_s = master ? "LongTermKey" : "SlaveLongTermKey"; + keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType"; + encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize"; + ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv"; + rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand"; + + key = g_key_file_get_string(key_file, peer, key_s, NULL); + if (!key || strlen(key) != 32) + goto failed; + + info = g_new0(struct mgmt_ltk_info, 1); + + str2ba(peer, &info->addr.bdaddr); + + info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType", + NULL); + + for (i = 0; i < sizeof(info->val); i++) + sscanf(key + (i * 2), "%02hhX", &info->val[i]); + + info->type = g_key_file_get_integer(key_file, peer, keytype_s, NULL); + + info->enc_size = g_key_file_get_integer(key_file, peer, encsize_s, + NULL); + + info->rand = g_key_file_get_uint64(key_file, peer, rand_s, NULL); + info->rand = cpu_to_le64(info->rand); + + info->ediv = g_key_file_get_integer(key_file, peer, ediv_s, NULL); + info->ediv = cpu_to_le16(info->ediv); + + info->master = master; + +failed: + g_free(key); + + return info; +} + +static struct mgmt_irk_info *get_irk_info(GKeyFile *key_file, const char *peer) +{ + struct mgmt_irk_info *info = NULL; + unsigned int i; + char *str; + + str = g_key_file_get_string(key_file, peer, "IdentityResolvingKey", + NULL); + if (!str || strlen(str) != 32) + goto failed; + + info = g_new0(struct mgmt_irk_info, 1); + + str2ba(peer, &info->addr.bdaddr); + + info->addr.type = g_key_file_get_integer(key_file, peer, "AddressType", + NULL); + + for (i = 0; i < sizeof(info->val); i++) + sscanf(str + (i * 2), "%02hhX", &info->val[i]); + +failed: + g_free(str); + + return info; +} + +static time_t device_timestamp(const struct device *dev) +{ + if (dev->bredr && dev->le) { + if (dev->le_seen > dev->bredr_seen) + return dev->le_seen; + + return dev->bredr_seen; + } + + if (dev->bredr) + return dev->bredr_seen; + + return dev->le_seen; +} + +static int device_timestamp_cmp(gconstpointer a, gconstpointer b) +{ + const struct device *deva = a; + const struct device *devb = b; + + return device_timestamp(deva) < device_timestamp(devb); +} + +static void load_devices_cache(void) +{ + GKeyFile *key_file; + gchar **devs; + gsize len = 0; + unsigned int i; + + key_file = g_key_file_new(); + + g_key_file_load_from_file(key_file, CACHE_FILE, 0, NULL); + + devs = g_key_file_get_groups(key_file, &len); + + for (i = 0; i < len; i++) { + struct device *dev; + + dev = create_device_from_info(key_file, devs[i]); + cached_devices = g_slist_prepend(cached_devices, dev); + } + + cached_devices = g_slist_sort(cached_devices, device_timestamp_cmp); + + g_strfreev(devs); + g_key_file_free(key_file); +} + +static void load_devices_info(bt_bluetooth_ready cb) +{ + GKeyFile *key_file; + gchar **devs; + gsize len = 0; + unsigned int i; + GSList *keys = NULL; + GSList *ltks = NULL; + GSList *irks = NULL; + + key_file = g_key_file_new(); + + g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL); + + devs = g_key_file_get_groups(key_file, &len); + + for (i = 0; i < len; i++) { + struct mgmt_link_key_info *key_info; + struct mgmt_ltk_info *ltk_info; + struct mgmt_irk_info *irk_info; + struct mgmt_ltk_info *slave_ltk_info; + struct device *dev; + + key_info = get_key_info(key_file, devs[i]); + irk_info = get_irk_info(key_file, devs[i]); + ltk_info = get_ltk_info(key_file, devs[i], true); + slave_ltk_info = get_ltk_info(key_file, devs[i], false); + + if (!key_info && !ltk_info && !slave_ltk_info) { + error("Failed to load keys for %s, skipping", devs[i]); + + continue; + } + + if (key_info) + keys = g_slist_prepend(keys, key_info); + + if (irk_info) + irks = g_slist_prepend(irks, irk_info); + + if (ltk_info) + ltks = g_slist_prepend(ltks, ltk_info); + + if (slave_ltk_info) + ltks = g_slist_prepend(ltks, slave_ltk_info); + + dev = create_device_from_info(key_file, devs[i]); + + bonded_devices = g_slist_prepend(bonded_devices, dev); + } + + load_ltks(ltks); + g_slist_free_full(ltks, g_free); + + load_irks(irks); + g_slist_free_full(irks, g_free); + + load_link_keys(keys, cb); + g_slist_free_full(keys, g_free); + + g_strfreev(devs); + g_key_file_free(key_file); +} + +static void set_adapter_class(void) +{ + struct mgmt_cp_set_dev_class cp; + + memset(&cp, 0, sizeof(cp)); + + /* + * kernel assign the major and minor numbers straight to dev_class[0] + * and dev_class[1] without considering the proper bit shifting. + */ + cp.major = ADAPTER_MAJOR_CLASS & 0x1f; + cp.minor = ADAPTER_MINOR_CLASS << 2; + + if (mgmt_send(mgmt_if, MGMT_OP_SET_DEV_CLASS, adapter.index, sizeof(cp), + &cp, NULL, NULL, NULL) > 0) + return; + + error("Failed to set class of device"); +} + +static sdp_record_t *mps_record(void) +{ + sdp_data_t *mpsd_features, *mpmd_features, *dependencies; + sdp_list_t *svclass_id, *pfseq, *root; + uuid_t root_uuid, svclass_uuid; + sdp_profile_desc_t profile; + sdp_record_t *record; + uint64_t mpsd_feat, mpmd_feat; + uint16_t deps; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&svclass_uuid, MPS_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &svclass_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile.uuid, MPS_PROFILE_ID); + profile.version = 0x0100; + pfseq = sdp_list_append(NULL, &profile); + sdp_set_profile_descs(record, pfseq); + + mpsd_feat = MPS_DEFAULT_MPSD; + mpmd_feat = MPS_DEFAULT_MPMD; + + /* TODO should be configurable based on HFP AG support */ + if (false) { + mpsd_feat &= MPS_MPSD_HFP_AG_DEP; + mpmd_feat &= MPS_MPMD_HFP_AG_DEP; + } + + mpsd_features = sdp_data_alloc(SDP_UINT64, &mpsd_feat); + sdp_attr_add(record, SDP_ATTR_MPSD_SCENARIOS, mpsd_features); + + mpmd_features = sdp_data_alloc(SDP_UINT64, &mpmd_feat); + sdp_attr_add(record, SDP_ATTR_MPMD_SCENARIOS, mpmd_features); + + deps = MPS_DEFAULT_DEPS; + dependencies = sdp_data_alloc(SDP_UINT16, &deps); + sdp_attr_add(record, SDP_ATTR_MPS_DEPENDENCIES, dependencies); + + sdp_set_info_attr(record, "Multi Profile", 0, 0); + + sdp_list_free(pfseq, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static void add_mps_record(void) +{ + sdp_record_t *rec; + + rec = mps_record(); + if (!rec) { + error("Failed to allocate MPS record"); + return; + } + + if (bt_adapter_add_record(rec, 0) < 0) { + error("Failed to register MPS record"); + sdp_record_free(rec); + } +} + +static void clear_auto_connect_list_complete(uint8_t status, + uint16_t length, + const void *param, + void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) + error("Failed to clear auto connect list: %s (0x%02x)", + mgmt_errstr(status), status); +} + +static void clear_auto_connect_list(void) +{ + struct mgmt_cp_remove_device cp; + + if (!kernel_conn_control) + return; + + memset(&cp, 0, sizeof(cp)); + + if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index, sizeof(cp), + &cp, clear_auto_connect_list_complete, NULL, NULL) > 0) + return; + + error("Could not clear auto connect list"); +} + +static void read_info_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_read_info *rp = param; + bt_bluetooth_ready cb = user_data; + uint32_t missing_settings; + int err; + + DBG(""); + + if (status) { + error("Failed to read info for index %u: %s (0x%02x)", + adapter.index, mgmt_errstr(status), status); + err = -EIO; + goto failed; + } + + if (length < sizeof(*rp)) { + error("Too small read info complete response"); + err = -EIO; + goto failed; + } + + if (!bacmp(&rp->bdaddr, BDADDR_ANY)) { + error("No Bluetooth address"); + err = -ENODEV; + goto failed; + } + + load_adapter_config(); + + if (!bacmp(&adapter.bdaddr, BDADDR_ANY)) { + bacpy(&adapter.bdaddr, &rp->bdaddr); + adapter.name = g_strdup(DEFAULT_ADAPTER_NAME); + store_adapter_config(); + } else if (bacmp(&adapter.bdaddr, &rp->bdaddr)) { + error("Bluetooth address mismatch"); + err = -ENODEV; + goto failed; + } + + if (g_strcmp0(adapter.name, (const char *) rp->name)) + set_adapter_name((uint8_t *)adapter.name, strlen(adapter.name)); + + set_adapter_class(); + + /* Store adapter information */ + adapter.dev_class = rp->dev_class[0] | (rp->dev_class[1] << 8) | + (rp->dev_class[2] << 16); + + adapter.supported_settings = btohs(rp->supported_settings); + adapter.current_settings = btohs(rp->current_settings); + + /* TODO: Register all event notification handlers */ + register_mgmt_handlers(); + + clear_uuids(); + clear_auto_connect_list(); + + set_io_capability(); + set_device_id(); + add_mps_record(); + + missing_settings = adapter.current_settings ^ + adapter.supported_settings; + + if (missing_settings & MGMT_SETTING_SSP) + set_mode(MGMT_OP_SET_SSP, 0x01); + + if (missing_settings & MGMT_SETTING_SECURE_CONN) + set_mode(MGMT_OP_SET_SECURE_CONN, 0x01); + + if (missing_settings & MGMT_SETTING_BONDABLE) + set_mode(MGMT_OP_SET_BONDABLE, 0x01); + + load_devices_info(cb); + load_devices_cache(); + + return; + +failed: + cb(err, NULL); +} + +static void mgmt_index_added_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + bt_bluetooth_ready cb = user_data; + + DBG("index %u", index); + + if (adapter.index != MGMT_INDEX_NONE) { + DBG("skip event for index %u", index); + return; + } + + if (option_index != MGMT_INDEX_NONE && option_index != index) { + DBG("skip event for index %u (option %u)", index, option_index); + return; + } + + adapter.index = index; + + if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL, + read_info_complete, cb, NULL) == 0) { + cb(-EIO, NULL); + return; + } +} + +static void mgmt_index_removed_event(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + DBG("index %u", index); + + if (index != adapter.index) + return; + + error("Adapter was removed. Exiting."); + raise(SIGTERM); +} + +static void read_index_list_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_read_index_list *rp = param; + bt_bluetooth_ready cb = user_data; + uint16_t num; + int i; + + DBG(""); + + if (status) { + error("%s: Failed to read index list: %s (0x%02x)", __func__, + mgmt_errstr(status), status); + goto failed; + } + + if (length < sizeof(*rp)) { + error("%s: Wrong size of read index list response", __func__); + goto failed; + } + + num = btohs(rp->num_controllers); + + DBG("Number of controllers: %u", num); + + if (num * sizeof(uint16_t) + sizeof(*rp) != length) { + error("%s: Incorrect pkt size for index list rsp", __func__); + goto failed; + } + + if (adapter.index != MGMT_INDEX_NONE) + return; + + for (i = 0; i < num; i++) { + uint16_t index = btohs(rp->index[i]); + + if (option_index != MGMT_INDEX_NONE && option_index != index) + continue; + + if (mgmt_send(mgmt_if, MGMT_OP_READ_INFO, index, 0, NULL, + read_info_complete, cb, NULL) == 0) + goto failed; + + adapter.index = index; + return; + } + + return; + +failed: + cb(-EIO, NULL); +} + +static void read_version_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_read_version *rp = param; + uint8_t mgmt_version, mgmt_revision; + bt_bluetooth_ready cb = user_data; + + DBG(""); + + if (status) { + error("Failed to read version information: %s (0x%02x)", + mgmt_errstr(status), status); + goto failed; + } + + if (length < sizeof(*rp)) { + error("Wrong size response"); + goto failed; + } + + mgmt_version = rp->version; + mgmt_revision = btohs(rp->revision); + + info("Bluetooth management interface %u.%u initialized", + mgmt_version, mgmt_revision); + + if (MGMT_VERSION(mgmt_version, mgmt_revision) < MGMT_VERSION(1, 3)) { + error("Version 1.3 or later of management interface required"); + goto failed; + } + + /* Starting from mgmt 1.7, kernel can handle connection control */ + if (MGMT_VERSION(mgmt_version, mgmt_revision) >= MGMT_VERSION(1, 7)) { + info("Kernel connection control will be used"); + kernel_conn_control = true; + } + + mgmt_register(mgmt_if, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + mgmt_index_added_event, cb, NULL); + mgmt_register(mgmt_if, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + mgmt_index_removed_event, NULL, NULL); + + if (mgmt_send(mgmt_if, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, + NULL, read_index_list_complete, cb, NULL) > 0) + return; + + error("Failed to read controller index list"); + +failed: + cb(-EIO, NULL); +} + +bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb) +{ + DBG("index %d", index); + + mgmt_if = mgmt_new_default(); + if (!mgmt_if) { + error("Failed to access management interface"); + return false; + } + + if (mgmt_dbg) + mgmt_set_debug(mgmt_if, mgmt_debug, "mgmt_if: ", NULL); + + if (mgmt_send(mgmt_if, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE, 0, NULL, + read_version_complete, cb, NULL) == 0) { + error("Error sending READ_VERSION mgmt command"); + + mgmt_unref(mgmt_if); + mgmt_if = NULL; + + return false; + } + + if (index >= 0) + option_index = index; + + return true; +} + +static void shutdown_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + bt_bluetooth_stopped cb = user_data; + + if (status != MGMT_STATUS_SUCCESS) + error("Clean controller shutdown failed"); + + cb(); +} + +bool bt_bluetooth_stop(bt_bluetooth_stopped cb) +{ + struct mgmt_mode cp; + + if (adapter.index == MGMT_INDEX_NONE) + return false; + + info("Switching controller off"); + + memset(&cp, 0, sizeof(cp)); + + return mgmt_send(mgmt_if, MGMT_OP_SET_POWERED, adapter.index, + sizeof(cp), &cp, shutdown_complete, (void *)cb, + NULL) > 0; +} + +void bt_bluetooth_cleanup(void) +{ + g_free(adapter.name); + adapter.name = NULL; + + mgmt_unref(mgmt_if); + mgmt_if = NULL; +} + +static bool set_discoverable(uint8_t mode, uint16_t timeout) +{ + struct mgmt_cp_set_discoverable cp; + + memset(&cp, 0, sizeof(cp)); + cp.val = mode; + cp.timeout = htobs(timeout); + + DBG("mode %u timeout %u", mode, timeout); + + if (mgmt_send(mgmt_if, MGMT_OP_SET_DISCOVERABLE, adapter.index, + sizeof(cp), &cp, set_mode_complete, NULL, NULL) > 0) + return true; + + error("Failed to set mode discoverable"); + + return false; +} + +static uint8_t get_adapter_address(void) +{ + uint8_t buf[6]; + + bdaddr2android(&adapter.bdaddr, buf); + + send_adapter_property(HAL_PROP_ADAPTER_ADDR, sizeof(buf), buf); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_adapter_name(void) +{ + if (!adapter.name) + return HAL_STATUS_FAILED; + + adapter_name_changed((uint8_t *) adapter.name); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_adapter_class(void) +{ + DBG(""); + + adapter_class_changed(); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t settings2type(void) +{ + bool bredr, le; + + bredr = adapter.current_settings & MGMT_SETTING_BREDR; + le = adapter.current_settings & MGMT_SETTING_LE; + + if (bredr && le) + return HAL_TYPE_DUAL; + + if (bredr && !le) + return HAL_TYPE_BREDR; + + if (!bredr && le) + return HAL_TYPE_LE; + + return 0; +} + +static uint8_t get_adapter_type(void) +{ + uint8_t type; + + DBG(""); + + type = settings2type(); + + if (!type) + return HAL_STATUS_FAILED; + + send_adapter_property(HAL_PROP_ADAPTER_TYPE, sizeof(type), &type); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_adapter_service_rec(void) +{ + DBG("Not implemented"); + + /* TODO: Add implementation */ + + return HAL_STATUS_FAILED; +} + +static uint8_t get_adapter_scan_mode(void) +{ + DBG(""); + + scan_mode_changed(); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_adapter_bonded_devices(void) +{ + uint8_t buf[sizeof(bdaddr_t) * g_slist_length(bonded_devices)]; + int i = 0; + GSList *l; + + DBG(""); + + for (l = bonded_devices; l; l = g_slist_next(l)) { + struct device *dev = l->data; + + get_device_android_addr(dev, buf + (i * sizeof(bdaddr_t))); + i++; + } + + send_adapter_property(HAL_PROP_ADAPTER_BONDED_DEVICES, + i * sizeof(bdaddr_t), buf); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_adapter_discoverable_timeout(void) +{ + send_adapter_property(HAL_PROP_ADAPTER_DISC_TIMEOUT, + sizeof(adapter.discoverable_timeout), + &adapter.discoverable_timeout); + + return HAL_STATUS_SUCCESS; +} + +static void handle_get_adapter_prop_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_get_adapter_prop *cmd = buf; + uint8_t status; + + switch (cmd->type) { + case HAL_PROP_ADAPTER_ADDR: + status = get_adapter_address(); + break; + case HAL_PROP_ADAPTER_NAME: + status = get_adapter_name(); + break; + case HAL_PROP_ADAPTER_UUIDS: + status = get_adapter_uuids(); + break; + case HAL_PROP_ADAPTER_CLASS: + status = get_adapter_class(); + break; + case HAL_PROP_ADAPTER_TYPE: + status = get_adapter_type(); + break; + case HAL_PROP_ADAPTER_SERVICE_REC: + status = get_adapter_service_rec(); + break; + case HAL_PROP_ADAPTER_SCAN_MODE: + status = get_adapter_scan_mode(); + break; + case HAL_PROP_ADAPTER_BONDED_DEVICES: + status = get_adapter_bonded_devices(); + break; + case HAL_PROP_ADAPTER_DISC_TIMEOUT: + status = get_adapter_discoverable_timeout(); + break; + default: + status = HAL_STATUS_FAILED; + break; + } + + if (status != HAL_STATUS_SUCCESS) + error("Failed to get adapter property (type %u status %u)", + cmd->type, status); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, + status); +} + +static void get_adapter_properties(void) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_adapter_props_changed *ev = (void *) buf; + uint8_t bonded[g_slist_length(bonded_devices) * sizeof(bdaddr_t)]; + uint128_t uuids[g_slist_length(adapter.uuids)]; + uint8_t android_bdaddr[6]; + uint8_t type, mode; + size_t size, i; + GSList *l; + + size = sizeof(*ev); + + ev->status = HAL_STATUS_SUCCESS; + ev->num_props = 0; + + bdaddr2android(&adapter.bdaddr, &android_bdaddr); + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_ADDR, + sizeof(android_bdaddr), android_bdaddr); + ev->num_props++; + + if (adapter.name) { + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_NAME, + strlen(adapter.name), adapter.name); + ev->num_props++; + } + + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_CLASS, + sizeof(adapter.dev_class), &adapter.dev_class); + ev->num_props++; + + type = settings2type(); + if (type) { + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_TYPE, + sizeof(type), &type); + ev->num_props++; + } + + mode = settings2scan_mode(); + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_SCAN_MODE, + sizeof(mode), &mode); + ev->num_props++; + + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_DISC_TIMEOUT, + sizeof(adapter.discoverable_timeout), + &adapter.discoverable_timeout); + ev->num_props++; + + for (i = 0, l = bonded_devices; l; l = g_slist_next(l), i++) { + struct device *dev = l->data; + + get_device_android_addr(dev, bonded + (i * sizeof(bdaddr_t))); + } + + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_BONDED_DEVICES, + sizeof(bonded), bonded); + ev->num_props++; + + for (i = 0, l = adapter.uuids; l; l = g_slist_next(l), i++) { + uuid_t *uuid = l->data; + + memcpy(&uuids[i], &uuid->value.uuid128, sizeof(uint128_t)); + } + + size += fill_hal_prop(buf + size, HAL_PROP_ADAPTER_UUIDS, sizeof(uuids), + uuids); + ev->num_props++; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_ADAPTER_PROPS_CHANGED, size, buf); +} + +static void cancel_pending_confirm_name(gpointer data, gpointer user_data) +{ + struct device *dev = data; + + mgmt_cancel(mgmt_if, dev->confirm_id); + dev->confirm_id = 0; +} + +static bool stop_discovery(uint8_t type) +{ + struct mgmt_cp_stop_discovery cp; + + cp.type = get_supported_discovery_type() & type; + + DBG("type=0x%x", cp.type); + + if (cp.type == SCAN_TYPE_NONE) + return false; + + /* Lets drop all confirm name request as we don't need it anymore */ + g_slist_foreach(cached_devices, cancel_pending_confirm_name, NULL); + + if (mgmt_send(mgmt_if, MGMT_OP_STOP_DISCOVERY, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) > 0) + return true; + + error("Failed to stop discovery"); + return false; +} + +struct adv_user_data { + bt_le_set_advertising_done cb; + void *user_data; +}; + +static void set_advertising_cb(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct adv_user_data *data = user_data; + + DBG(""); + + if (status) + error("Failed to set adverising %s (0x%02x))", + mgmt_errstr(status), status); + + data->cb(status, data->user_data); +} + +bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb, + void *user_data) +{ + struct adv_user_data *data; + uint8_t adv = advertising ? 0x01 : 0x00; + + data = new0(struct adv_user_data, 1); + if (!data) + return false; + + data->cb = cb; + data->user_data = user_data; + + if (mgmt_send(mgmt_if, MGMT_OP_SET_ADVERTISING, adapter.index, + sizeof(adv), &adv, set_advertising_cb, data, free) > 0) + return true; + + error("Failed to set advertising"); + free(data); + return false; +} + +bool bt_le_register(bt_le_device_found cb) +{ + if (gatt_device_found_cb) + return false; + + gatt_device_found_cb = cb; + + return true; +} + +void bt_le_unregister(void) +{ + gatt_device_found_cb = NULL; +} + +bool bt_le_discovery_stop(bt_le_discovery_stopped cb) +{ + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) + return false; + + adapter.le_scanning = false; + + if (adapter.cur_discovery_type != SCAN_TYPE_LE) { + if (cb) + cb(); + + return true; + } + + if (!stop_discovery(SCAN_TYPE_LE)) + return false; + + gatt_discovery_stopped_cb = cb; + adapter.exp_discovery_type = SCAN_TYPE_NONE; + + return true; +} + +bool bt_le_discovery_start(void) +{ + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) + return false; + + adapter.le_scanning = true; + + /* + * If core is discovering - just set expected next scan type. + * It will be triggered in case current scan session is almost done + * i.e. we missed LE phase in interleaved scan, or we're trying to + * connect to device that was already discovered. + */ + if (adapter.cur_discovery_type != SCAN_TYPE_NONE) { + adapter.exp_discovery_type = SCAN_TYPE_LE; + return true; + } + + if (start_discovery(SCAN_TYPE_LE)) + return true; + + return false; +} + +struct read_rssi_user_data { + bt_read_device_rssi_done cb; + void *user_data; +}; + +static void read_device_rssi_cb(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_get_conn_info *rp = param; + struct read_rssi_user_data *data = user_data; + + DBG(""); + + if (status) + error("Failed to get conn info: %s (0x%02x))", + mgmt_errstr(status), status); + + if (length < sizeof(*rp)) { + error("Wrong size of get conn info response"); + return; + } + + data->cb(status, &rp->addr.bdaddr, rp->rssi, data->user_data); +} + +bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb, + void *user_data) +{ + struct device *dev; + struct read_rssi_user_data *data; + struct mgmt_cp_get_conn_info cp; + + dev = find_device(addr); + if (!dev) + return false; + + memcpy(&cp.addr.bdaddr, addr, sizeof(cp.addr.bdaddr)); + cp.addr.type = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type; + + data = new0(struct read_rssi_user_data, 1); + if (!data) + return false; + + data->cb = cb; + data->user_data = user_data; + + if (!mgmt_send(mgmt_if, MGMT_OP_GET_CONN_INFO, adapter.index, + sizeof(cp), &cp, read_device_rssi_cb, data, free)) { + free(data); + error("Failed to get conn info"); + return false; + } + + return true; +} + +bool bt_get_csrk(const bdaddr_t *addr, enum bt_csrk_type type, uint8_t key[16], + uint32_t *sign_cnt) +{ + struct device *dev; + bool local = (type == LOCAL_CSRK); + + dev = find_device(addr); + if (!dev) + return false; + + if (local && dev->valid_local_csrk) { + memcpy(key, dev->local_csrk, 16); + *sign_cnt = dev->local_sign_cnt; + } else if (!local && dev->valid_remote_csrk) { + memcpy(key, dev->remote_csrk, 16); + *sign_cnt = dev->remote_sign_cnt; + } else { + return false; + } + + return true; +} + +static void store_sign_counter(struct device *dev, enum bt_csrk_type type) +{ + const char *sign_cnt_s; + uint32_t sign_cnt; + GKeyFile *key_file; + bool local = (type == LOCAL_CSRK); + + gsize length = 0; + char addr[18]; + char *data; + + key_file = g_key_file_new(); + if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) { + g_key_file_free(key_file); + return; + } + + ba2str(&dev->bdaddr, addr); + + sign_cnt_s = local ? "LocalCSRKSignCounter" : "RemoteCSRKSignCounter"; + sign_cnt = local ? dev->local_sign_cnt : dev->remote_sign_cnt; + + g_key_file_set_integer(key_file, addr, sign_cnt_s, sign_cnt); + + data = g_key_file_to_data(key_file, &length, NULL); + g_file_set_contents(DEVICES_FILE, data, length, NULL); + g_free(data); + + g_key_file_free(key_file); +} + +void bt_update_sign_counter(const bdaddr_t *addr, enum bt_csrk_type type) +{ + struct device *dev; + + dev = find_device(addr); + if (!dev) + return; + + if (type == LOCAL_CSRK) + dev->local_sign_cnt++; + else + dev->remote_sign_cnt++; + + store_sign_counter(dev, type); +} + +static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len) +{ + const uint8_t *mode = buf; + bool conn, disc, cur_conn, cur_disc; + + if (len != sizeof(*mode)) { + error("Invalid set scan mode size (%u bytes), terminating", + len); + raise(SIGTERM); + return HAL_STATUS_FAILED; + } + + cur_conn = adapter.current_settings & MGMT_SETTING_CONNECTABLE; + cur_disc = adapter.current_settings & MGMT_SETTING_DISCOVERABLE; + + DBG("connectable %u discoverable %d mode %u", cur_conn, cur_disc, + *mode); + + switch (*mode) { + case HAL_ADAPTER_SCAN_MODE_NONE: + if (!cur_conn && !cur_disc) + goto done; + + conn = false; + disc = false; + break; + case HAL_ADAPTER_SCAN_MODE_CONN: + if (cur_conn && !cur_disc) + goto done; + + conn = true; + disc = false; + break; + case HAL_ADAPTER_SCAN_MODE_CONN_DISC: + if (cur_conn && cur_disc) + goto done; + + conn = true; + disc = true; + break; + default: + return HAL_STATUS_FAILED; + } + + if (cur_conn != conn) { + if (!set_mode(MGMT_OP_SET_CONNECTABLE, conn ? 0x01 : 0x00)) + return HAL_STATUS_FAILED; + } + + if (cur_disc != disc && conn) { + if (!set_discoverable(disc ? 0x01 : 0x00, 0)) + return HAL_STATUS_FAILED; + } + + return HAL_STATUS_SUCCESS; + +done: + /* Android expects property changed callback */ + scan_mode_changed(); + + return HAL_STATUS_SUCCESS; +} + +static void handle_set_adapter_prop_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_set_adapter_prop *cmd = buf; + uint8_t status; + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid set adapter prop cmd (0x%x), terminating", + cmd->type); + raise(SIGTERM); + return; + } + + switch (cmd->type) { + case HAL_PROP_ADAPTER_SCAN_MODE: + status = set_adapter_scan_mode(cmd->val, cmd->len); + break; + case HAL_PROP_ADAPTER_NAME: + status = set_adapter_name(cmd->val, cmd->len); + break; + case HAL_PROP_ADAPTER_DISC_TIMEOUT: + status = set_adapter_discoverable_timeout(cmd->val, cmd->len); + break; + default: + DBG("Unhandled property type 0x%x", cmd->type); + status = HAL_STATUS_FAILED; + break; + } + + if (status != HAL_STATUS_SUCCESS) + error("Failed to set adapter property (type %u status %u)", + cmd->type, status); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, + status); +} + +static void pair_device_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_pair_device *rp = param; + struct device *dev; + + DBG("status %u", status); + + dev = find_device(&rp->addr.bdaddr); + if (!dev) + return; + + /* + * Update pairing and paired status. Bonded status will be updated once + * any link key come + */ + update_device_state(dev, rp->addr.type, status_mgmt2hal(status), false, + !status, false); +} + +static uint8_t select_device_bearer(struct device *dev) +{ + uint8_t res; + + if (dev->bredr && dev->le) { + if (dev->le_seen > dev->bredr_seen) + res = dev->bdaddr_type; + else + res = BDADDR_BREDR; + } else { + res = dev->bredr ? BDADDR_BREDR : dev->bdaddr_type; + } + + DBG("Selected bearer %d", res); + + return res; +} + +uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr) +{ + struct device *dev; + + dev = find_device(bdaddr); + if (!dev) + return BDADDR_BREDR; + + return select_device_bearer(dev); +} + +static void handle_create_bond_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_create_bond *cmd = buf; + struct device *dev; + uint8_t status; + struct mgmt_cp_pair_device cp; + + dev = get_device_android(cmd->bdaddr); + + cp.io_cap = DEFAULT_IO_CAPABILITY; + cp.addr.type = select_device_bearer(dev); + bacpy(&cp.addr.bdaddr, &dev->bdaddr); + + if (device_is_paired(dev, cp.addr.type)) { + status = HAL_STATUS_FAILED; + goto fail; + } + + if (mgmt_send(mgmt_if, MGMT_OP_PAIR_DEVICE, adapter.index, sizeof(cp), + &cp, pair_device_complete, NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto fail; + } + + status = HAL_STATUS_SUCCESS; + + update_device_state(dev, cp.addr.type, HAL_STATUS_SUCCESS, true, false, + false); + +fail: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, + status); +} + +static void handle_cancel_bond_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_cancel_bond *cmd = buf; + struct mgmt_addr_info cp; + struct device *dev; + uint8_t status; + + dev = find_device_android(cmd->bdaddr); + if (!dev) { + status = HAL_STATUS_FAILED; + goto failed; + } + + cp.type = select_device_bearer(dev); + bacpy(&cp.bdaddr, &dev->bdaddr); + + if (mgmt_reply(mgmt_if, MGMT_OP_CANCEL_PAIR_DEVICE, adapter.index, + sizeof(cp), &cp, NULL, NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, + status); +} + +static void send_unpaired_notification(void *data, void *user_data) +{ + bt_unpaired_device_cb cb = data; + struct mgmt_addr_info *addr = user_data; + + cb(&addr->bdaddr, addr->type); +} + +static void unpair_device_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_rp_unpair_device *rp = param; + struct device *dev; + + DBG("status %u", status); + + if (status != MGMT_STATUS_SUCCESS && status != MGMT_STATUS_NOT_PAIRED) + return; + + dev = find_device(&rp->addr.bdaddr); + if (!dev) + return; + + update_device_state(dev, rp->addr.type, HAL_STATUS_SUCCESS, false, + false, false); + + /* Cast rp->addr to (void *) since queue_foreach don't take const */ + queue_foreach(unpaired_cb_list, send_unpaired_notification, + (void *)&rp->addr); +} + +static void handle_remove_bond_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_remove_bond *cmd = buf; + struct mgmt_cp_unpair_device cp; + struct device *dev; + uint8_t status; + + dev = find_device_android(cmd->bdaddr); + if (!dev) { + status = HAL_STATUS_FAILED; + goto failed; + } + + cp.disconnect = 1; + bacpy(&cp.addr.bdaddr, &dev->bdaddr); + + if (dev->le_paired) { + cp.addr.type = dev->bdaddr_type; + + if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, + sizeof(cp), &cp, unpair_device_complete, + NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + } + + if (dev->bredr_paired) { + cp.addr.type = BDADDR_BREDR; + + if (mgmt_send(mgmt_if, MGMT_OP_UNPAIR_DEVICE, adapter.index, + sizeof(cp), &cp, unpair_device_complete, + NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, + status); +} + +static void handle_pin_reply_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_pin_reply *cmd = buf; + uint8_t status; + bdaddr_t bdaddr; + char addr[18]; + + android2bdaddr(cmd->bdaddr, &bdaddr); + ba2str(&bdaddr, addr); + + DBG("%s accept %u pin_len %u", addr, cmd->accept, cmd->pin_len); + + if (!cmd->accept && cmd->pin_len) { + status = HAL_STATUS_INVALID; + goto failed; + } + + if (cmd->accept) { + struct mgmt_cp_pin_code_reply rp; + + memset(&rp, 0, sizeof(rp)); + + bacpy(&rp.addr.bdaddr, &bdaddr); + rp.addr.type = BDADDR_BREDR; + rp.pin_len = cmd->pin_len; + memcpy(rp.pin_code, cmd->pin_code, rp.pin_len); + + if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_REPLY, adapter.index, + sizeof(rp), &rp, NULL, NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + } else { + struct mgmt_cp_pin_code_neg_reply rp; + + bacpy(&rp.addr.bdaddr, &bdaddr); + rp.addr.type = BDADDR_BREDR; + + if (mgmt_reply(mgmt_if, MGMT_OP_PIN_CODE_NEG_REPLY, + adapter.index, sizeof(rp), &rp, + NULL, NULL, NULL) == 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + } + + status = HAL_STATUS_SUCCESS; +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, + status); +} + +static uint8_t user_confirm_reply(const bdaddr_t *bdaddr, uint8_t type, + bool accept) +{ + struct mgmt_addr_info cp; + uint16_t opcode; + + if (accept) + opcode = MGMT_OP_USER_CONFIRM_REPLY; + else + opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY; + + bacpy(&cp.bdaddr, bdaddr); + cp.type = type; + + if (mgmt_reply(mgmt_if, opcode, adapter.index, sizeof(cp), &cp, + NULL, NULL, NULL) > 0) + return HAL_STATUS_SUCCESS; + + return HAL_STATUS_FAILED; +} + +static uint8_t user_passkey_reply(const bdaddr_t *bdaddr, uint8_t type, + bool accept, uint32_t passkey) +{ + unsigned int id; + + if (accept) { + struct mgmt_cp_user_passkey_reply cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, bdaddr); + cp.addr.type = type; + cp.passkey = htobl(passkey); + + id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_REPLY, + adapter.index, sizeof(cp), &cp, + NULL, NULL, NULL); + } else { + struct mgmt_cp_user_passkey_neg_reply cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, bdaddr); + cp.addr.type = type; + + id = mgmt_reply(mgmt_if, MGMT_OP_USER_PASSKEY_NEG_REPLY, + adapter.index, sizeof(cp), &cp, + NULL, NULL, NULL); + } + + if (id == 0) + return HAL_STATUS_FAILED; + + return HAL_STATUS_SUCCESS; +} + +static void handle_ssp_reply_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_ssp_reply *cmd = buf; + struct device *dev; + uint8_t status; + char addr[18]; + + /* TODO should parameters sanity be verified here? */ + + dev = find_device_android(cmd->bdaddr); + if (!dev) + return; + + ba2str(&dev->bdaddr, addr); + + DBG("%s variant %u accept %u", addr, cmd->ssp_variant, cmd->accept); + + switch (cmd->ssp_variant) { + case HAL_SSP_VARIANT_CONFIRM: + case HAL_SSP_VARIANT_CONSENT: + status = user_confirm_reply(&dev->bdaddr, + select_device_bearer(dev), + cmd->accept); + break; + case HAL_SSP_VARIANT_ENTRY: + status = user_passkey_reply(&dev->bdaddr, + select_device_bearer(dev), + cmd->accept, cmd->passkey); + break; + case HAL_SSP_VARIANT_NOTIF: + status = HAL_STATUS_SUCCESS; + break; + default: + status = HAL_STATUS_INVALID; + break; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, + status); +} + +static void handle_get_remote_services_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_get_remote_services *cmd = buf; + uint8_t status; + bdaddr_t addr; + + android2bdaddr(&cmd->bdaddr, &addr); + + status = browse_remote_sdp(&addr); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICES, status); +} + +static uint8_t get_device_uuids(struct device *dev) +{ + send_device_uuids_notif(dev); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_class(struct device *dev) +{ + send_device_property(dev, HAL_PROP_DEVICE_CLASS, sizeof(dev->class), + &dev->class); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_type(struct device *dev) +{ + uint8_t type = get_device_android_type(dev); + + send_device_property(dev, HAL_PROP_DEVICE_TYPE, sizeof(type), &type); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_service_rec(struct device *dev) +{ + DBG("Not implemented"); + + /* TODO */ + + return HAL_STATUS_FAILED; +} + +static uint8_t get_device_friendly_name(struct device *dev) +{ + if (!dev->friendly_name) + return HAL_STATUS_FAILED; + + send_device_property(dev, HAL_PROP_DEVICE_FRIENDLY_NAME, + strlen(dev->friendly_name), dev->friendly_name); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_rssi(struct device *dev) +{ + if (!dev->rssi) + return HAL_STATUS_FAILED; + + send_device_property(dev, HAL_PROP_DEVICE_RSSI, sizeof(dev->rssi), + &dev->rssi); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t get_device_version_info(struct device *dev) +{ + DBG("Not implemented"); + + /* TODO */ + + return HAL_STATUS_FAILED; +} + +static uint8_t get_device_timestamp(struct device *dev) +{ + uint32_t timestamp; + + timestamp = device_timestamp(dev); + + send_device_property(dev, HAL_PROP_DEVICE_TIMESTAMP, sizeof(timestamp), + ×tamp); + + return HAL_STATUS_SUCCESS; +} + +static void get_remote_device_props(struct device *dev) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_remote_device_props *ev = (void *) buf; + uint128_t uuids[g_slist_length(dev->uuids)]; + uint8_t android_type; + uint32_t timestamp; + size_t size, i; + GSList *l; + + memset(buf, 0, sizeof(buf)); + + size = sizeof(*ev); + + ev->status = HAL_STATUS_SUCCESS; + get_device_android_addr(dev, ev->bdaddr); + + android_type = get_device_android_type(dev); + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE, + sizeof(android_type), &android_type); + ev->num_props++; + + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS, + sizeof(dev->class), &dev->class); + ev->num_props++; + + if (dev->rssi) { + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI, + sizeof(dev->rssi), &dev->rssi); + ev->num_props++; + } + + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME, + strlen(dev->name), dev->name); + ev->num_props++; + + if (dev->friendly_name) { + size += fill_hal_prop(buf + size, + HAL_PROP_DEVICE_FRIENDLY_NAME, + strlen(dev->friendly_name), + dev->friendly_name); + ev->num_props++; + } + + for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) + memcpy(&uuids[i], l->data, sizeof(uint128_t)); + + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_UUIDS, sizeof(uuids), + uuids); + ev->num_props++; + + timestamp = get_device_timestamp(dev); + + size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TIMESTAMP, + sizeof(timestamp), ×tamp); + ev->num_props++; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_EV_REMOTE_DEVICE_PROPS, size, buf); +} + +static void send_bonded_devices_props(void) +{ + GSList *l; + + for (l = bonded_devices; l; l = g_slist_next(l)) { + struct device *dev = l->data; + + get_remote_device_props(dev); + } +} + +static void handle_enable_cmd(const void *buf, uint16_t len) +{ + uint8_t status; + + /* + * Framework expects all properties to be emitted while enabling + * adapter + */ + get_adapter_properties(); + + /* Sent also properties of bonded devices */ + send_bonded_devices_props(); + + if (adapter.current_settings & MGMT_SETTING_POWERED) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + if (!set_mode(MGMT_OP_SET_POWERED, 0x01)) { + status = HAL_STATUS_FAILED; + goto reply; + } + + status = HAL_STATUS_SUCCESS; +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, status); +} + +static void handle_disable_cmd(const void *buf, uint16_t len) +{ + uint8_t status; + + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* Cancel all pending requests. Need it in case of ongoing paring */ + mgmt_cancel_index(mgmt_if, adapter.index); + + if (!set_mode(MGMT_OP_SET_POWERED, 0x00)) { + status = HAL_STATUS_FAILED; + goto reply; + } + + status = HAL_STATUS_SUCCESS; +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, status); +} + +static void handle_get_adapter_props_cmd(const void *buf, uint16_t len) +{ + get_adapter_properties(); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_ADAPTER_PROPS, HAL_STATUS_SUCCESS); +} + +static void handle_get_remote_device_props_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_get_remote_device_props *cmd = buf; + struct device *dev; + uint8_t status; + + dev = find_device_android(cmd->bdaddr); + if (!dev) { + status = HAL_STATUS_INVALID; + goto failed; + } + + get_remote_device_props(dev); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROPS, status); +} + +static void handle_get_remote_device_prop_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_get_remote_device_prop *cmd = buf; + struct device *dev; + uint8_t status; + + dev = find_device_android(cmd->bdaddr); + if (!dev) { + status = HAL_STATUS_INVALID; + goto failed; + } + + switch (cmd->type) { + case HAL_PROP_DEVICE_NAME: + status = get_device_name(dev); + break; + case HAL_PROP_DEVICE_UUIDS: + status = get_device_uuids(dev); + break; + case HAL_PROP_DEVICE_CLASS: + status = get_device_class(dev); + break; + case HAL_PROP_DEVICE_TYPE: + status = get_device_type(dev); + break; + case HAL_PROP_DEVICE_SERVICE_REC: + status = get_device_service_rec(dev); + break; + case HAL_PROP_DEVICE_FRIENDLY_NAME: + status = get_device_friendly_name(dev); + break; + case HAL_PROP_DEVICE_RSSI: + status = get_device_rssi(dev); + break; + case HAL_PROP_DEVICE_VERSION_INFO: + status = get_device_version_info(dev); + break; + case HAL_PROP_DEVICE_TIMESTAMP: + status = get_device_timestamp(dev); + break; + default: + status = HAL_STATUS_FAILED; + break; + } + + if (status != HAL_STATUS_SUCCESS) + error("Failed to get device property (type %u status %u)", + cmd->type, status); + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROP, status); +} + +static uint8_t set_device_friendly_name(struct device *dev, const uint8_t *val, + uint16_t len) +{ + DBG(""); + + g_free(dev->friendly_name); + dev->friendly_name = g_strndup((const char *) val, len); + + if (dev->bredr_paired || dev->le_paired) + store_device_info(dev, DEVICES_FILE); + else + store_device_info(dev, CACHE_FILE); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t set_device_version_info(struct device *dev) +{ + DBG("Not implemented"); + + /* TODO */ + + return HAL_STATUS_FAILED; +} + +static void handle_set_remote_device_prop_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_set_remote_device_prop *cmd = buf; + struct device *dev; + uint8_t status; + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid set remote device prop cmd (0x%x), terminating", + cmd->type); + raise(SIGTERM); + return; + } + + dev = find_device_android(cmd->bdaddr); + if (!dev) { + status = HAL_STATUS_INVALID; + goto failed; + } + + switch (cmd->type) { + case HAL_PROP_DEVICE_FRIENDLY_NAME: + status = set_device_friendly_name(dev, cmd->val, cmd->len); + break; + case HAL_PROP_DEVICE_VERSION_INFO: + status = set_device_version_info(dev); + break; + default: + status = HAL_STATUS_FAILED; + break; + } + + if (status != HAL_STATUS_SUCCESS) + error("Failed to set device property (type %u status %u)", + cmd->type, status); + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_REMOTE_DEVICE_PROP, status); +} + +static void handle_get_remote_service_rec_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_get_remote_service_rec *cmd = buf; + uint8_t status; + bdaddr_t addr; + + android2bdaddr(&cmd->bdaddr, &addr); + + status = find_remote_sdp_rec(&addr, cmd->uuid); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICE_REC, status); +} + +static void handle_start_discovery_cmd(const void *buf, uint16_t len) +{ + uint8_t status; + + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { + status = HAL_STATUS_NOT_READY; + goto failed; + } + + switch (adapter.cur_discovery_type) { + case SCAN_TYPE_DUAL: + case SCAN_TYPE_BREDR: + break; + case SCAN_TYPE_NONE: + if (!start_discovery(SCAN_TYPE_DUAL)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case SCAN_TYPE_LE: + if (get_supported_discovery_type() == SCAN_TYPE_LE) + break; + + if (!stop_discovery(SCAN_TYPE_LE)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + adapter.exp_discovery_type = SCAN_TYPE_DUAL; + break; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, + status); +} + +static void handle_cancel_discovery_cmd(const void *buf, uint16_t len) +{ + uint8_t status; + + if (!(adapter.current_settings & MGMT_SETTING_POWERED)) { + status = HAL_STATUS_NOT_READY; + goto failed; + } + + switch (adapter.cur_discovery_type) { + case SCAN_TYPE_NONE: + break; + case SCAN_TYPE_LE: + if (get_supported_discovery_type() != SCAN_TYPE_LE) + break; + + if (adapter.exp_discovery_type == SCAN_TYPE_LE) { + status = HAL_STATUS_BUSY; + goto failed; + } + + if (!stop_discovery(SCAN_TYPE_LE)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case SCAN_TYPE_DUAL: + case SCAN_TYPE_BREDR: + if (!stop_discovery(SCAN_TYPE_DUAL)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (adapter.exp_discovery_type != SCAN_TYPE_LE) + adapter.exp_discovery_type = SCAN_TYPE_NONE; + + break; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, + status); +} + +static void handle_dut_mode_conf_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_dut_mode_conf *cmd = buf; + char path[FILENAME_MAX]; + uint8_t status; + int fd, ret; + + DBG("enable %u", cmd->enable); + + snprintf(path, sizeof(path), DUT_MODE_FILE, adapter.index); + + fd = open(path, O_WRONLY); + if (fd < 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (cmd->enable) + ret = write(fd, "1", sizeof("1")); + else + ret = write(fd, "0", sizeof("0")); + + if (ret < 0) + status = HAL_STATUS_FAILED; + else + status = HAL_STATUS_SUCCESS; + + close(fd); + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF, + status); +} + +static void handle_dut_mode_send_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_dut_mode_send *cmd = buf; + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid dut mode send cmd, terminating"); + raise(SIGTERM); + return; + } + + error("dut_mode_send not supported (cmd opcode %u)", cmd->opcode); + + /* TODO */ + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, + HAL_STATUS_FAILED); +} + +static void handle_le_test_mode_cmd(const void *buf, uint16_t len) +{ + const struct hal_cmd_le_test_mode *cmd = buf; + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid le test mode cmd, terminating"); + raise(SIGTERM); + return; + } + + error("le_test_mode not supported (cmd opcode %u)", cmd->opcode); + + /* TODO */ + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE, + HAL_STATUS_FAILED); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_ENABLE */ + { handle_enable_cmd, false, 0 }, + /* HAL_OP_DISABLE */ + { handle_disable_cmd, false, 0 }, + /* HAL_OP_GET_ADAPTER_PROPS */ + { handle_get_adapter_props_cmd, false, 0 }, + /* HAL_OP_GET_ADAPTER_PROP */ + { handle_get_adapter_prop_cmd, false, + sizeof(struct hal_cmd_get_adapter_prop) }, + /* HAL_OP_SET_ADAPTER_PROP */ + { handle_set_adapter_prop_cmd, true, + sizeof(struct hal_cmd_set_adapter_prop) }, + /* HAL_OP_GET_REMOTE_DEVICE_PROPS */ + { handle_get_remote_device_props_cmd, false, + sizeof(struct hal_cmd_get_remote_device_props) }, + /* HAL_OP_GET_REMOTE_DEVICE_PROP */ + { handle_get_remote_device_prop_cmd, false, + sizeof(struct hal_cmd_get_remote_device_prop) }, + /* HAL_OP_SET_REMOTE_DEVICE_PROP */ + { handle_set_remote_device_prop_cmd, true, + sizeof(struct hal_cmd_set_remote_device_prop) }, + /* HAL_OP_GET_REMOTE_SERVICE_REC */ + { handle_get_remote_service_rec_cmd, false, + sizeof(struct hal_cmd_get_remote_service_rec) }, + /* HAL_OP_GET_REMOTE_SERVICES */ + { handle_get_remote_services_cmd, false, + sizeof(struct hal_cmd_get_remote_services) }, + /* HAL_OP_START_DISCOVERY */ + { handle_start_discovery_cmd, false, 0 }, + /* HAL_OP_CANCEL_DISCOVERY */ + { handle_cancel_discovery_cmd, false, 0 }, + /* HAL_OP_CREATE_BOND */ + { handle_create_bond_cmd, false, sizeof(struct hal_cmd_create_bond) }, + /* HAL_OP_REMOVE_BOND */ + { handle_remove_bond_cmd, false, sizeof(struct hal_cmd_remove_bond) }, + /* HAL_OP_CANCEL_BOND */ + {handle_cancel_bond_cmd, false, sizeof(struct hal_cmd_cancel_bond) }, + /* HAL_OP_PIN_REPLY */ + { handle_pin_reply_cmd, false, sizeof(struct hal_cmd_pin_reply) }, + /* HAL_OP_SSP_REPLY */ + { handle_ssp_reply_cmd, false, sizeof(struct hal_cmd_ssp_reply) }, + /* HAL_OP_DUT_MODE_CONF */ + { handle_dut_mode_conf_cmd, false, + sizeof(struct hal_cmd_dut_mode_conf) }, + /* HAL_OP_DUT_MODE_SEND */ + { handle_dut_mode_send_cmd, true, + sizeof(struct hal_cmd_dut_mode_send) }, + /* HAL_OP_LE_TEST_MODE */ + { handle_le_test_mode_cmd, true, sizeof(struct hal_cmd_le_test_mode) }, +}; + +bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode) +{ + uint32_t missing_settings; + + DBG("mode 0x%x", mode); + + unpaired_cb_list = queue_new(); + if (!unpaired_cb_list) { + error("Can not allocate queue for unpaired callbacks"); + return false; + } + + missing_settings = adapter.current_settings ^ + adapter.supported_settings; + + switch (mode) { + case HAL_MODE_DEFAULT: + if (missing_settings & MGMT_SETTING_BREDR) + set_mode(MGMT_OP_SET_BREDR, 0x01); + + if (missing_settings & MGMT_SETTING_LE) + set_mode(MGMT_OP_SET_LE, 0x01); + break; + case HAL_MODE_LE: + /* Fail if controller does not support LE */ + if (!(adapter.supported_settings & MGMT_SETTING_LE)) { + error("LE Mode not supported by controller"); + goto failed; + } + + /* If LE it is not yet enabled then enable it */ + if (!(adapter.current_settings & MGMT_SETTING_LE)) + set_mode(MGMT_OP_SET_LE, 0x01); + + /* Disable BR/EDR if it is enabled */ + if (adapter.current_settings & MGMT_SETTING_BREDR) + set_mode(MGMT_OP_SET_BREDR, 0x00); + break; + case HAL_MODE_BREDR: + /* Fail if controller does not support BR/EDR */ + if (!(adapter.supported_settings & MGMT_SETTING_BREDR)) { + error("BR/EDR Mode not supported"); + goto failed; + } + + /* Enable BR/EDR if it is not enabled */ + if (missing_settings & MGMT_SETTING_BREDR) + set_mode(MGMT_OP_SET_BREDR, 0x01); + + /* + * According to Core Spec 4.0 host should not disable LE in + * controller if it was enabled (Vol 2. Part E. 7.3.79). + * Core Spec 4.1 removed this limitation and chips seem to be + * handling this just fine anyway. + */ + if (adapter.current_settings & MGMT_SETTING_LE) + set_mode(MGMT_OP_SET_LE, 0x00); + break; + default: + error("Unknown mode 0x%x", mode); + goto failed; + } + + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + return true; + +failed: + queue_destroy(unpaired_cb_list, NULL); + return false; +} + +void bt_bluetooth_unregister(void) +{ + DBG(""); + + g_slist_free_full(bonded_devices, (GDestroyNotify) free_device); + bonded_devices = NULL; + + g_slist_free_full(cached_devices, (GDestroyNotify) free_device); + cached_devices = NULL; + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE); + hal_ipc = NULL; + + queue_destroy(unpaired_cb_list, NULL); + unpaired_cb_list = NULL; +} diff -Nru bluez-4.101/android/bluetoothd-snoop.c bluez-5.23/android/bluetoothd-snoop.c --- bluez-4.101/android/bluetoothd-snoop.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/bluetoothd-snoop.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,249 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#if defined(ANDROID) +#include +#endif + +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/mgmt.h" + +#include "monitor/mainloop.h" +#include "src/shared/btsnoop.h" + +#define DEFAULT_SNOOP_FILE "/sdcard/btsnoop_hci.log" + +static struct btsnoop *snoop = NULL; +static uint8_t monitor_buf[BTSNOOP_MAX_PACKET_SIZE]; +static int monitor_fd = -1; + +static void signal_callback(int signum, void *user_data) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + mainloop_quit(); + break; + } +} + +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; +} + +static void data_callback(int fd, uint32_t events, void *user_data) +{ + unsigned char control[32]; + struct mgmt_hdr hdr; + struct msghdr msg; + struct iovec iov[2]; + + if (events & (EPOLLERR | EPOLLHUP)) { + mainloop_remove_fd(monitor_fd); + return; + } + + iov[0].iov_base = &hdr; + iov[0].iov_len = MGMT_HDR_SIZE; + iov[1].iov_base = monitor_buf; + iov[1].iov_len = sizeof(monitor_buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + while (true) { + struct cmsghdr *cmsg; + struct timeval *tv = NULL; + struct timeval ctv; + uint16_t opcode, index, pktlen; + uint32_t flags; + ssize_t len; + + len = recvmsg(monitor_fd, &msg, MSG_DONTWAIT); + if (len < 0) + break; + + if (len < MGMT_HDR_SIZE) + break; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + + if (cmsg->cmsg_type == SCM_TIMESTAMP) { + memcpy(&ctv, CMSG_DATA(cmsg), sizeof(ctv)); + tv = &ctv; + } + } + + opcode = btohs(hdr.opcode); + index = btohs(hdr.index); + pktlen = btohs(hdr.len); + + if (index) + continue; + + flags = get_flags_from_opcode(opcode); + if (flags != 0xff) + btsnoop_write(snoop, tv, flags, monitor_buf, pktlen); + } +} + +static int open_monitor(const char *path) +{ + struct sockaddr_hci addr; + int opt = 1; + + snoop = btsnoop_create(path, BTSNOOP_TYPE_HCI); + if (!snoop) + return -1; + + monitor_fd = socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC, BTPROTO_HCI); + if (monitor_fd < 0) + goto failed; + + memset(&addr, 0, sizeof(addr)); + addr.hci_family = AF_BLUETOOTH; + addr.hci_dev = HCI_DEV_NONE; + addr.hci_channel = HCI_CHANNEL_MONITOR; + + if (bind(monitor_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + goto failed_close; + + if (setsockopt(monitor_fd, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)) + < 0) + goto failed_close; + + mainloop_add_fd(monitor_fd, EPOLLIN, data_callback, NULL, NULL); + + return 0; + +failed_close: + close(monitor_fd); + monitor_fd = -1; + +failed: + btsnoop_unref(snoop); + snoop = NULL; + + return -1; +} + +static void close_monitor(void) +{ + btsnoop_unref(snoop); + snoop = NULL; + + close(monitor_fd); + monitor_fd = -1; +} + +static void set_capabilities(void) +{ +#if defined(ANDROID) + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + + /* + * CAP_NET_RAW: for snooping + * CAP_DAC_READ_SEARCH: override path search permissions + */ + cap.effective = cap.permitted = + CAP_TO_MASK(CAP_NET_RAW) | + CAP_TO_MASK(CAP_DAC_READ_SEARCH); + cap.inheritable = 0; + + /* TODO: Move to cap_set_proc once bionic support it */ + if (capset(&header, &cap) < 0) + exit(EXIT_FAILURE); +#endif +} + +int main(int argc, char *argv[]) +{ + const char *path; + sigset_t mask; + + set_capabilities(); + + if (argc > 1) + path = argv[1]; + else + path = DEFAULT_SNOOP_FILE; + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + if (!strcmp(DEFAULT_SNOOP_FILE, path)) + rename(DEFAULT_SNOOP_FILE, DEFAULT_SNOOP_FILE ".old"); + + if (open_monitor(path) < 0) { + printf("Failed to start bluetoothd_snoop\n"); + return EXIT_FAILURE; + } + + mainloop_run(); + + close_monitor(); + + return EXIT_SUCCESS; +} diff -Nru bluez-4.101/android/bluetooth.h bluez-5.23/android/bluetooth.h --- bluez-4.101/android/bluetooth.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/bluetooth.h 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,91 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +enum bt_csrk_type { + LOCAL_CSRK, + REMOTE_CSRK, +}; + +typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr); +bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb); + +typedef void (*bt_bluetooth_stopped)(void); +bool bt_bluetooth_stop(bt_bluetooth_stopped cb); + +void bt_bluetooth_cleanup(void); + +bool bt_bluetooth_register(struct ipc *ipc, uint8_t mode); +void bt_bluetooth_unregister(void); + +int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint); +void bt_adapter_remove_record(uint32_t handle); + +typedef void (*bt_le_device_found)(const bdaddr_t *addr, uint8_t addr_type, + int rssi, uint16_t eir_len, + const void *eir, bool discoverable, + bool bonded); +bool bt_le_register(bt_le_device_found cb); +void bt_le_unregister(void); + +bool bt_le_discovery_start(void); + +typedef void (*bt_le_discovery_stopped)(void); +bool bt_le_discovery_stop(bt_le_discovery_stopped cb); + +typedef void (*bt_le_set_advertising_done)(uint8_t status, void *user_data); +bool bt_le_set_advertising(bool advertising, bt_le_set_advertising_done cb, + void *user_data); + +uint8_t bt_get_device_android_type(const bdaddr_t *addr); +bool bt_is_device_le(const bdaddr_t *addr); +uint8_t bt_device_last_seen_bearer(const bdaddr_t *bdaddr); + +const char *bt_get_adapter_name(void); +bool bt_device_is_bonded(const bdaddr_t *bdaddr); +bool bt_device_set_uuids(const bdaddr_t *bdaddr, GSList *uuids); + +typedef void (*bt_read_device_rssi_done)(uint8_t status, const bdaddr_t *addr, + int8_t rssi, void *user_data); +bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb, + void *user_data); + +bool bt_get_csrk(const bdaddr_t *addr, enum bt_csrk_type type, + uint8_t key[16], uint32_t *sign_cnt); + +void bt_update_sign_counter(const bdaddr_t *addr, enum bt_csrk_type type); + +void bt_store_gatt_ccc(const bdaddr_t *addr, uint16_t value); + +uint16_t bt_get_gatt_ccc(const bdaddr_t *addr); + +const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type); + +bool bt_kernel_conn_control(void); + +bool bt_auto_connect_add(const bdaddr_t *addr); + +void bt_auto_connect_remove(const bdaddr_t *addr); + +typedef void (*bt_unpaired_device_cb)(const bdaddr_t *addr, uint8_t type); +bool bt_unpaired_register(bt_unpaired_device_cb cb); +void bt_unpaired_unregister(bt_unpaired_device_cb cb); diff -Nru bluez-4.101/android/client/haltest.c bluez-5.23/android/client/haltest.c --- bluez-4.101/android/client/haltest.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/haltest.c 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "if-main.h" +#include "terminal.h" +#include "pollhandler.h" +#include "history.h" + +const struct interface *interfaces[] = { + &audio_if, + &sco_if, + &bluetooth_if, + &av_if, + &rc_if, + &gatt_if, + &gatt_client_if, + &gatt_server_if, + &hf_if, + &hh_if, + &pan_if, + &hl_if, + &sock_if, + NULL +}; + +static struct method commands[]; + +struct method *get_method(struct method *methods, const char *name) +{ + while (strcmp(methods->name, "") != 0) { + if (strcmp(methods->name, name) == 0) + return methods; + methods++; + } + + return NULL; +} + +/* function returns interface of given name or NULL if not found */ +const struct interface *get_interface(const char *name) +{ + int i; + + for (i = 0; interfaces[i] != NULL; ++i) { + if (strcmp(interfaces[i]->name, name) == 0) + break; + } + + return interfaces[i]; +} + +int haltest_error(const char *format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = terminal_vprint(format, args); + va_end(args); + return ret; +} + +int haltest_info(const char *format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = terminal_vprint(format, args); + va_end(args); + return ret; +} + +int haltest_warn(const char *format, ...) +{ + va_list args; + int ret; + va_start(args, format); + ret = terminal_vprint(format, args); + va_end(args); + return ret; +} + +static void help_print_interface(const struct interface *i) +{ + struct method *m; + + for (m = i->methods; strcmp(m->name, "") != 0; m++) + haltest_info("%s %s %s\n", i->name, m->name, + (m->help ? m->help : "")); +} + +/* Help completion */ +static void help_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 2) + *enum_func = interface_name; +} + +/* Help execution */ +static void help_p(int argc, const char **argv) +{ + const struct method *m = commands; + const struct interface **ip = interfaces; + const struct interface *i; + + if (argc == 1) { + terminal_print("haltest allows to call Android HAL methods.\n"); + terminal_print("\nAvailable commands:\n"); + while (0 != strcmp(m->name, "")) { + terminal_print("\t%s %s\n", m->name, + (m->help ? m->help : "")); + m++; + } + + terminal_print("\nAvailable interfaces to use:\n"); + while (NULL != *ip) { + terminal_print("\t%s\n", (*ip)->name); + ip++; + } + + terminal_print("\nTo get help on methods for each interface type:\n"); + terminal_print("\n\thelp \n"); + terminal_print("\nBasic scenario:\n\tbluetooth init\n"); + terminal_print("\tbluetooth enable\n\tbluetooth start_discovery\n"); + terminal_print("\tbluetooth get_profile_interface handsfree\n"); + terminal_print("\thandsfree init\n\n"); + return; + } + + i = get_interface(argv[1]); + if (i == NULL) { + haltest_error("No such interface\n"); + return; + } + + help_print_interface(i); +} + +/* quit/exit execution */ +static void quit_p(int argc, const char **argv) +{ + exit(0); +} + +static int fd_stack[10]; +static int fd_stack_pointer = 0; + +static void stdin_handler(struct pollfd *pollfd); + +static void process_file(const char *name) +{ + int fd = open(name, O_RDONLY); + + if (fd < 0) { + haltest_error("Can't open file: %s for reading\n", name); + return; + } + + if (fd_stack_pointer >= 10) { + haltest_error("To many open files\n"); + close(fd); + return; + } + + fd_stack[fd_stack_pointer++] = fd; + poll_unregister_fd(fd_stack[fd_stack_pointer - 2], stdin_handler); + poll_register_fd(fd_stack[fd_stack_pointer - 1], POLLIN, stdin_handler); +} + +static void source_p(int argc, const char **argv) +{ + if (argc < 2) { + haltest_error("No file specified"); + return; + } + + process_file(argv[1]); +} + +/* Commands available without interface */ +static struct method commands[] = { + STD_METHODCH(help, "[]"), + STD_METHOD(quit), + METHOD("exit", quit_p, NULL, NULL), + STD_METHODH(source, ""), + END_METHOD +}; + +/* Gets comman by name */ +struct method *get_command(const char *name) +{ + return get_method(commands, name); +} + +/* Function to enumerate interface names */ +const char *interface_name(void *v, int i) +{ + return interfaces[i] ? interfaces[i]->name : NULL; +} + +/* Function to enumerate command and interface names */ +const char *command_name(void *v, int i) +{ + int cmd_cnt = NELEM(commands); + + if (i >= cmd_cnt) + return interface_name(v, i - cmd_cnt); + else + return commands[i].name; +} + +/* + * This function changes input parameter line_buffer so it has + * null termination after each token (due to strtok) + * Output argv is filled with pointers to arguments + * returns number of tokens parsed - argc + */ +static int command_line_to_argv(char *line_buffer, char *argv[], int argv_size) +{ + static const char *token_breaks = "\r\n\t "; + char *token; + int argc = 0; + + token = strtok(line_buffer, token_breaks); + while (token != NULL && argc < (int) argv_size) { + argv[argc++] = token; + token = strtok(NULL, token_breaks); + } + + return argc; +} + +static void process_line(char *line_buffer) +{ + char *argv[50]; + int argc; + int i = 0; + struct method *m; + + argc = command_line_to_argv(line_buffer, argv, 50); + if (argc < 1) + return; + + while (interfaces[i] != NULL) { + if (strcmp(interfaces[i]->name, argv[0])) { + i++; + continue; + } + + if (argc < 2 || strcmp(argv[1], "?") == 0) { + help_print_interface(interfaces[i]); + return; + } + + m = get_method(interfaces[i]->methods, argv[1]); + if (m != NULL) { + m->func(argc, (const char **) argv); + return; + } + + haltest_error("No function %s found\n", argv[1]); + return; + } + /* No interface, try commands */ + m = get_command(argv[0]); + if (m == NULL) + haltest_error("No such command %s\n", argv[0]); + else + m->func(argc, (const char **) argv); +} + +/* called when there is something on stdin */ +static void stdin_handler(struct pollfd *pollfd) +{ + char buf[10]; + + if (pollfd->revents & POLLIN) { + int count = read(fd_stack[fd_stack_pointer - 1], buf, 10); + + if (count > 0) { + int i; + + for (i = 0; i < count; ++i) + terminal_process_char(buf[i], process_line); + return; + } + } + + if (fd_stack_pointer > 1) + poll_register_fd(fd_stack[fd_stack_pointer - 2], POLLIN, + stdin_handler); + if (fd_stack_pointer > 0) { + poll_unregister_fd(fd_stack[--fd_stack_pointer], stdin_handler); + + if (fd_stack[fd_stack_pointer]) + close(fd_stack[fd_stack_pointer]); + } +} + +static void usage(void) +{ + printf("haltest Android Bluetooth HAL testing tool\n" + "Usage:\n"); + printf("\thaltest [options]\n"); + printf("options:\n" + "\t-n, --no-init Don't call init for interfaces\n" + "\t --version Print version\n" + "\t-h, --help Show help options\n"); +} + +enum { + PRINT_VERSION = 1000 +}; + +int version = 1; +int revision = 0; + +static void print_version(void) +{ + printf("haltest version %d.%d\n", version, revision); +} + +static const struct option main_options[] = { + { "no-init", no_argument, NULL, 'n' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, PRINT_VERSION }, + { NULL } +}; + +static bool no_init = false; + +static void parse_command_line(int argc, char *argv[]) +{ + for (;;) { + int opt; + + opt = getopt_long(argc, argv, "nh", main_options, NULL); + if (opt < 0) + break; + + switch (opt) { + case 'n': + no_init = true; + break; + case 'h': + usage(); + exit(0); + case PRINT_VERSION: + print_version(); + exit(0); + default: + putchar('\n'); + exit(-1); + break; + } + } +} + +static void init(void) +{ + static const char * const inames[] = { + BT_PROFILE_HANDSFREE_ID, + BT_PROFILE_ADVANCED_AUDIO_ID, + BT_PROFILE_AV_RC_ID, + BT_PROFILE_HEALTH_ID, + BT_PROFILE_HIDHOST_ID, + BT_PROFILE_PAN_ID, + BT_PROFILE_GATT_ID, + BT_PROFILE_SOCKETS_ID + }; + const struct method *m; + const char *argv[4]; + char init_audio[] = "audio init"; + char init_sco[] = "sco init"; + char init_bt[] = "bluetooth init"; + uint32_t i; + + process_line(init_audio); + process_line(init_sco); + process_line(init_bt); + + m = get_interface_method("bluetooth", "get_profile_interface"); + + for (i = 0; i < NELEM(inames); ++i) { + argv[2] = inames[i]; + m->func(3, argv); + } + + /* Init what is available to init */ + for (i = 2; i < NELEM(interfaces) - 1; ++i) { + m = get_interface_method(interfaces[i]->name, "init"); + if (m != NULL) + m->func(2, argv); + } +} + +int main(int argc, char **argv) +{ + struct stat rcstat; + + parse_command_line(argc, argv); + + terminal_setup(); + + if (!no_init) + init(); + + history_restore(".haltest_history"); + + fd_stack[fd_stack_pointer++] = 0; + /* Register command line handler */ + poll_register_fd(0, POLLIN, stdin_handler); + + if (stat(".haltestrc", &rcstat) == 0 && (rcstat.st_mode & S_IFREG) != 0) + process_file(".haltestrc"); + + poll_dispatch_loop(); + + return 0; +} diff -Nru bluez-4.101/android/client/history.c bluez-5.23/android/client/history.c --- bluez-4.101/android/client/history.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/history.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "history.h" + +/* Very simple history storage for easy usage of tool */ + +#define HISTORY_DEPTH 40 +#define LINE_SIZE 200 +static char lines[HISTORY_DEPTH][LINE_SIZE]; +static int last_line = 0; +static int history_size = 0; + +/* TODO: Storing history not implemented yet */ +void history_store(const char *filename) +{ +} + +/* Restoring history from file */ +void history_restore(const char *filename) +{ + char line[1000]; + FILE *f = fopen(filename, "rt"); + + if (f == NULL) + return; + + for (;;) { + if (fgets(line, 1000, f) != NULL) { + int l = strlen(line); + + while (l > 0 && isspace(line[--l])) + line[l] = 0; + + if (l > 0) + history_add_line(line); + } else + break; + } + + fclose(f); +} + +/* Add new line to history buffer */ +void history_add_line(const char *line) +{ + if (line == NULL || strlen(line) == 0) + return; + + if (strcmp(line, lines[last_line]) == 0) + return; + + last_line = (last_line + 1) % HISTORY_DEPTH; + strncpy(&lines[last_line][0], line, LINE_SIZE - 1); + if (history_size < HISTORY_DEPTH) + history_size++; +} + +/* + * Get n-th line from history + * 0 - means latest + * -1 - means oldest + * return -1 if there is no such line + */ +int history_get_line(int n, char *buf, int buf_size) +{ + if (n == -1) + n = history_size - 1; + + if (n >= history_size || buf_size == 0 || n < 0) + return -1; + + strncpy(buf, + &lines[(HISTORY_DEPTH + last_line - n) % HISTORY_DEPTH][0], + buf_size - 1); + buf[buf_size - 1] = 0; + + return n; +} diff -Nru bluez-4.101/android/client/history.h bluez-5.23/android/client/history.h --- bluez-4.101/android/client/history.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/history.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +void history_store(const char *filename); +void history_restore(const char *filename); +void history_add_line(const char *line); +int history_get_line(int n, char *buf, int buf_size); diff -Nru bluez-4.101/android/client/if-audio.c bluez-5.23/android/client/if-audio.c --- bluez-4.101/android/client/if-audio.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-audio.c 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "if-main.h" +#include "../hal-utils.h" + +audio_hw_device_t *if_audio = NULL; +static struct audio_stream_out *stream_out = NULL; + +static size_t buffer_size = 0; +static pthread_t play_thread = 0; +static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER; + +enum state { + STATE_STOPPED, + STATE_STOPPING, + STATE_PLAYING, + STATE_SUSPENDED, + STATE_MAX +}; + +SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)") + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_MONO), + DELEMENT(AUDIO_CHANNEL_OUT_STEREO), + DELEMENT(AUDIO_CHANNEL_OUT_QUAD), + DELEMENT(AUDIO_CHANNEL_OUT_SURROUND), + DELEMENT(AUDIO_CHANNEL_OUT_5POINT1), + DELEMENT(AUDIO_CHANNEL_OUT_7POINT1), + DELEMENT(AUDIO_CHANNEL_OUT_ALL), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), +ENDMAP + +SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)") + DELEMENT(AUDIO_FORMAT_DEFAULT), + DELEMENT(AUDIO_FORMAT_PCM), + DELEMENT(AUDIO_FORMAT_MP3), + DELEMENT(AUDIO_FORMAT_AMR_NB), + DELEMENT(AUDIO_FORMAT_AMR_WB), + DELEMENT(AUDIO_FORMAT_AAC), + DELEMENT(AUDIO_FORMAT_HE_AAC_V1), + DELEMENT(AUDIO_FORMAT_HE_AAC_V2), + DELEMENT(AUDIO_FORMAT_VORBIS), + DELEMENT(AUDIO_FORMAT_MAIN_MASK), + DELEMENT(AUDIO_FORMAT_SUB_MASK), + DELEMENT(AUDIO_FORMAT_PCM_16_BIT), + DELEMENT(AUDIO_FORMAT_PCM_8_BIT), + DELEMENT(AUDIO_FORMAT_PCM_32_BIT), + DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT), +ENDMAP + +static int current_state = STATE_STOPPED; + +#define SAMPLERATE 44100 +static short sample[SAMPLERATE]; +static uint16_t sample_pos; + +static void init_p(int argc, const char **argv) +{ + int err; + const hw_module_t *module; + audio_hw_device_t *device; + + err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, + AUDIO_HARDWARE_MODULE_ID_A2DP, &module); + if (err) { + haltest_error("hw_get_module_by_class returned %d\n", err); + return; + } + + err = audio_hw_device_open(module, &device); + if (err) { + haltest_error("audio_hw_device_open returned %d\n", err); + return; + } + + if_audio = device; +} + +static int feed_from_file(short *buffer, void *data) +{ + FILE *in = data; + return fread(buffer, buffer_size, 1, in); +} + +static int feed_from_generator(short *buffer, void *data) +{ + size_t i = 0; + float volume = 0.5; + float *freq = data; + float f = 1; + + if (freq) + f = *freq; + + /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/ + for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) { + if (sample_pos >= SAMPLERATE) + sample_pos = sample_pos % SAMPLERATE; + + /* Use the same sample for both channels */ + buffer[i++] = sample[sample_pos] * volume; + buffer[i++] = sample[sample_pos] * volume; + + sample_pos += f; + } + + return buffer_size; +} + +static void prepare_sample(void) +{ + int x; + double s; + + haltest_info("Preparing audio sample...\n"); + + for (x = 0; x < SAMPLERATE; x++) { + /* prepare sinusoidal 1Hz sample */ + s = (2.0 * 3.14159) * ((double)x / SAMPLERATE); + s = sin(s); + + /* remap <-1, 1> to signed 16bit PCM range */ + sample[x] = s * 32767; + } + + sample_pos = 0; +} + +static void *playback_thread(void *data) +{ + int (*filbuff_cb) (short*, void*); + short buffer[buffer_size / sizeof(short)]; + size_t len = 0; + ssize_t w_len = 0; + FILE *in = data; + void *cb_data = NULL; + float freq = 440.0; + + /* Use file or fall back to generator */ + if (in) { + filbuff_cb = feed_from_file; + cb_data = in; + } else { + prepare_sample(); + filbuff_cb = feed_from_generator; + cb_data = &freq; + } + + pthread_mutex_lock(&state_mutex); + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); + + do { + pthread_mutex_lock(&state_mutex); + + if (current_state == STATE_STOPPING) { + pthread_mutex_unlock(&state_mutex); + break; + } else if (current_state == STATE_SUSPENDED) { + pthread_mutex_unlock(&state_mutex); + usleep(500); + continue; + } + + pthread_mutex_unlock(&state_mutex); + + len = filbuff_cb(buffer, cb_data); + + pthread_mutex_lock(&outstream_mutex); + if (!stream_out) { + pthread_mutex_unlock(&outstream_mutex); + break; + } + + w_len = stream_out->write(stream_out, buffer, buffer_size); + pthread_mutex_unlock(&outstream_mutex); + } while (len && w_len > 0); + + if (in) + fclose(in); + + pthread_mutex_lock(&state_mutex); + current_state = STATE_STOPPED; + pthread_mutex_unlock(&state_mutex); + + haltest_info("Done playing.\n"); + + return NULL; +} + +static void play_p(int argc, const char **argv) +{ + const char *fname = NULL; + FILE *in = NULL; + + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_error("Invalid audio file path.\n"); + haltest_info("Using sound generator.\n"); + } else { + fname = argv[2]; + in = fopen(fname, "r"); + + if (in == NULL) { + haltest_error("Cannot open file: %s\n", fname); + return; + } + haltest_info("Playing file: %s\n", fname); + } + + if (buffer_size == 0) { + haltest_error("Invalid buffer size. Was stream_out opened?\n"); + goto fail; + } + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + haltest_error("Already playing or stream suspended!\n"); + pthread_mutex_unlock(&state_mutex); + goto fail; + } + pthread_mutex_unlock(&state_mutex); + + if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) { + haltest_error("Cannot create playback thread!\n"); + goto fail; + } + + return; +fail: + if (in) + fclose(in); +} + +static void stop_p(int argc, const char **argv) +{ + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) { + pthread_mutex_unlock(&state_mutex); + return; + } + + current_state = STATE_STOPPING; + pthread_mutex_unlock(&state_mutex); + + pthread_mutex_lock(&outstream_mutex); + stream_out->common.standby(&stream_out->common); + pthread_mutex_unlock(&outstream_mutex); +} + +static void open_output_stream_p(int argc, const char **argv) +{ + int err; + + RETURN_IF_NULL(if_audio); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_PLAYING) { + haltest_error("Already playing!\n"); + pthread_mutex_unlock(&state_mutex); + return; + } + pthread_mutex_unlock(&state_mutex); + + err = if_audio->open_output_stream(if_audio, + 0, + AUDIO_DEVICE_OUT_ALL_A2DP, + AUDIO_OUTPUT_FLAG_NONE, + NULL, + &stream_out); + if (err < 0) { + haltest_error("open output stream returned %d\n", err); + return; + } + + buffer_size = stream_out->common.get_buffer_size(&stream_out->common); + if (buffer_size == 0) + haltest_error("Invalid buffer size received!\n"); + else + haltest_info("Using buffer size: %zu\n", buffer_size); +} + +static void close_output_stream_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + stop_p(argc, argv); + + haltest_info("Waiting for playback thread...\n"); + pthread_join(play_thread, NULL); + + if_audio->close_output_stream(if_audio, stream_out); + + stream_out = NULL; + buffer_size = 0; +} + +static void cleanup_p(int argc, const char **argv) +{ + int err; + + RETURN_IF_NULL(if_audio); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + pthread_mutex_unlock(&state_mutex); + close_output_stream_p(0, NULL); + } else { + pthread_mutex_unlock(&state_mutex); + } + + err = audio_hw_device_close(if_audio); + if (err < 0) { + haltest_error("audio_hw_device_close returned %d\n", err); + return; + } + + if_audio = NULL; +} + +static void suspend_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_PLAYING) { + pthread_mutex_unlock(&state_mutex); + return; + } + current_state = STATE_SUSPENDED; + pthread_mutex_unlock(&state_mutex); + + pthread_mutex_lock(&outstream_mutex); + stream_out->common.standby(&stream_out->common); + pthread_mutex_unlock(&outstream_mutex); +} + +static void resume_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_SUSPENDED) + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); +} + +static void get_latency_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + haltest_info("Output audio stream latency: %d\n", + stream_out->get_latency(stream_out)); +} + +static void get_buffer_size_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + haltest_info("Current output buffer size: %zu\n", + stream_out->common.get_buffer_size(&stream_out->common)); +} + +static void get_channels_p(int argc, const char **argv) +{ + audio_channel_mask_t channels; + + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + channels = stream_out->common.get_channels(&stream_out->common); + + haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels)); +} + +static void get_format_p(int argc, const char **argv) +{ + audio_format_t format; + + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + format = stream_out->common.get_format(&stream_out->common); + + haltest_info("Format: %s\n", audio_format_t2str(format)); +} + +static void get_sample_rate_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + haltest_info("Current sample rate: %d\n", + stream_out->common.get_sample_rate(&stream_out->common)); +} + +static void get_parameters_p(int argc, const char **argv) +{ + const char *keystr; + + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_info("No keys given.\n"); + keystr = ""; + } else { + keystr = argv[2]; + } + + haltest_info("Current parameters: %s\n", + stream_out->common.get_parameters(&stream_out->common, + keystr)); +} + +static void set_parameters_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_error("No key=value; pairs given.\n"); + return; + } + + stream_out->common.set_parameters(&stream_out->common, argv[2]); +} + +static void set_sample_rate_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + RETURN_IF_NULL(stream_out); + + if (argc < 3) + return; + + stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2])); +} + +static void init_check_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio); + + haltest_info("Init check result: %d\n", if_audio->init_check(if_audio)); +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHOD(cleanup), + STD_METHOD(open_output_stream), + STD_METHOD(close_output_stream), + STD_METHODH(play, ""), + STD_METHOD(stop), + STD_METHOD(suspend), + STD_METHOD(resume), + STD_METHOD(get_latency), + STD_METHOD(get_buffer_size), + STD_METHOD(get_channels), + STD_METHOD(get_format), + STD_METHOD(get_sample_rate), + STD_METHODH(get_parameters, ""), + STD_METHODH(set_parameters, ""), + STD_METHODH(set_sample_rate, ""), + STD_METHOD(init_check), + END_METHOD +}; + +const struct interface audio_if = { + .name = "audio", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-av.c bluez-5.23/android/client/if-av.c --- bluez-4.101/android/client/if-av.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-av.c 2014-02-22 01:42:17.000000000 +0000 @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "if-main.h" +#include "../hal-utils.h" + +const btav_interface_t *if_av = NULL; + +SINTMAP(btav_connection_state_t, -1, "(unknown)") + DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTED), + DELEMENT(BTAV_CONNECTION_STATE_CONNECTING), + DELEMENT(BTAV_CONNECTION_STATE_CONNECTED), + DELEMENT(BTAV_CONNECTION_STATE_DISCONNECTING), +ENDMAP + +SINTMAP(btav_audio_state_t, -1, "(unknown)") + DELEMENT(BTAV_AUDIO_STATE_REMOTE_SUSPEND), + DELEMENT(BTAV_AUDIO_STATE_STOPPED), + DELEMENT(BTAV_AUDIO_STATE_STARTED), +ENDMAP + +static char last_addr[MAX_ADDR_STR_LEN]; + +static void connection_state(btav_connection_state_t state, + bt_bdaddr_t *bd_addr) +{ + haltest_info("%s: connection_state=%s remote_bd_addr=%s\n", __func__, + btav_connection_state_t2str(state), + bt_bdaddr_t2str(bd_addr, last_addr)); +} + +static void audio_state(btav_audio_state_t state, bt_bdaddr_t *bd_addr) +{ + haltest_info("%s: audio_state=%s remote_bd_addr=%s\n", __func__, + btav_audio_state_t2str(state), + bt_bdaddr_t2str(bd_addr, last_addr)); +} + +static btav_callbacks_t av_cbacks = { + .size = sizeof(av_cbacks), + .connection_state_cb = connection_state, + .audio_state_cb = audio_state +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_av); + + EXEC(if_av->init, &av_cbacks); +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_av); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_av->connect, &addr); +} + +/* disconnect */ + +static void disconnect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = last_addr; + *enum_func = enum_one_string; + } +} + +static void disconnect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_av); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_av->disconnect, &addr); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_av); + + EXECV(if_av->cleanup); + if_av = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(connect, ""), + STD_METHODCH(disconnect, ""), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface av_if = { + .name = "av", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-bt.c bluez-5.23/android/client/if-bt.c --- bluez-4.101/android/client/if-bt.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-bt.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,857 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "if-main.h" +#include "terminal.h" +#include "../hal-utils.h" + +const bt_interface_t *if_bluetooth; + +#define VERIFY_PROP_TYPE_ARG(n, typ) \ + do { \ + if (n < argc) \ + typ = str2btpropertytype(argv[n]); \ + else { \ + haltest_error("No property type specified\n"); \ + return;\ + } \ + } while (0) + +static bt_scan_mode_t str2btscanmode(const char *str) +{ + bt_scan_mode_t v = str2bt_scan_mode_t(str); + + if ((int) v != -1) + return v; + + haltest_warn("WARN: %s cannot convert %s\n", __func__, str); + return (bt_scan_mode_t) atoi(str); +} + +static bt_ssp_variant_t str2btsspvariant(const char *str) +{ + bt_ssp_variant_t v = str2bt_ssp_variant_t(str); + + if ((int) v != -1) + return v; + + haltest_warn("WARN: %s cannot convert %s\n", __func__, str); + return (bt_ssp_variant_t) atoi(str); +} + +static bt_property_type_t str2btpropertytype(const char *str) +{ + bt_property_type_t v = str2bt_property_type_t(str); + + if ((int) v != -1) + return v; + + haltest_warn("WARN: %s cannot convert %s\n", __func__, str); + return (bt_property_type_t) atoi(str); +} + +static void dump_properties(int num_properties, bt_property_t *properties) +{ + int i; + + for (i = 0; i < num_properties; i++) { + /* + * properities sometimes come unaligned hence memcp to + * aligned buffer + */ + bt_property_t prop; + memcpy(&prop, properties + i, sizeof(prop)); + + haltest_info("prop: %s\n", btproperty2str(&prop)); + } +} + +/* Cache for remote devices, stored in sorted array */ +static bt_bdaddr_t *remote_devices = NULL; +static int remote_devices_cnt = 0; +static int remote_devices_capacity = 0; + +/* Adds address to remote device set so it can be used in tab completion */ +void add_remote_device(const bt_bdaddr_t *addr) +{ + int i; + + if (remote_devices == NULL) { + remote_devices = malloc(4 * sizeof(bt_bdaddr_t)); + remote_devices_cnt = 0; + if (remote_devices == NULL) { + remote_devices_capacity = 0; + return; + } + + remote_devices_capacity = 4; + } + + /* Array is sorted, search for right place */ + for (i = 0; i < remote_devices_cnt; ++i) { + int res = memcmp(&remote_devices[i], addr, sizeof(*addr)); + + if (res == 0) + return; /* Already added */ + else if (res > 0) + break; + } + + /* Realloc space if needed */ + if (remote_devices_cnt >= remote_devices_capacity) { + remote_devices_capacity *= 2; + remote_devices = realloc(remote_devices, sizeof(bt_bdaddr_t) * + remote_devices_capacity); + if (remote_devices == NULL) { + remote_devices_capacity = 0; + remote_devices_cnt = 0; + return; + } + } + + if (i < remote_devices_cnt) + memmove(remote_devices + i + 1, remote_devices + i, + (remote_devices_cnt - i) * sizeof(bt_bdaddr_t)); + remote_devices[i] = *addr; + remote_devices_cnt++; +} + +const char *enum_devices(void *v, int i) +{ + static char buf[MAX_ADDR_STR_LEN]; + + if (i >= remote_devices_cnt) + return NULL; + + bt_bdaddr_t2str(&remote_devices[i], buf); + return buf; +} + +static void add_remote_device_from_props(int num_properties, + const bt_property_t *properties) +{ + int i; + + for (i = 0; i < num_properties; i++) { + /* + * properities sometimes come unaligned hence memcp to + * aligned buffer + */ + bt_property_t property; + + memcpy(&property, properties + i, sizeof(property)); + if (property.type == BT_PROPERTY_BDADDR) + add_remote_device((bt_bdaddr_t *) property.val); + } +} + +static void adapter_state_changed_cb(bt_state_t state) +{ + haltest_info("%s: state=%s\n", __func__, bt_state_t2str(state)); +} + +static void adapter_properties_cb(bt_status_t status, int num_properties, + bt_property_t *properties) +{ + haltest_info("%s: status=%s num_properties=%d\n", __func__, + bt_status_t2str(status), num_properties); + + dump_properties(num_properties, properties); +} + +static void remote_device_properties_cb(bt_status_t status, + bt_bdaddr_t *bd_addr, + int num_properties, + bt_property_t *properties) +{ + haltest_info("%s: status=%s bd_addr=%s num_properties=%d\n", __func__, + bt_status_t2str(status), bdaddr2str(bd_addr), + num_properties); + + add_remote_device(bd_addr); + + dump_properties(num_properties, properties); +} + +static void device_found_cb(int num_properties, bt_property_t *properties) +{ + haltest_info("%s: num_properties=%d\n", __func__, num_properties); + + add_remote_device_from_props(num_properties, properties); + + dump_properties(num_properties, properties); +} + +static void discovery_state_changed_cb(bt_discovery_state_t state) +{ + haltest_info("%s: state=%s\n", __func__, + bt_discovery_state_t2str(state)); +} + +/* + * Buffer for remote addres that came from one of bind request. + * It's stored for command completion. + */ +static char last_remote_addr[MAX_ADDR_STR_LEN]; +static bt_ssp_variant_t last_ssp_variant = (bt_ssp_variant_t) -1; + +static bt_bdaddr_t pin_request_addr; +static void pin_request_answer(char *reply) +{ + bt_pin_code_t pin; + int accept = 0; + int pin_len = strlen(reply); + + if (pin_len > 0) { + accept = 1; + if (pin_len > 16) + pin_len = 16; + memcpy(&pin.pin, reply, pin_len); + } + + EXEC(if_bluetooth->pin_reply, &pin_request_addr, accept, pin_len, &pin); +} + +static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, + uint32_t cod) +{ + /* Store for command completion */ + bt_bdaddr_t2str(remote_bd_addr, last_remote_addr); + pin_request_addr = *remote_bd_addr; + + haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x\n", __func__, + last_remote_addr, bd_name->name, cod); + terminal_prompt_for("Enter pin: ", pin_request_answer); +} + +/* Variables to store information from ssp_request_cb used for ssp_reply */ +static bt_bdaddr_t ssp_request_addr; +static bt_ssp_variant_t ssp_request_variant; +static uint32_t ssp_request_pask_key; + +/* Called when user hit enter on prompt for confirmation */ +static void ssp_request_yes_no_answer(char *reply) +{ + int accept = *reply == 0 || *reply == 'y' || *reply == 'Y'; + + if_bluetooth->ssp_reply(&ssp_request_addr, ssp_request_variant, accept, + ssp_request_pask_key); +} + +static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *bd_name, + uint32_t cod, bt_ssp_variant_t pairing_variant, + uint32_t pass_key) +{ + static char prompt[50]; + + /* Store for command completion */ + bt_bdaddr_t2str(remote_bd_addr, last_remote_addr); + last_ssp_variant = pairing_variant; + + haltest_info("%s: remote_bd_addr=%s bd_name=%s cod=%06x pairing_variant=%s pass_key=%d\n", + __func__, last_remote_addr, bd_name->name, cod, + bt_ssp_variant_t2str(pairing_variant), pass_key); + + switch (pairing_variant) { + case BT_SSP_VARIANT_PASSKEY_CONFIRMATION: + sprintf(prompt, "Does other device show %d [Y/n] ?", pass_key); + + ssp_request_addr = *remote_bd_addr; + ssp_request_variant = pairing_variant; + ssp_request_pask_key = pass_key; + + terminal_prompt_for(prompt, ssp_request_yes_no_answer); + break; + case BT_SSP_VARIANT_CONSENT: + sprintf(prompt, "Consent pairing [Y/n] ?"); + + ssp_request_addr = *remote_bd_addr; + ssp_request_variant = pairing_variant; + ssp_request_pask_key = 0; + + terminal_prompt_for(prompt, ssp_request_yes_no_answer); + break; + default: + haltest_info("Not automatically handled\n"); + break; + } +} + +static void bond_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, + bt_bond_state_t state) +{ + haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__, + bt_status_t2str(status), bdaddr2str(remote_bd_addr), + bt_bond_state_t2str(state)); +} + +static void acl_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, + bt_acl_state_t state) +{ + haltest_info("%s: status=%s remote_bd_addr=%s state=%s\n", __func__, + bt_status_t2str(status), bdaddr2str(remote_bd_addr), + bt_acl_state_t2str(state)); +} + +static void thread_evt_cb(bt_cb_thread_evt evt) +{ + haltest_info("%s: evt=%s\n", __func__, bt_cb_thread_evt2str(evt)); +} + +static void dut_mode_recv_cb(uint16_t opcode, uint8_t *buf, uint8_t len) +{ + haltest_info("%s\n", __func__); +} + +static void le_test_mode_cb(bt_status_t status, uint16_t num_packets) +{ + haltest_info("%s %s %d\n", __func__, bt_status_t2str(status), + num_packets); +} + +static bt_callbacks_t bt_callbacks = { + .size = sizeof(bt_callbacks), + .adapter_state_changed_cb = adapter_state_changed_cb, + .adapter_properties_cb = adapter_properties_cb, + .remote_device_properties_cb = remote_device_properties_cb, + .device_found_cb = device_found_cb, + .discovery_state_changed_cb = discovery_state_changed_cb, + .pin_request_cb = pin_request_cb, + .ssp_request_cb = ssp_request_cb, + .bond_state_changed_cb = bond_state_changed_cb, + .acl_state_changed_cb = acl_state_changed_cb, + .thread_evt_cb = thread_evt_cb, + .dut_mode_recv_cb = dut_mode_recv_cb, + .le_test_mode_cb = le_test_mode_cb +}; + +static void init_p(int argc, const char **argv) +{ + int err; + const hw_module_t *module; + hw_device_t *device; + + err = hw_get_module(BT_HARDWARE_MODULE_ID, &module); + if (err) { + haltest_error("he_get_module returned %d\n", err); + return; + } + + err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device); + if (err) { + haltest_error("module->methods->open returned %d\n", err); + return; + } + + if_bluetooth = + ((bluetooth_device_t *) device)->get_bluetooth_interface(); + if (!if_bluetooth) { + haltest_error("get_bluetooth_interface returned NULL\n"); + return; + } + + EXEC(if_bluetooth->init, &bt_callbacks); +} + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXECV(if_bluetooth->cleanup); + + if_bluetooth = NULL; +} + +static void enable_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->enable); +} + +static void disable_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->disable); +} + +static void get_adapter_properties_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->get_adapter_properties); +} + +static void get_adapter_property_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(bt_property_type_t); + *enum_func = enum_defines; + } +} + +static void get_adapter_property_p(int argc, const char **argv) +{ + int type; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_PROP_TYPE_ARG(2, type); + + EXEC(if_bluetooth->get_adapter_property, type); +} + +static const char * const names[] = { + "BT_PROPERTY_BDNAME", + "BT_PROPERTY_ADAPTER_SCAN_MODE", + "BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT", + NULL +}; + +static void set_adapter_property_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = (void *) names; + *enum_func = enum_strings; + } else if (argc == 4) { + if (0 == strcmp(argv[2], "BT_PROPERTY_ADAPTER_SCAN_MODE")) { + *user = TYPE_ENUM(bt_scan_mode_t); + *enum_func = enum_defines; + } + } +} + +static void set_adapter_property_p(int argc, const char **argv) +{ + bt_property_t property; + bt_scan_mode_t mode; + int timeout; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_PROP_TYPE_ARG(2, property.type); + + if (argc <= 3) { + haltest_error("No property value specified\n"); + return; + } + switch (property.type) { + case BT_PROPERTY_BDNAME: + property.len = strlen(argv[3]) + 1; + property.val = (char *) argv[3]; + break; + + case BT_PROPERTY_ADAPTER_SCAN_MODE: + mode = str2btscanmode(argv[3]); + property.len = sizeof(bt_scan_mode_t); + property.val = &mode; + break; + + case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: + timeout = atoi(argv[3]); + property.val = &timeout; + property.len = sizeof(timeout); + break; + + default: + haltest_error("Invalid property %s\n", argv[3]); + return; + } + + EXEC(if_bluetooth->set_adapter_property, &property); +} + +/* This function is to be used for completion methods that need only address */ +static void complete_addr_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } +} + +/* Just addres to complete, use complete_addr_c */ +#define get_remote_device_properties_c complete_addr_c + +static void get_remote_device_properties_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->get_remote_device_properties, &addr); +} + +static void get_remote_device_property_c(int argc, const char **argv, + enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } else if (argc == 4) { + *user = TYPE_ENUM(bt_property_type_t); + *enum_func = enum_defines; + } +} + +static void get_remote_device_property_p(int argc, const char **argv) +{ + bt_property_type_t type; + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + VERIFY_PROP_TYPE_ARG(3, type); + + EXEC(if_bluetooth->get_remote_device_property, &addr, type); +} + +/* + * Same completion as for get_remote_device_property_c can be used for + * set_remote_device_property_c. No need to create separate function. + */ +#define set_remote_device_property_c get_remote_device_property_c + +static void set_remote_device_property_p(int argc, const char **argv) +{ + bt_property_t property; + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + VERIFY_PROP_TYPE_ARG(3, property.type); + + switch (property.type) { + case BT_PROPERTY_REMOTE_FRIENDLY_NAME: + property.len = strlen(argv[4]); + property.val = (char *) argv[4]; + break; + default: + return; + } + + EXEC(if_bluetooth->set_remote_device_property, &addr, &property); +} + +/* For now uuid is not autocompleted. Use routine for complete_addr_c */ +#define get_remote_service_record_c complete_addr_c + +static void get_remote_service_record_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bt_uuid_t uuid; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + if (argc <= 3) { + haltest_error("No uuid specified\n"); + return; + } + + str2bt_uuid_t(argv[3], &uuid); + + EXEC(if_bluetooth->get_remote_service_record, &addr, &uuid); +} + +/* Just addres to complete, use complete_addr_c */ +#define get_remote_services_c complete_addr_c + +static void get_remote_services_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->get_remote_services, &addr); +} + +static void start_discovery_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->start_discovery); +} + +static void cancel_discovery_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_bluetooth); + + EXEC(if_bluetooth->cancel_discovery); +} + +/* Just addres to complete, use complete_addr_c */ +#define create_bond_c complete_addr_c + +static void create_bond_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->create_bond, &addr); +} + +/* Just addres to complete, use complete_addr_c */ +#define remove_bond_c complete_addr_c + +static void remove_bond_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->remove_bond, &addr); +} + +/* Just addres to complete, use complete_addr_c */ +#define cancel_bond_c complete_addr_c + +static void cancel_bond_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_bluetooth->cancel_bond, &addr); +} + +static void pin_reply_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + static const char *const completions[] = { last_remote_addr, NULL }; + + if (argc == 3) { + *user = (void *) completions; + *enum_func = enum_strings; + } +} + +static void pin_reply_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bt_pin_code_t pin; + int pin_len = 0; + int accept = 0; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + if (argc > 3) { + accept = 1; + pin_len = strlen(argv[3]); + memcpy(pin.pin, argv[3], pin_len); + } + + EXEC(if_bluetooth->pin_reply, &addr, accept, pin_len, &pin); +} + +static void ssp_reply_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = last_remote_addr; + *enum_func = enum_one_string; + } else if (argc == 5) { + *user = "1"; + *enum_func = enum_one_string; + } else if (argc == 4) { + if (-1 != (int) last_ssp_variant) { + *user = (void *) bt_ssp_variant_t2str(last_ssp_variant); + *enum_func = enum_one_string; + } else { + *user = TYPE_ENUM(bt_ssp_variant_t); + *enum_func = enum_defines; + } + } +} + +static void ssp_reply_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bt_ssp_variant_t var; + int accept; + int passkey; + + RETURN_IF_NULL(if_bluetooth); + VERIFY_ADDR_ARG(2, &addr); + + if (argc < 4) { + haltest_error("No ssp variant specified\n"); + return; + } + + var = str2btsspvariant(argv[3]); + if (argc < 5) { + haltest_error("No accept value specified\n"); + return; + } + + accept = atoi(argv[4]); + passkey = 0; + + if (accept && var == BT_SSP_VARIANT_PASSKEY_ENTRY && argc >= 5) + passkey = atoi(argv[4]); + + EXEC(if_bluetooth->ssp_reply, &addr, var, accept, passkey); +} + +static void get_profile_interface_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + static const char *const profile_ids[] = { + BT_PROFILE_HANDSFREE_ID, + BT_PROFILE_ADVANCED_AUDIO_ID, + BT_PROFILE_HEALTH_ID, + BT_PROFILE_SOCKETS_ID, + BT_PROFILE_HIDHOST_ID, + BT_PROFILE_PAN_ID, + BT_PROFILE_GATT_ID, + BT_PROFILE_AV_RC_ID, + NULL + }; + + if (argc == 3) { + *user = (void *) profile_ids; + *enum_func = enum_strings; + } +} + +static void get_profile_interface_p(int argc, const char **argv) +{ + const char *id; + const void **pif = NULL; + + RETURN_IF_NULL(if_bluetooth); + if (argc <= 2) { + haltest_error("No interface specified\n"); + return; + } + + id = argv[2]; + + if (strcmp(BT_PROFILE_HANDSFREE_ID, id) == 0) + pif = (const void **) &if_hf; + else if (strcmp(BT_PROFILE_ADVANCED_AUDIO_ID, id) == 0) + pif = (const void **) &if_av; + else if (strcmp(BT_PROFILE_HEALTH_ID, id) == 0) + pif = (const void **) &if_hl; + else if (strcmp(BT_PROFILE_SOCKETS_ID, id) == 0) + pif = (const void **) &if_sock; + else if (strcmp(BT_PROFILE_HIDHOST_ID, id) == 0) + pif = (const void **) &if_hh; + else if (strcmp(BT_PROFILE_PAN_ID, id) == 0) + pif = (const void **) &if_pan; + else if (strcmp(BT_PROFILE_AV_RC_ID, id) == 0) + pif = (const void **) &if_rc; + else if (strcmp(BT_PROFILE_GATT_ID, id) == 0) + pif = (const void **) &if_gatt; + else + haltest_error("%s is not correct for get_profile_interface\n", + id); + + if (pif != NULL) { + *pif = if_bluetooth->get_profile_interface(id); + haltest_info("get_profile_interface(%s) : %p\n", id, *pif); + } +} + +static void dut_mode_configure_p(int argc, const char **argv) +{ + uint8_t mode; + + RETURN_IF_NULL(if_bluetooth); + + if (argc <= 2) { + haltest_error("No dut mode specified\n"); + return; + } + + mode = strtol(argv[2], NULL, 0); + + EXEC(if_bluetooth->dut_mode_configure, mode); +} + +static void dut_mode_send_p(int argc, const char **argv) +{ + haltest_error("not implemented\n"); +} + +static void le_test_mode_p(int argc, const char **argv) +{ + haltest_error("not implemented\n"); +} + +static void config_hci_snoop_log_p(int argc, const char **argv) +{ + uint8_t mode; + + RETURN_IF_NULL(if_bluetooth); + + if (argc <= 2) { + haltest_error("No mode specified\n"); + return; + } + + mode = strtol(argv[2], NULL, 0); + + EXEC(if_bluetooth->config_hci_snoop_log, mode); +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHOD(cleanup), + STD_METHOD(enable), + STD_METHOD(disable), + STD_METHOD(get_adapter_properties), + STD_METHODCH(get_adapter_property, ""), + STD_METHODCH(set_adapter_property, " "), + STD_METHODCH(get_remote_device_properties, ""), + STD_METHODCH(get_remote_device_property, " "), + STD_METHODCH(set_remote_device_property, + " "), + STD_METHODCH(get_remote_service_record, " "), + STD_METHODCH(get_remote_services, ""), + STD_METHOD(start_discovery), + STD_METHOD(cancel_discovery), + STD_METHODCH(create_bond, ""), + STD_METHODCH(remove_bond, ""), + STD_METHODCH(cancel_bond, ""), + STD_METHODCH(pin_reply, "
[]"), + STD_METHODCH(ssp_reply, "
1|0 []"), + STD_METHODCH(get_profile_interface, ""), + STD_METHODH(dut_mode_configure, ""), + STD_METHOD(dut_mode_send), + STD_METHOD(le_test_mode), + STD_METHODH(config_hci_snoop_log, ""), + END_METHOD +}; + +const struct interface bluetooth_if = { + .name = "bluetooth", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-gatt.c bluez-5.23/android/client/if-gatt.c --- bluez-4.101/android/client/if-gatt.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-gatt.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,1872 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "../hal-utils.h" +#include "if-main.h" + +const btgatt_interface_t *if_gatt = NULL; + +/* + * In version 19 some callback were changed. + * btgatt_char_id_t -> btgatt_gatt_id_t + * bt_uuid_t -> btgatt_gatt_id_t + */ +#define str2btgatt_descr_id_t str2btgatt_gatt_id_t +#define btgatt_descr_id_t2str btgatt_gatt_id_t2str +#define btgatt_descr_id_t btgatt_gatt_id_t + +#define MAX_CHAR_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11) +#define MAX_SRVC_ID_STR_LEN (MAX_UUID_STR_LEN + 3 + 11 + 1 + 11) +/* How man characters print from binary objects (arbitrary) */ +#define MAX_HEX_VAL_STR_LEN 100 +#define MAX_NOTIFY_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \ + + MAX_ADDR_STR_LEN + MAX_HEX_VAL_STR_LEN + 60) +#define MAX_READ_PARAMS_STR_LEN (MAX_SRVC_ID_STR_LEN + MAX_CHAR_ID_STR_LEN \ + + MAX_UUID_STR_LEN + MAX_HEX_VAL_STR_LEN + 80) + +/* Hex arguments must have "0x" or "0X" prefix */ +#define VERIFY_INT_ARG(n, v, err) \ + do { \ + if (n < argc) \ + v = strtol(argv[n], NULL, 0); \ + else { \ + haltest_error(err); \ + return;\ + } \ + } while (0) + +#define VERIFY_HEX_ARG(n, v, err) \ + do { \ + if (n < argc) \ + v = strtol(argv[n], NULL, 16); \ + else { \ + haltest_error(err); \ + return;\ + } \ + } while (0) + +/* Helper macros to verify arguments of methods */ +#define VERIFY_CLIENT_IF(n, v) VERIFY_INT_ARG(n, v, "No client_if specified\n") +#define VERIFY_SERVER_IF(n, v) VERIFY_INT_ARG(n, v, "No server_if specified\n") +#define VERIFY_CONN_ID(n, v) VERIFY_INT_ARG(n, v, "No conn_if specified\n") +#define VERIFY_TRANS_ID(n, v) VERIFY_INT_ARG(n, v, "No trans_id specified\n") +#define VERIFY_STATUS(n, v) VERIFY_INT_ARG(n, v, "No status specified\n") +#define VERIFY_OFFSET(n, v) VERIFY_INT_ARG(n, v, "No offset specified\n") +#define VERIFY_TEST_ARG(n, v) VERIFY_INT_ARG(n, v, "No test arg specified\n") +#define VERIFY_HANDLE(n, v) VERIFY_HEX_ARG(n, v, "No "#v" specified\n") +#define VERIFY_SERVICE_HANDLE(n, v) VERIFY_HANDLE(n, v) + +#define VERIFY_UUID(n, v) \ + do { \ + if (n < argc) \ + gatt_str2bt_uuid_t(argv[n], -1, v); \ + else { \ + haltest_error("No uuid specified\n"); \ + return;\ + } \ + } while (0) + +#define VERIFY_SRVC_ID(n, v) \ + do { \ + if (n < argc) \ + str2btgatt_srvc_id_t(argv[n], v); \ + else { \ + haltest_error("No srvc_id specified\n"); \ + return;\ + } \ + } while (0) + +#define VERIFY_CHAR_ID(n, v) \ + do { \ + if (n < argc) \ + str2btgatt_gatt_id_t(argv[n], v); \ + else { \ + haltest_error("No char_id specified\n"); \ + return;\ + } \ + } while (0) + +#define VERIFY_DESCR_ID(n, v) \ + do { \ + if (n < argc) \ + str2btgatt_descr_id_t(argv[n], v); \ + else { \ + haltest_error("No descr_id specified\n"); \ + return;\ + } \ + } while (0) + +#define GET_VERIFY_HEX_STRING(n, v, l) \ + do { \ + if (n[0] != '0' || (n[1] != 'X' && n[1] != 'x')) { \ + haltest_error("Value must be hex string\n"); \ + return; \ + } \ + l = fill_buffer(n + 2, (uint8_t *) v, sizeof(v)); \ + } while (0) + +/* Gatt uses little endian uuid */ +static const char GATT_BASE_UUID[] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * converts gatt uuid to string + * buf should be at least 39 bytes + * + * This function formats 16, 32 and 128 bits uuid + * + * returns string representation of uuid + */ +static char *gatt_uuid_t2str(const bt_uuid_t *uuid, char *buf) +{ + int shift = 0; + int i = 16; + int limit = 0; + int j = 0; + + /* for bluetooth uuid only 32 bits */ + if (0 == memcmp(&uuid->uu, &GATT_BASE_UUID, + sizeof(bt_uuid_t) - 4)) { + limit = 12; + /* make it 16 bits */ + if (uuid->uu[15] == 0 && uuid->uu[14] == 0) + i = 14; + } + + while (i-- > limit) { + if (i == 11 || i == 9 || i == 7 || i == 5) { + buf[j * 2 + shift] = '-'; + shift++; + } + + sprintf(buf + j * 2 + shift, "%02x", uuid->uu[i]); + ++j; + } + + return buf; +} + +/* + * Tries to convert hex string of given size into out buffer. + * Output buffer is little endian. + */ +static void scan_field(const char *str, int len, uint8_t *out, int out_size) +{ + int i; + + memset(out, 0, out_size); + if (out_size * 2 > len + 1) + out_size = (len + 1) / 2; + + for (i = 0; i < out_size && len > 0; ++i) { + len -= 2; + if (len >= 0) + sscanf(str + len, "%02hhx", &out[i]); + else + sscanf(str, "%1hhx", &out[i]); + } +} + +/* Like strchr but with upper limit instead of 0 terminated string */ +static const char *strchrlimit(const char *p, const char *e, int c) +{ + while (p < e && *p != (char) c) + ++p; + + return p < e ? p : NULL; +} + +/* + * converts string to uuid + * it accepts uuid in following forms: + * 123 + * 0000123 + * 0000123-0014-1234-0000-000056789abc + * 0000123001412340000000056789abc + * 123-14-1234-0-56789abc + */ +static void gatt_str2bt_uuid_t(const char *str, int len, bt_uuid_t *uuid) +{ + int dash_cnt = 0; + int dashes[6] = {-1}; /* indexes of '-' or \0 */ + static uint8_t filed_offset[] = { 16, 12, 10, 8, 6, 0 }; + const char *p = str; + const char *e; + int i; + + e = str + ((len >= 0) ? len : (int) strlen(str)); + + while (p != NULL && dash_cnt < 5) { + const char *f = strchrlimit(p, e, '-'); + + if (f != NULL) + dashes[++dash_cnt] = f++ - str; + p = f; + } + + /* get index of \0 to dashes table */ + if (dash_cnt < 5) + dashes[++dash_cnt] = e - str; + + memcpy(uuid, GATT_BASE_UUID, sizeof(bt_uuid_t)); + + /* whole uuid in one string without dashes */ + if (dash_cnt == 1 && dashes[1] > 8) { + if (dashes[1] > 32) + dashes[1] = 32; + scan_field(str, dashes[1], + &uuid->uu[16 - (dashes[1] + 1) / 2], + (dashes[1] + 1) / 2); + } else { + for (i = 0; i < dash_cnt; ++i) { + scan_field(str + dashes[i] + 1, + dashes[i + 1] - dashes[i] - 1, + &uuid->uu[filed_offset[i + 1]], + filed_offset[i] - filed_offset[i + 1]); + } + } +} + +/* char_id formating function */ +static char *btgatt_gatt_id_t2str(const btgatt_gatt_id_t *char_id, char *buf) +{ + char uuid_buf[MAX_UUID_STR_LEN]; + + sprintf(buf, "{%s,%d}", gatt_uuid_t2str(&char_id->uuid, uuid_buf), + char_id->inst_id); + return buf; +} + +/* Parse btgatt_gatt_id_t */ +static void str2btgatt_gatt_id_t(const char *buf, btgatt_gatt_id_t *char_id) +{ + const char *e; + + memcpy(&char_id->uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t)); + char_id->inst_id = 0; + + if (*buf == '{') + buf++; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + + gatt_str2bt_uuid_t(buf, e - buf, &char_id->uuid); + if (*e == ',') { + buf = e + 1; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + if (buf < e) + char_id->inst_id = atoi(buf); + } +} + +/* service_id formating function */ +static char *btgatt_srvc_id_t2str(const btgatt_srvc_id_t *srvc_id, char *buf) +{ + char uuid_buf[MAX_UUID_STR_LEN]; + + sprintf(buf, "{%s,%d,%d}", gatt_uuid_t2str(&srvc_id->id.uuid, uuid_buf), + srvc_id->id.inst_id, srvc_id->is_primary); + return buf; +} + +/* Parse btgatt_srvc_id_t */ +static void str2btgatt_srvc_id_t(const char *buf, btgatt_srvc_id_t *srvc_id) +{ + const char *e; + + memcpy(&srvc_id->id.uuid, &GATT_BASE_UUID, sizeof(bt_uuid_t)); + srvc_id->id.inst_id = 0; + srvc_id->is_primary = 1; + + if (*buf == '{') + buf++; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + + gatt_str2bt_uuid_t(buf, e - buf, &srvc_id->id.uuid); + if (*e == ',') { + buf = e + 1; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + if (buf < e) + srvc_id->id.inst_id = atoi(buf); + } + + if (*e == ',') { + buf = e + 1; + e = strpbrk(buf, " ,}"); + if (e == NULL) + e = buf + strlen(buf); + if (buf < e) + srvc_id->is_primary = atoi(buf); + } +} + +/* Converts array of uint8_t to string representation */ +static char *array2str(const uint8_t *v, int size, char *buf, int out_size) +{ + int limit = size; + int i; + + if (out_size > 0) { + *buf = '\0'; + if (size >= 2 * out_size) + limit = (out_size - 2) / 2; + + for (i = 0; i < limit; ++i) + sprintf(buf + 2 * i, "%02x", v[i]); + + /* output buffer not enough to hold whole field fill with ...*/ + if (limit < size) + sprintf(buf + 2 * i, "..."); + } + + return buf; +} + +/* Converts btgatt_notify_params_t to string */ +static char *btgatt_notify_params_t2str(const btgatt_notify_params_t *data, + char *buf) +{ + char addr[MAX_ADDR_STR_LEN]; + char srvc_id[MAX_SRVC_ID_STR_LEN]; + char char_id[MAX_CHAR_ID_STR_LEN]; + char value[MAX_HEX_VAL_STR_LEN]; + + sprintf(buf, "{bda=%s, srvc_id=%s, char_id=%s, val=%s, is_notify=%u}", + bt_bdaddr_t2str(&data->bda, addr), + btgatt_srvc_id_t2str(&data->srvc_id, srvc_id), + btgatt_gatt_id_t2str(&data->char_id, char_id), + array2str(data->value, data->len, value, sizeof(value)), + data->is_notify); + return buf; +} + +static char *btgatt_unformatted_value_t2str(const btgatt_unformatted_value_t *v, + char *buf, int size) +{ + return array2str(v->value, v->len, buf, size); +} + +static char *btgatt_read_params_t2str(const btgatt_read_params_t *data, + char *buf) +{ + char srvc_id[MAX_SRVC_ID_STR_LEN]; + char char_id[MAX_CHAR_ID_STR_LEN]; + char descr_id[MAX_UUID_STR_LEN]; + char value[MAX_HEX_VAL_STR_LEN]; + + sprintf(buf, "{srvc_id=%s, char_id=%s, descr_id=%s, val=%s value_type=%d, status=%d}", + btgatt_srvc_id_t2str(&data->srvc_id, srvc_id), + btgatt_gatt_id_t2str(&data->char_id, char_id), + btgatt_descr_id_t2str(&data->descr_id, descr_id), + btgatt_unformatted_value_t2str(&data->value, value, 100), + data->value_type, data->status); + return buf; +} + +/* BT-GATT Client callbacks. */ + +/* Cache client_if and conn_id for tab completion */ +static char client_if_str[20]; +static char conn_id_str[20]; +/* Cache address for tab completion */ +static char last_addr[MAX_ADDR_STR_LEN]; + +/* Callback invoked in response to register_client */ +static void gattc_register_client_cb(int status, int client_if, + bt_uuid_t *app_uuid) +{ + char buf[MAX_UUID_STR_LEN]; + + snprintf(client_if_str, sizeof(client_if_str), "%d", client_if); + + haltest_info("%s: status=%d client_if=%d app_uuid=%s\n", __func__, + status, client_if, + gatt_uuid_t2str(app_uuid, buf)); +} + +/* Callback for scan results */ +static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bda=%s rssi=%d adv_data=%p\n", __func__, + bt_bdaddr_t2str(bda, buf), rssi, adv_data); +} + +/* GATT open callback invoked in response to open */ +static void gattc_connect_cb(int conn_id, int status, int client_if, + bt_bdaddr_t *bda) +{ + haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n", + __func__, conn_id, status, client_if, + bt_bdaddr_t2str(bda, last_addr)); +} + +/* Callback invoked in response to close */ +static void gattc_disconnect_cb(int conn_id, int status, int client_if, + bt_bdaddr_t *bda) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d, client_if=%d bda=%s\n", + __func__, conn_id, status, client_if, + bt_bdaddr_t2str(bda, buf)); +} + +/* + * Invoked in response to search_service when the GATT service search + * has been completed. + */ +static void gattc_search_complete_cb(int conn_id, int status) +{ + haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); +} + +/* Reports GATT services on a remote device */ +static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id) +{ + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d srvc_id=%s\n", __func__, conn_id, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf)); +} + +/* GATT characteristic enumeration result callback */ +static void gattc_get_characteristic_cb(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + int char_prop) +{ + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + char char_id_buf[MAX_CHAR_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, char_prop=0x%x\n", + __func__, conn_id, status, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), + btgatt_gatt_id_t2str(char_id, char_id_buf), char_prop); + + /* enumerate next characteristic */ + if (status == 0) + EXEC(if_gatt->client->get_characteristic, conn_id, srvc_id, + char_id); +} + +/* GATT descriptor enumeration result callback */ +static void gattc_get_descriptor_cb(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_descr_id_t *descr_id) +{ + char buf[MAX_UUID_STR_LEN]; + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + char char_id_buf[MAX_CHAR_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d srvc_id=%s char_id=%s, descr_id=%s\n", + __func__, conn_id, status, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), + btgatt_gatt_id_t2str(char_id, char_id_buf), + btgatt_descr_id_t2str(descr_id, buf)); + + if (status == 0) + EXEC(if_gatt->client->get_descriptor, conn_id, srvc_id, char_id, + descr_id); +} + +/* GATT included service enumeration result callback */ +static void gattc_get_included_service_cb(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, + btgatt_srvc_id_t *incl_srvc_id) +{ + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + char incl_srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d srvc_id=%s incl_srvc_id=%s)\n", + __func__, conn_id, status, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), + btgatt_srvc_id_t2str(incl_srvc_id, incl_srvc_id_buf)); + + if (status == 0) + EXEC(if_gatt->client->get_included_service, conn_id, srvc_id, + incl_srvc_id); +} + +/* Callback invoked in response to [de]register_for_notification */ +static void gattc_register_for_notification_cb(int conn_id, int registered, + int status, + btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id) +{ + char srvc_id_buf[MAX_SRVC_ID_STR_LEN]; + char char_id_buf[MAX_CHAR_ID_STR_LEN]; + + haltest_info("%s: conn_id=%d registered=%d status=%d srvc_id=%s char_id=%s\n", + __func__, conn_id, registered, status, + btgatt_srvc_id_t2str(srvc_id, srvc_id_buf), + btgatt_gatt_id_t2str(char_id, char_id_buf)); +} + +/* + * Remote device notification callback, invoked when a remote device sends + * a notification or indication that a client has registered for. + */ +static void gattc_notify_cb(int conn_id, btgatt_notify_params_t *p_data) +{ + char buf[MAX_NOTIFY_PARAMS_STR_LEN]; + + haltest_info("%s: conn_id=%d data=%s\n", __func__, conn_id, + btgatt_notify_params_t2str(p_data, buf)); +} + +/* Reports result of a GATT read operation */ +static void gattc_read_characteristic_cb(int conn_id, int status, + btgatt_read_params_t *p_data) +{ + char buf[MAX_READ_PARAMS_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id, + status, btgatt_read_params_t2str(p_data, buf)); +} + +/* GATT write characteristic operation callback */ +static void gattc_write_characteristic_cb(int conn_id, int status, + btgatt_write_params_t *p_data) +{ + haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); +} + +/* GATT execute prepared write callback */ +static void gattc_execute_write_cb(int conn_id, int status) +{ + haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); +} + +/* Callback invoked in response to read_descriptor */ +static void gattc_read_descriptor_cb(int conn_id, int status, + btgatt_read_params_t *p_data) +{ + char buf[MAX_READ_PARAMS_STR_LEN]; + + haltest_info("%s: conn_id=%d status=%d data=%s\n", __func__, conn_id, + status, btgatt_read_params_t2str(p_data, buf)); +} + +/* Callback invoked in response to write_descriptor */ +static void gattc_write_descriptor_cb(int conn_id, int status, + btgatt_write_params_t *p_data) +{ + haltest_info("%s: conn_id=%d status=%d\n", __func__, conn_id, status); +} + +/* Callback triggered in response to read_remote_rssi */ +static void gattc_read_remote_rssi_cb(int client_if, bt_bdaddr_t *bda, int rssi, + int status) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: client_if=%d bda=%s rssi=%d satus=%d\n", __func__, + client_if, bt_bdaddr_t2str(bda, buf), rssi, status); +} + +/* Callback invoked in response to listen */ +static void gattc_listen_cb(int status, int client_if) +{ + haltest_info("%s: client_if=%d status=%d\n", __func__, client_if, + status); +} + +static const btgatt_client_callbacks_t btgatt_client_callbacks = { + .register_client_cb = gattc_register_client_cb, + .scan_result_cb = gattc_scan_result_cb, + .open_cb = gattc_connect_cb, + .close_cb = gattc_disconnect_cb, + .search_complete_cb = gattc_search_complete_cb, + .search_result_cb = gattc_search_result_cb, + .get_characteristic_cb = gattc_get_characteristic_cb, + .get_descriptor_cb = gattc_get_descriptor_cb, + .get_included_service_cb = gattc_get_included_service_cb, + .register_for_notification_cb = gattc_register_for_notification_cb, + .notify_cb = gattc_notify_cb, + .read_characteristic_cb = gattc_read_characteristic_cb, + .write_characteristic_cb = gattc_write_characteristic_cb, + .read_descriptor_cb = gattc_read_descriptor_cb, + .write_descriptor_cb = gattc_write_descriptor_cb, + .execute_write_cb = gattc_execute_write_cb, + .read_remote_rssi_cb = gattc_read_remote_rssi_cb, + .listen_cb = gattc_listen_cb, +}; + +/* BT-GATT Server callbacks */ + +/* Cache server_if and conn_id for tab completion */ +static char server_if_str[20]; + +/* Callback invoked in response to register_server */ +static void gatts_register_server_cb(int status, int server_if, + bt_uuid_t *app_uuid) +{ + char buf[MAX_UUID_STR_LEN]; + + haltest_info("%s: status=%d server_if=%d app_uuid=%s\n", __func__, + status, server_if, gatt_uuid_t2str(app_uuid, buf)); +} + +/* + * Callback indicating that a remote device has connected + * or been disconnected + */ +static void gatts_connection_cb(int conn_id, int server_if, int connected, + bt_bdaddr_t *bda) +{ + haltest_info("%s: conn_id=%d server_if=%d connected=%d bda=%s\n", + __func__, conn_id, server_if, connected, + bt_bdaddr_t2str(bda, last_addr)); + snprintf(conn_id_str, sizeof(conn_id_str), "%d", conn_id); +} + +/* Callback invoked in response to create_service */ +static void gatts_service_added_cb(int status, int server_if, + btgatt_srvc_id_t *srvc_id, int srvc_handle) +{ + char buf[MAX_SRVC_ID_STR_LEN]; + + snprintf(server_if_str, sizeof(server_if_str), "%d", server_if); + + haltest_info("%s: status=%d server_if=%d srvc_id=%s handle=0x%x\n", + __func__, status, server_if, + btgatt_srvc_id_t2str(srvc_id, buf), srvc_handle); +} + +/* Callback indicating that an included service has been added to a service */ +static void gatts_included_service_added_cb(int status, int server_if, + int srvc_handle, + int incl_srvc_handle) +{ + haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x inc_srvc_handle=0x%x\n", + __func__, status, server_if, + srvc_handle, incl_srvc_handle); +} + +/* Callback invoked when a characteristic has been added to a service */ +static void gatts_characteristic_added_cb(int status, int server_if, + bt_uuid_t *uuid, + int srvc_handle, + int char_handle) +{ + char buf[MAX_SRVC_ID_STR_LEN]; + + haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x char_handle=0x%x\n", + __func__, status, server_if, gatt_uuid_t2str(uuid, buf), + srvc_handle, char_handle); +} + +/* Callback invoked when a descriptor has been added to a characteristic */ +static void gatts_descriptor_added_cb(int status, int server_if, + bt_uuid_t *uuid, int srvc_handle, + int descr_handle) +{ + char buf[MAX_SRVC_ID_STR_LEN]; + + haltest_info("%s: status=%d server_if=%d uuid=%s srvc_handle=0x%x descr_handle=0x%x\n", + __func__, status, server_if, gatt_uuid_t2str(uuid, buf), + srvc_handle, descr_handle); +} + +/* Callback invoked in response to start_service */ +static void gatts_service_started_cb(int status, int server_if, int srvc_handle) +{ + haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n", + __func__, status, server_if, srvc_handle); +} + +/* Callback invoked in response to stop_service */ +static void gatts_service_stopped_cb(int status, int server_if, int srvc_handle) +{ + haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n", + __func__, status, server_if, srvc_handle); +} + +/* Callback triggered when a service has been deleted */ +static void gatts_service_deleted_cb(int status, int server_if, int srvc_handle) +{ + haltest_info("%s: status=%d server_if=%d srvc_handle=0x%x\n", + __func__, status, server_if, srvc_handle); +} + +/* + * Callback invoked when a remote device has requested to read a characteristic + * or descriptor. The application must respond by calling send_response + */ +static void gatts_request_read_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int offset, + bool is_long) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d is_long=%d\n", + __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), + attr_handle, offset, is_long); +} + +/* + * Callback invoked when a remote device has requested to write to a + * characteristic or descriptor. + */ +static void gatts_request_write_cb(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int offset, int length, + bool need_rsp, bool is_prep, + uint8_t *value) +{ + char buf[MAX_ADDR_STR_LEN]; + char valbuf[100]; + + haltest_info("%s: conn_id=%d trans_id=%d bda=%s attr_handle=0x%x offset=%d length=%d need_rsp=%d is_prep=%d value=%s\n", + __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), + attr_handle, offset, length, need_rsp, is_prep, + array2str(value, length, valbuf, sizeof(valbuf))); +} + +/* Callback invoked when a previously prepared write is to be executed */ +static void gatts_request_exec_write_cb(int conn_id, int trans_id, + bt_bdaddr_t *bda, int exec_write) +{ + char buf[MAX_ADDR_STR_LEN]; + + haltest_info("%s: conn_id=%d trans_id=%d bda=%s exec_write=%d\n", + __func__, conn_id, trans_id, bt_bdaddr_t2str(bda, buf), + exec_write); +} + +/* + * Callback triggered in response to send_response if the remote device + * sends a confirmation. + */ +static void gatts_response_confirmation_cb(int status, int handle) +{ + haltest_info("%s: status=%d handle=0x%x\n", __func__, status, handle); +} + +static const btgatt_server_callbacks_t btgatt_server_callbacks = { + .register_server_cb = gatts_register_server_cb, + .connection_cb = gatts_connection_cb, + .service_added_cb = gatts_service_added_cb, + .included_service_added_cb = gatts_included_service_added_cb, + .characteristic_added_cb = gatts_characteristic_added_cb, + .descriptor_added_cb = gatts_descriptor_added_cb, + .service_started_cb = gatts_service_started_cb, + .service_stopped_cb = gatts_service_stopped_cb, + .service_deleted_cb = gatts_service_deleted_cb, + .request_read_cb = gatts_request_read_cb, + .request_write_cb = gatts_request_write_cb, + .request_exec_write_cb = gatts_request_exec_write_cb, + .response_confirmation_cb = gatts_response_confirmation_cb +}; + +static const btgatt_callbacks_t gatt_cbacks = { + .size = sizeof(gatt_cbacks), + .client = &btgatt_client_callbacks, + .server = &btgatt_server_callbacks +}; + +/* + * convert hex string to uint8_t array + */ +static int fill_buffer(const char *str, uint8_t *out, int out_size) +{ + int str_len; + int i, j; + char c; + uint8_t b; + + str_len = strlen(str); + + for (i = 0, j = 0; i < out_size && j < str_len; i++, j++) { + c = str[j]; + + if (c >= 'a' && c <= 'f') + c += 'A' - 'a'; + + if (c >= '0' && c <= '9') + b = c - '0'; + else if (c >= 'A' && c <= 'F') + b = 10 + c - 'A'; + else + return 0; + + j++; + + c = str[j]; + + if (c >= 'a' && c <= 'f') + c += 'A' - 'a'; + + if (c >= '0' && c <= '9') + b = b * 16 + c - '0'; + else if (c >= 'A' && c <= 'F') + b = b * 16 + 10 + c - 'A'; + else + return 0; + + out[i] = b; + } + + return i; +} + +/* gatt client methods */ + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_gatt); + + EXEC(if_gatt->init, &gatt_cbacks); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_gatt); + + EXECV(if_gatt->cleanup); + + if_gatt = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface gatt_if = { + .name = "gatt", + .methods = methods +}; + +/* register_client */ + +static void register_client_p(int argc, const char **argv) +{ + bt_uuid_t uuid; + + RETURN_IF_NULL(if_gatt); + + /* uuid */ + if (argc <= 2) + gatt_str2bt_uuid_t("babe4bed", -1, &uuid); + else + gatt_str2bt_uuid_t(argv[2], -1, &uuid); + + EXEC(if_gatt->client->register_client, &uuid); +} + +/* unregister_client */ + +static void unregister_client_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } +} + +static void unregister_client_p(int argc, const char **argv) +{ + int client_if; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + + EXEC(if_gatt->client->unregister_client, client_if); +} + +/* scan */ + +/* Same completion as unregister for now, start stop is not auto completed */ +#define scan_c unregister_client_c + +static void scan_p(int argc, const char **argv) +{ + int client_if; + int start = 1; + + RETURN_IF_NULL(if_gatt); + + VERIFY_CLIENT_IF(2, client_if); + + /* start */ + if (argc >= 4) + start = atoi(argv[3]); + + EXEC(if_gatt->client->scan, client_if, start); +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = NULL; + *enum_func = enum_devices; + } +} + +static void connect_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + int is_direct = 1; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + + /* is_direct */ + if (argc > 4) + is_direct = atoi(argv[4]); + + EXEC(if_gatt->client->connect, client_if, &bd_addr, is_direct); +} + +/* disconnect */ + +static void disconnect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = last_addr; + *enum_func = enum_one_string; + } else if (argc == 5) { + *user = conn_id_str; + *enum_func = enum_one_string; + } +} + +static void disconnect_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + int conn_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_CONN_ID(4, conn_id); + + EXEC(if_gatt->client->disconnect, client_if, &bd_addr, conn_id); +} + +/* listen */ + +/* Same completion as unregister for now, start stop is not auto completed */ +#define listen_c unregister_client_c + +static void listen_p(int argc, const char **argv) +{ + int client_if; + int start = 1; + + RETURN_IF_NULL(if_gatt); + + VERIFY_CLIENT_IF(2, client_if); + + /* start */ + if (argc >= 4) + start = atoi(argv[3]); + + EXEC(if_gatt->client->listen, client_if, start); +} + +/* refresh */ + +static void refresh_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *enum_func = enum_devices; + } +} + +static void refresh_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + + EXEC(if_gatt->client->refresh, client_if, &bd_addr); +} + +/* search_service */ + +static void search_service_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = conn_id_str; + *enum_func = enum_one_string; + } +} + +static void search_service_p(int argc, const char **argv) +{ + int conn_id; + + RETURN_IF_NULL(if_gatt); + + VERIFY_CONN_ID(2, conn_id); + + /* uuid */ + if (argc <= 3) { + EXEC(if_gatt->client->search_service, conn_id, NULL); + + } else { + bt_uuid_t filter_uuid; + + gatt_str2bt_uuid_t(argv[3], -1, &filter_uuid); + EXEC(if_gatt->client->search_service, conn_id, &filter_uuid); + } +} + +/* get_included_service */ + +static void get_included_service_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = conn_id_str; + *enum_func = enum_one_string; + } +} + +static void get_included_service_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + + EXEC(if_gatt->client->get_included_service, conn_id, &srvc_id, NULL); +} + +/* get_characteristic */ + +/* Same completion as get_included_service_c */ +#define get_characteristic_c get_included_service_c + +static void get_characteristic_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + + EXEC(if_gatt->client->get_characteristic, conn_id, &srvc_id, NULL); +} + +/* get_descriptor */ + +/* Same completion as get_included_service_c */ +#define get_descriptor_c get_included_service_c + +static void get_descriptor_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + + EXEC(if_gatt->client->get_descriptor, conn_id, &srvc_id, &char_id, + NULL); +} + +/* read_characteristic */ + +/* Same completion as get_included_service_c */ +#define read_characteristic_c get_included_service_c + +static void read_characteristic_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + int auth_req = 0; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + + /* auth_req */ + if (argc > 5) + auth_req = atoi(argv[5]); + + EXEC(if_gatt->client->read_characteristic, conn_id, &srvc_id, &char_id, + auth_req); +} + +/* write_characteristic */ + +static void write_characteristic_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + /* + * This should be from tGATT_WRITE_TYPE but it's burried + * inside bluedroid guts + */ + static const char *wrtypes[] = { "1", "2", "3", NULL }; + + if (argc == 3) { + *user = conn_id_str; + *enum_func = enum_one_string; + } else if (argc == 6) { + *user = wrtypes; + *enum_func = enum_strings; + } +} + +static void write_characteristic_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + int write_type; + int len; + int auth_req = 0; + uint8_t value[100]; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + + /* write type */ + if (argc <= 5) { + haltest_error("No write type specified\n"); + return; + } + write_type = atoi(argv[5]); + + /* value */ + if (argc <= 6) { + haltest_error("No value specified\n"); + return; + } + + GET_VERIFY_HEX_STRING(argv[6], value, len); + + /* auth_req */ + if (argc > 7) + auth_req = atoi(argv[7]); + + EXEC(if_gatt->client->write_characteristic, conn_id, &srvc_id, &char_id, + write_type, len, auth_req, (char *) value); +} + +/* read_descriptor */ + +/* Same completion as get_included_service_c */ +#define read_descriptor_c get_included_service_c + +static void read_descriptor_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_descr_id_t descr_id; + int auth_req = 0; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + VERIFY_DESCR_ID(5, &descr_id); + + /* auth_req */ + if (argc > 6) + auth_req = atoi(argv[6]); + + EXEC(if_gatt->client->read_descriptor, conn_id, &srvc_id, &char_id, + &descr_id, auth_req); +} + +/* write_descriptor */ + +static void write_descriptor_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + /* + * This should be from tGATT_WRITE_TYPE but it's burried + * inside bluedroid guts + */ + static const char *wrtypes[] = { "1", "2", "3", NULL }; + + if (argc == 3) { + *user = conn_id_str; + *enum_func = enum_one_string; + } else if (argc == 7) { + *user = wrtypes; + *enum_func = enum_strings; + } +} + +static void write_descriptor_p(int argc, const char **argv) +{ + int conn_id; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_descr_id_t descr_id; + int write_type; + int len; + int auth_req = 0; + uint8_t value[200] = {0}; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + VERIFY_SRVC_ID(3, &srvc_id); + VERIFY_CHAR_ID(4, &char_id); + VERIFY_DESCR_ID(5, &descr_id); + + /* write type */ + if (argc <= 6) { + haltest_error("No write type specified\n"); + return; + } + write_type = atoi(argv[6]); + + /* value */ + if (argc <= 7) { + haltest_error("No value specified\n"); + return; + } + + /* len in chars */ + if (strncmp(argv[7], "0X", 2) && strncmp(argv[7], "0x", 2)) { + haltest_error("Value must be hex string"); + return; + } + + len = fill_buffer(argv[7] + 2, value, sizeof(value)); + + /* auth_req */ + if (argc > 8) + auth_req = atoi(argv[8]); + + EXEC(if_gatt->client->write_descriptor, conn_id, &srvc_id, &char_id, + &descr_id, write_type, len, auth_req, (char *) value); +} + +/* execute_write */ + +/* Same completion as search_service */ +#define execute_write_c search_service_c + +static void execute_write_p(int argc, const char **argv) +{ + int conn_id; + int execute; + + RETURN_IF_NULL(if_gatt); + VERIFY_CONN_ID(2, conn_id); + + /* execute */ + if (argc <= 3) { + haltest_error("No execute specified\n"); + return; + } + execute = atoi(argv[3]); + + EXEC(if_gatt->client->execute_write, conn_id, execute); +} + +/* register_for_notification */ + +static void register_for_notification_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = last_addr; + *enum_func = enum_one_string; + } +} + +static void register_for_notification_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_SRVC_ID(4, &srvc_id); + VERIFY_CHAR_ID(5, &char_id); + + EXEC(if_gatt->client->register_for_notification, client_if, &bd_addr, + &srvc_id, &char_id); +} + +/* deregister_for_notification */ + +/* Same completion as search_service */ +#define deregister_for_notification_c register_for_notification_c + +static void deregister_for_notification_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_SRVC_ID(4, &srvc_id); + VERIFY_CHAR_ID(5, &char_id); + + EXEC(if_gatt->client->deregister_for_notification, client_if, &bd_addr, + &srvc_id, &char_id); +} + +/* read_remote_rssi */ + +static void read_remote_rssi_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = client_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *enum_func = enum_devices; + } +} + +static void read_remote_rssi_p(int argc, const char **argv) +{ + int client_if; + bt_bdaddr_t bd_addr; + + RETURN_IF_NULL(if_gatt); + VERIFY_CLIENT_IF(2, client_if); + VERIFY_ADDR_ARG(3, &bd_addr); + + EXEC(if_gatt->client->read_remote_rssi, client_if, &bd_addr); +} + +/* get_device_type */ + +static void get_device_type_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) + *enum_func = enum_devices; +} + +static void get_device_type_p(int argc, const char **argv) +{ + bt_bdaddr_t bd_addr; + int dev_type; + + RETURN_IF_NULL(if_gatt); + VERIFY_ADDR_ARG(2, &bd_addr); + + dev_type = if_gatt->client->get_device_type(&bd_addr); + haltest_info("%s: %d\n", "get_device_type", dev_type); +} + +/* test_command */ + +static void test_command_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 4) + *enum_func = enum_devices; +} + +static void test_command_p(int argc, const char **argv) +{ + int command; + int i; + bt_bdaddr_t bd_addr; + bt_uuid_t uuid; + btgatt_test_params_t params = { + .bda1 = &bd_addr, + .uuid1 = &uuid + }; + uint16_t *u = ¶ms.u1; + + RETURN_IF_NULL(if_gatt); + + /* command */ + if (argc <= 2) { + haltest_error("No command specified\n"); + return; + } + command = atoi(argv[2]); + + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_UUID(4, &uuid); + + for (i = 5; i < argc; i++) + VERIFY_TEST_ARG(i, *u++); + + EXEC(if_gatt->client->test_command, command, ¶ms); +} + +static struct method client_methods[] = { + STD_METHODH(register_client, "[]"), + STD_METHODCH(unregister_client, ""), + STD_METHODCH(scan, " [1|0]"), + STD_METHODCH(connect, " []"), + STD_METHODCH(disconnect, " "), + STD_METHODCH(refresh, " "), + STD_METHODCH(search_service, " []"), + STD_METHODCH(get_included_service, " "), + STD_METHODCH(get_characteristic, " "), + STD_METHODCH(get_descriptor, " "), + STD_METHODCH(read_characteristic, + " []"), + STD_METHODCH(write_characteristic, + " []"), + STD_METHODCH(read_descriptor, + " []"), + STD_METHODCH(write_descriptor, + " []"), + STD_METHODCH(execute_write, " "), + STD_METHODCH(register_for_notification, + " "), + STD_METHODCH(deregister_for_notification, + " "), + STD_METHODCH(read_remote_rssi, " "), + STD_METHODCH(get_device_type, ""), + STD_METHODCH(test_command, + " [u1] [u2] [u3] [u4] [u5]"), + STD_METHODCH(listen, " [1|0]"), + END_METHOD +}; + +const struct interface gatt_client_if = { + .name = "gattc", + .methods = client_methods +}; + +/* gatt server methods */ + +/* register_server */ + +static void gatts_register_server_p(int argc, const char *argv[]) +{ + bt_uuid_t uuid; + + RETURN_IF_NULL(if_gatt); + + /* uuid */ + if (argc <= 2) + gatt_str2bt_uuid_t("bed4babe", -1, &uuid); + else + gatt_str2bt_uuid_t(argv[2], -1, &uuid); + + EXEC(if_gatt->server->register_server, &uuid); +} + +/* unregister_server */ + +static void gatts_unregister_server_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = server_if_str; + *enum_func = enum_one_string; + } +} + +static void gatts_unregister_server_p(int argc, const char *argv[]) +{ + int server_if; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + + EXEC(if_gatt->server->unregister_server, server_if); +} + +/* connect */ + +static void gatts_connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = server_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = NULL; + *enum_func = enum_devices; + } +} + +static void gatts_connect_p(int argc, const char *argv[]) +{ + int server_if; + bt_bdaddr_t bd_addr; + int is_direct = 1; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_ADDR_ARG(3, &bd_addr); + + /* is_direct */ + if (argc > 4) + is_direct = atoi(argv[4]); + + EXEC(if_gatt->server->connect, server_if, &bd_addr, is_direct); +} + +/* disconnect */ + +static void gatts_disconnect_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = server_if_str; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = last_addr; + *enum_func = enum_one_string; + } else if (argc == 5) { + *user = conn_id_str; + *enum_func = enum_one_string; + } +} + +static void gatts_disconnect_p(int argc, const char *argv[]) +{ + int server_if; + bt_bdaddr_t bd_addr; + int conn_id; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_ADDR_ARG(3, &bd_addr); + VERIFY_CONN_ID(4, conn_id); + + EXEC(if_gatt->server->disconnect, server_if, &bd_addr, conn_id); +} + +/* add_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_add_service_c gatts_unregister_server_c + +static void gatts_add_service_p(int argc, const char *argv[]) +{ + int server_if; + btgatt_srvc_id_t srvc_id; + int num_handles; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SRVC_ID(3, &srvc_id); + + /* num handles */ + if (argc <= 4) { + haltest_error("No num_handles specified\n"); + return; + } + num_handles = atoi(argv[4]); + + EXEC(if_gatt->server->add_service, server_if, &srvc_id, num_handles); +} + +/* add_included_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_add_included_service_c gatts_unregister_server_c + +static void gatts_add_included_service_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + int included_handle; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + VERIFY_HANDLE(4, included_handle); + + EXEC(if_gatt->server->add_included_service, server_if, service_handle, + included_handle); +} + +/* add_characteristic */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_add_characteristic_c gatts_unregister_server_c + +static void gatts_add_characteristic_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + int properties; + int permissions; + bt_uuid_t uuid; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + VERIFY_UUID(4, &uuid); + + /* properties */ + if (argc <= 5) { + haltest_error("No properties specified\n"); + return; + } + properties = atoi(argv[5]); + + /* permissions */ + if (argc <= 6) { + haltest_error("No permissions specified\n"); + return; + } + permissions = atoi(argv[6]); + + EXEC(if_gatt->server->add_characteristic, server_if, service_handle, + &uuid, properties, permissions); +} + +/* add_descriptor */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_add_descriptor_c gatts_unregister_server_c + +static void gatts_add_descriptor_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + int permissions; + bt_uuid_t uuid; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + VERIFY_UUID(4, &uuid); + + /* permissions */ + if (argc <= 5) { + haltest_error("No permissions specified\n"); + return; + } + permissions = atoi(argv[5]); + + EXEC(if_gatt->server->add_descriptor, server_if, service_handle, &uuid, + permissions); +} + +/* start_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_start_service_c gatts_unregister_server_c + +static void gatts_start_service_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + int transport; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + + /* transport */ + if (argc <= 4) { + haltest_error("No transport specified\n"); + return; + } + transport = atoi(argv[4]); + + EXEC(if_gatt->server->start_service, server_if, service_handle, + transport); +} + +/* stop_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_stop_service_c gatts_unregister_server_c + +static void gatts_stop_service_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + + EXEC(if_gatt->server->stop_service, server_if, service_handle); +} + +/* delete_service */ + +/* Same completion as gatts_unregister_server_c */ +#define gatts_delete_service_c gatts_unregister_server_c + +static void gatts_delete_service_p(int argc, const char *argv[]) +{ + int server_if; + int service_handle; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_SERVICE_HANDLE(3, service_handle); + + EXEC(if_gatt->server->delete_service, server_if, service_handle); +} + +/* send_indication */ + +static void gatts_send_indication_p(int argc, const char *argv[]) +{ + int server_if; + int attr_handle; + int conn_id; + int confirm; + char data[200]; + int len = 0; + + RETURN_IF_NULL(if_gatt); + VERIFY_SERVER_IF(2, server_if); + VERIFY_HANDLE(3, attr_handle); + VERIFY_CONN_ID(4, conn_id); + + /* confirm */ + if (argc <= 5) { + haltest_error("No transport specified\n"); + return; + } + confirm = atoi(argv[5]); + + if (argc > 6) + GET_VERIFY_HEX_STRING(argv[6], data, len); + + EXEC(if_gatt->server->send_indication, server_if, attr_handle, conn_id, + len, confirm, data); +} + +/* send_response */ + +static void gatts_send_response_p(int argc, const char *argv[]) +{ + int conn_id; + int trans_id; + int status; + btgatt_response_t data; + + memset(&data, 0, sizeof(data)); + + RETURN_IF_NULL(if_gatt); + + VERIFY_CONN_ID(2, conn_id); + VERIFY_TRANS_ID(3, trans_id); + VERIFY_STATUS(4, status); + VERIFY_HANDLE(5, data.attr_value.handle); + VERIFY_OFFSET(6, data.attr_value.offset); + + data.attr_value.auth_req = 0; + data.attr_value.len = 0; + + if (argc > 7) { + GET_VERIFY_HEX_STRING(argv[7], data.attr_value.value, + data.attr_value.len); + + if (data.attr_value.len == 0) { + haltest_error("Failed to parse response value"); + return; + } + } + + haltest_info("conn_id %d, trans_id %d, status %d", conn_id, trans_id, + status); + + EXEC(if_gatt->server->send_response, conn_id, trans_id, status, &data); +} + +#define GATTS_METHODH(n, h) METHOD(#n, gatts_##n##_p, NULL, h) +#define GATTS_METHODCH(n, h) METHOD(#n, gatts_##n##_p, gatts_##n##_c, h) + +static struct method server_methods[] = { + GATTS_METHODH(register_server, "[]"), + GATTS_METHODCH(unregister_server, ""), + GATTS_METHODCH(connect, " []"), + GATTS_METHODCH(disconnect, " "), + GATTS_METHODCH(add_service, " "), + GATTS_METHODCH(add_included_service, + " "), + GATTS_METHODCH(add_characteristic, + " "), + GATTS_METHODCH(add_descriptor, + " "), + GATTS_METHODCH(start_service, + " "), + GATTS_METHODCH(stop_service, " "), + GATTS_METHODCH(delete_service, " "), + GATTS_METHODH(send_indication, + " []"), + GATTS_METHODH(send_response, + " []"), + END_METHOD +}; + +const struct interface gatt_server_if = { + .name = "gatts", + .methods = server_methods +}; diff -Nru bluez-4.101/android/client/if-hf.c bluez-5.23/android/client/if-hf.c --- bluez-4.101/android/client/if-hf.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-hf.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,787 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "if-main.h" +#include "../hal-utils.h" + +const bthf_interface_t *if_hf = NULL; + +SINTMAP(bthf_at_response_t, -1, "(unknown)") + DELEMENT(BTHF_AT_RESPONSE_ERROR), + DELEMENT(BTHF_AT_RESPONSE_OK), +ENDMAP + +SINTMAP(bthf_connection_state_t, -1, "(unknown)") + DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTED), + DELEMENT(BTHF_CONNECTION_STATE_CONNECTING), + DELEMENT(BTHF_CONNECTION_STATE_CONNECTED), + DELEMENT(BTHF_CONNECTION_STATE_SLC_CONNECTED), + DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTING), +ENDMAP + +SINTMAP(bthf_audio_state_t, -1, "(unknown)") + DELEMENT(BTHF_AUDIO_STATE_DISCONNECTED), + DELEMENT(BTHF_AUDIO_STATE_CONNECTING), + DELEMENT(BTHF_AUDIO_STATE_CONNECTED), + DELEMENT(BTHF_AUDIO_STATE_DISCONNECTING), +ENDMAP + +SINTMAP(bthf_vr_state_t, -1, "(unknown)") + DELEMENT(BTHF_VR_STATE_STOPPED), + DELEMENT(BTHF_VR_STATE_STARTED), +ENDMAP + +SINTMAP(bthf_volume_type_t, -1, "(unknown)") + DELEMENT(BTHF_VOLUME_TYPE_SPK), + DELEMENT(BTHF_VOLUME_TYPE_MIC), +ENDMAP + +SINTMAP(bthf_nrec_t, -1, "(unknown)") + DELEMENT(BTHF_NREC_STOP), + DELEMENT(BTHF_NREC_START), +ENDMAP + +SINTMAP(bthf_chld_type_t, -1, "(unknown)") + DELEMENT(BTHF_CHLD_TYPE_RELEASEHELD), + DELEMENT(BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD), + DELEMENT(BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD), + DELEMENT(BTHF_CHLD_TYPE_ADDHELDTOCONF), +ENDMAP + +/* Network Status */ +SINTMAP(bthf_network_state_t, -1, "(unknown)") + DELEMENT(BTHF_NETWORK_STATE_NOT_AVAILABLE), + DELEMENT(BTHF_NETWORK_STATE_AVAILABLE), +ENDMAP + +/* Service type */ +SINTMAP(bthf_service_type_t, -1, "(unknown)") + DELEMENT(BTHF_SERVICE_TYPE_HOME), + DELEMENT(BTHF_SERVICE_TYPE_ROAMING), +ENDMAP + +SINTMAP(bthf_call_state_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_STATE_ACTIVE), + DELEMENT(BTHF_CALL_STATE_HELD), + DELEMENT(BTHF_CALL_STATE_DIALING), + DELEMENT(BTHF_CALL_STATE_ALERTING), + DELEMENT(BTHF_CALL_STATE_INCOMING), + DELEMENT(BTHF_CALL_STATE_WAITING), + DELEMENT(BTHF_CALL_STATE_IDLE), +ENDMAP + +SINTMAP(bthf_call_direction_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_DIRECTION_OUTGOING), + DELEMENT(BTHF_CALL_DIRECTION_INCOMING), +ENDMAP + +SINTMAP(bthf_call_mode_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_TYPE_VOICE), + DELEMENT(BTHF_CALL_TYPE_DATA), + DELEMENT(BTHF_CALL_TYPE_FAX), +ENDMAP + +SINTMAP(bthf_call_mpty_type_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_MPTY_TYPE_SINGLE), + DELEMENT(BTHF_CALL_MPTY_TYPE_MULTI), +ENDMAP + +SINTMAP(bthf_call_addrtype_t, -1, "(unknown)") + DELEMENT(BTHF_CALL_ADDRTYPE_UNKNOWN), + DELEMENT(BTHF_CALL_ADDRTYPE_INTERNATIONAL), +ENDMAP + +/* Callbacks */ + +static char last_addr[MAX_ADDR_STR_LEN]; + +/* + * Callback for connection state change. + * state will have one of the values from BtHfConnectionState + */ +static void connection_state_cb(bthf_connection_state_t state, + bt_bdaddr_t *bd_addr) +{ + haltest_info("%s: state=%s bd_addr=%s\n", __func__, + bthf_connection_state_t2str(state), + bt_bdaddr_t2str(bd_addr, last_addr)); +} + +/* + * Callback for audio connection state change. + * state will have one of the values from BtHfAudioState + */ +static void audio_state_cb(bthf_audio_state_t state, bt_bdaddr_t *bd_addr) +{ + haltest_info("%s: state=%s bd_addr=%s\n", __func__, + bthf_audio_state_t2str(state), + bt_bdaddr_t2str(bd_addr, last_addr)); +} + +/* + * Callback for VR connection state change. + * state will have one of the values from BtHfVRState + */ +static void vr_cmd_cb(bthf_vr_state_t state) +{ + haltest_info("%s: state=%s\n", __func__, bthf_vr_state_t2str(state)); +} + +/* Callback for answer incoming call (ATA) */ +static void answer_call_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* Callback for disconnect call (AT+CHUP) */ +static void hangup_call_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* + * Callback for disconnect call (AT+CHUP) + * type will denote Speaker/Mic gain (BtHfVolumeControl). + */ +static void volume_cmd_cb(bthf_volume_type_t type, int volume) +{ + haltest_info("%s: type=%s volume=%d\n", __func__, + bthf_volume_type_t2str(type), volume); +} + +/* + * Callback for dialing an outgoing call + * If number is NULL, redial + */ +static void dial_call_cmd_cb(char *number) +{ + haltest_info("%s: number=%s\n", __func__, number); +} + +/* + * Callback for sending DTMF tones + * tone contains the dtmf character to be sent + */ +static void dtmf_cmd_cb(char tone) +{ + haltest_info("%s: tone=%d\n", __func__, tone); +} + +/* + * Callback for enabling/disabling noise reduction/echo cancellation + * value will be 1 to enable, 0 to disable + */ +static void nrec_cmd_cb(bthf_nrec_t nrec) +{ + haltest_info("%s: nrec=%s\n", __func__, bthf_nrec_t2str(nrec)); +} + +/* + * Callback for call hold handling (AT+CHLD) + * value will contain the call hold command (0, 1, 2, 3) + */ +static void chld_cmd_cb(bthf_chld_type_t chld) +{ + haltest_info("%s: chld=%s\n", __func__, bthf_chld_type_t2str(chld)); +} + +/* Callback for CNUM (subscriber number) */ +static void cnum_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* Callback for indicators (CIND) */ +static void cind_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* Callback for operator selection (COPS) */ +static void cops_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* Callback for call list (AT+CLCC) */ +static void clcc_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +/* + * Callback for unknown AT command recd from HF + * at_string will contain the unparsed AT string + */ +static void unknown_at_cmd_cb(char *at_string) +{ + haltest_info("%s: at_string=%s\n", __func__, at_string); +} + +/* Callback for keypressed (HSP) event. */ +static void key_pressed_cmd_cb(void) +{ + haltest_info("%s\n", __func__); +} + +static bthf_callbacks_t hf_cbacks = { + + .size = sizeof(hf_cbacks), + .connection_state_cb = connection_state_cb, + .audio_state_cb = audio_state_cb, + .vr_cmd_cb = vr_cmd_cb, + .answer_call_cmd_cb = answer_call_cmd_cb, + .hangup_call_cmd_cb = hangup_call_cmd_cb, + .volume_cmd_cb = volume_cmd_cb, + .dial_call_cmd_cb = dial_call_cmd_cb, + .dtmf_cmd_cb = dtmf_cmd_cb, + .nrec_cmd_cb = nrec_cmd_cb, + .chld_cmd_cb = chld_cmd_cb, + .cnum_cmd_cb = cnum_cmd_cb, + .cind_cmd_cb = cind_cmd_cb, + .cops_cmd_cb = cops_cmd_cb, + .clcc_cmd_cb = clcc_cmd_cb, + .unknown_at_cmd_cb = unknown_at_cmd_cb, + .key_pressed_cmd_cb = key_pressed_cmd_cb, +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + EXEC(if_hf->init, &hf_cbacks); +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hf); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hf->connect, &addr); +} + +/* disconnect */ + +/* + * This completion function will be used for several methods + * returning recently connected address + */ +static void connected_addr_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = last_addr; + *enum_func = enum_one_string; + } +} + +/* Map completion to connected_addr_c */ +#define disconnect_c connected_addr_c + +static void disconnect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hf); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hf->disconnect, &addr); +} + +/* create an audio connection */ + +/* Map completion to connected_addr_c */ +#define connect_audio_c connected_addr_c + +static void connect_audio_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hf); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hf->connect_audio, &addr); +} + +/* close the audio connection */ + +/* Map completion to connected_addr_c */ +#define disconnect_audio_c connected_addr_c + +static void disconnect_audio_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hf); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hf->disconnect_audio, &addr); +} + +/* start voice recognition */ + +static void start_voice_recognition_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + EXEC(if_hf->start_voice_recognition); +} + +/* stop voice recognition */ + +static void stop_voice_recognition_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + EXEC(if_hf->stop_voice_recognition); +} + +/* volume control */ + +static void volume_control_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(bthf_volume_type_t); + *enum_func = enum_defines; + } +} + +static void volume_control_p(int argc, const char **argv) +{ + bthf_volume_type_t type; + int volume; + + RETURN_IF_NULL(if_hf); + + /* volume type */ + if (argc <= 2) { + haltest_error("No volume type specified\n"); + return; + } + type = str2bthf_volume_type_t(argv[2]); + + /* volume */ + if (argc <= 3) { + haltest_error("No volume specified\n"); + return; + } + volume = atoi(argv[3]); + + EXEC(if_hf->volume_control, type, volume); +} + +/* Combined device status change notification */ + +static void device_status_notification_c(int argc, const char **argv, + enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(bthf_network_state_t); + *enum_func = enum_defines; + } else if (argc == 4) { + *user = TYPE_ENUM(bthf_service_type_t); + *enum_func = enum_defines; + } +} + +static void device_status_notification_p(int argc, const char **argv) +{ + bthf_network_state_t ntk_state; + bthf_service_type_t svc_type; + int signal; + int batt_chg; + + RETURN_IF_NULL(if_hf); + + /* network state */ + if (argc <= 2) { + haltest_error("No network state specified\n"); + return; + } + ntk_state = str2bthf_network_state_t(argv[2]); + + /* service type */ + if (argc <= 3) { + haltest_error("No service type specified\n"); + return; + } + svc_type = str2bthf_service_type_t(argv[3]); + + /* signal */ + if (argc <= 4) { + haltest_error("No signal specified\n"); + return; + } + signal = atoi(argv[4]); + + /* batt_chg */ + if (argc <= 5) { + haltest_error("No batt_chg specified\n"); + return; + } + batt_chg = atoi(argv[5]); + + EXEC(if_hf->device_status_notification, ntk_state, svc_type, signal, + batt_chg); +} + +/* Response for COPS command */ + +static void cops_response_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + /* response */ + if (argc <= 2) { + haltest_error("No cops specified\n"); + return; + } + + EXEC(if_hf->cops_response, argv[2]); +} + +/* Response for CIND command */ + +static void cind_response_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 6) { + *user = TYPE_ENUM(bthf_call_state_t); + *enum_func = enum_defines; + } +} + +static void cind_response_p(int argc, const char **argv) +{ + int svc; + int num_active; + int num_held; + bthf_call_state_t call_setup_state; + int signal; + int roam; + int batt_chg; + + RETURN_IF_NULL(if_hf); + + /* svc */ + if (argc <= 2) { + haltest_error("No service specified\n"); + return; + } + svc = atoi(argv[2]); + + /* num active */ + if (argc <= 3) { + haltest_error("No num active specified\n"); + return; + } + num_active = atoi(argv[3]); + + /* num held */ + if (argc <= 4) { + haltest_error("No num held specified\n"); + return; + } + num_held = atoi(argv[4]); + + /* call setup state */ + if (argc <= 5) { + haltest_error("No call setup state specified\n"); + return; + } + call_setup_state = str2bthf_call_state_t(argv[5]); + + /* signal */ + if (argc <= 6) { + haltest_error("No signal specified\n"); + return; + } + signal = atoi(argv[6]); + + /* roam */ + if (argc <= 7) { + haltest_error("No roam specified\n"); + return; + } + roam = atoi(argv[7]); + + /* batt_chg */ + if (argc <= 8) { + haltest_error("No batt_chg specified\n"); + return; + } + batt_chg = atoi(argv[8]); + + EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state, + signal, roam, batt_chg); +} + +/* Pre-formatted AT response, typically in response to unknown AT cmd */ + +static void formatted_at_response_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + /* response */ + if (argc <= 2) { + haltest_error("No response specified\n"); + return; + } + + EXEC(if_hf->formatted_at_response, argv[2]); +} + +/* at_response */ + +static void at_response_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(bthf_at_response_t); + *enum_func = enum_defines; + } +} + +static void at_response_p(int argc, const char **argv) +{ + bthf_at_response_t response_code; + int error_code; + + RETURN_IF_NULL(if_hf); + + /* response type */ + if (argc <= 2) { + haltest_error("No response specified\n"); + return; + } + response_code = str2bthf_at_response_t(argv[2]); + + /* error code */ + if (argc <= 3) + error_code = 0; + else + error_code = atoi(argv[3]); + + EXEC(if_hf->at_response, response_code, error_code); +} + +/* response for CLCC command */ + +static void clcc_response_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 4) { + *user = TYPE_ENUM(bthf_call_direction_t); + *enum_func = enum_defines; + } else if (argc == 5) { + *user = TYPE_ENUM(bthf_call_state_t); + *enum_func = enum_defines; + } else if (argc == 6) { + *user = TYPE_ENUM(bthf_call_mode_t); + *enum_func = enum_defines; + } else if (argc == 7) { + *user = TYPE_ENUM(bthf_call_mpty_type_t); + *enum_func = enum_defines; + } else if (argc == 9) { + *user = TYPE_ENUM(bthf_call_addrtype_t); + *enum_func = enum_defines; + } +} + +static void clcc_response_p(int argc, const char **argv) +{ + int index; + bthf_call_direction_t dir; + bthf_call_state_t state; + bthf_call_mode_t mode; + bthf_call_mpty_type_t mpty; + const char *number; + bthf_call_addrtype_t type; + + RETURN_IF_NULL(if_hf); + + /* index */ + if (argc <= 2) { + haltest_error("No index specified\n"); + return; + } + index = atoi(argv[2]); + + /* direction */ + if (argc <= 3) { + haltest_error("No direction specified\n"); + return; + } + dir = str2bthf_call_direction_t(argv[3]); + + /* call state */ + if (argc <= 4) { + haltest_error("No call state specified\n"); + return; + } + state = str2bthf_call_state_t(argv[4]); + + /* call mode */ + if (argc <= 5) { + haltest_error("No mode specified\n"); + return; + } + mode = str2bthf_call_mode_t(argv[5]); + + /* call mpty type */ + if (argc <= 6) { + haltest_error("No mpty type specified\n"); + return; + } + mpty = str2bthf_call_mpty_type_t(argv[6]); + + /* number */ + if (argc <= 7) { + haltest_error("No number specified\n"); + return; + } + number = argv[7]; + + /* call mpty type */ + if (argc <= 8) { + haltest_error("No address type specified\n"); + return; + } + type = str2bthf_call_addrtype_t(argv[8]); + + EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number, + type); +} + +/* phone state change */ + +static void phone_state_change_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 5) { + *user = TYPE_ENUM(bthf_call_state_t); + *enum_func = enum_defines; + } else if (argc == 7) { + *user = TYPE_ENUM(bthf_call_addrtype_t); + *enum_func = enum_defines; + } +} + +static void phone_state_change_p(int argc, const char **argv) +{ + int num_active; + int num_held; + bthf_call_state_t call_setup_state; + const char *number; + bthf_call_addrtype_t type; + + RETURN_IF_NULL(if_hf); + + /* num_active */ + if (argc <= 2) { + haltest_error("No num_active specified\n"); + return; + } + num_active = atoi(argv[2]); + + /* num_held */ + if (argc <= 3) { + haltest_error("No num_held specified\n"); + return; + } + num_held = atoi(argv[3]); + + /* setup state */ + if (argc <= 4) { + haltest_error("No call setup state specified\n"); + return; + } + call_setup_state = str2bthf_call_state_t(argv[4]); + + /* number */ + if (argc <= 5) { + haltest_error("No number specified\n"); + return; + } + number = argv[5]; + + /* call mpty type */ + if (argc <= 6) { + haltest_error("No address type specified\n"); + return; + } + type = str2bthf_call_addrtype_t(argv[6]); + + EXEC(if_hf->phone_state_change, num_active, num_held, call_setup_state, + number, type); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hf); + + EXECV(if_hf->cleanup); + if_hf = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(connect, ""), + STD_METHODCH(disconnect, ""), + STD_METHODCH(connect_audio, ""), + STD_METHODCH(disconnect_audio, ""), + STD_METHOD(start_voice_recognition), + STD_METHOD(stop_voice_recognition), + STD_METHODCH(volume_control, " "), + STD_METHODCH(device_status_notification, + " "), + STD_METHODH(cops_response, ""), + STD_METHODCH(cind_response, + " "), + STD_METHODH(formatted_at_response, ""), + STD_METHODCH(at_response, " []"), + STD_METHODCH(clcc_response, + " "), + STD_METHODCH(phone_state_change, + " "), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface hf_if = { + .name = "handsfree", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-hh.c bluez-5.23/android/client/if-hh.c --- bluez-4.101/android/client/if-hh.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-hh.c 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include + +#include "if-main.h" +#include "pollhandler.h" +#include "../hal-utils.h" + +const bthh_interface_t *if_hh = NULL; + +SINTMAP(bthh_protocol_mode_t, -1, "(unknown)") + DELEMENT(BTHH_REPORT_MODE), + DELEMENT(BTHH_BOOT_MODE), + DELEMENT(BTHH_UNSUPPORTED_MODE), +ENDMAP + +SINTMAP(bthh_report_type_t, -1, "(unknown)") + DELEMENT(BTHH_INPUT_REPORT), + DELEMENT(BTHH_OUTPUT_REPORT), + DELEMENT(BTHH_FEATURE_REPORT), +ENDMAP + +SINTMAP(bthh_connection_state_t, -1, "(unknown)") + DELEMENT(BTHH_CONN_STATE_CONNECTED), + DELEMENT(BTHH_CONN_STATE_CONNECTING), + DELEMENT(BTHH_CONN_STATE_DISCONNECTED), + DELEMENT(BTHH_CONN_STATE_DISCONNECTING), + DELEMENT(BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST), + DELEMENT(BTHH_CONN_STATE_FAILED_KBD_FROM_HOST), + DELEMENT(BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES), + DELEMENT(BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER), + DELEMENT(BTHH_CONN_STATE_FAILED_GENERIC), + DELEMENT(BTHH_CONN_STATE_UNKNOWN), +ENDMAP + +SINTMAP(bthh_status_t, -1, "(unknown)") + DELEMENT(BTHH_OK), + DELEMENT(BTHH_HS_HID_NOT_READY), + DELEMENT(BTHH_HS_INVALID_RPT_ID), + DELEMENT(BTHH_HS_TRANS_NOT_SPT), + DELEMENT(BTHH_HS_INVALID_PARAM), + DELEMENT(BTHH_HS_ERROR), + DELEMENT(BTHH_ERR), + DELEMENT(BTHH_ERR_SDP), + DELEMENT(BTHH_ERR_PROTO), + DELEMENT(BTHH_ERR_DB_FULL), + DELEMENT(BTHH_ERR_TOD_UNSPT), + DELEMENT(BTHH_ERR_NO_RES), + DELEMENT(BTHH_ERR_AUTH_FAILED), + DELEMENT(BTHH_ERR_HDL), +ENDMAP + +static char connected_device_addr[MAX_ADDR_STR_LEN]; +/* + * Callback for connection state change. + * state will have one of the values from bthh_connection_state_t + */ +static void connection_state_cb(bt_bdaddr_t *bd_addr, + bthh_connection_state_t state) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bd_addr=%s connection_state=%s\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_connection_state_t2str(state)); + if (state == BTHH_CONN_STATE_CONNECTED) + strcpy(connected_device_addr, addr); +} + +/* + * Callback for virtual unplug api. + * the status of the virtual unplug + */ +static void virtual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bd_addr=%s hh_status=%s\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_status_t2str(hh_status)); +} + +/* + * Callback for get hid info + * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, + * version, ctry_code, len + */ +static void hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info) +{ + char addr[MAX_ADDR_STR_LEN]; + + /* TODO: bluedroid does not seem to ever call this callback */ + haltest_info("%s: bd_addr=%s\n", __func__, + bt_bdaddr_t2str(bd_addr, addr)); +} + +/* + * Callback for get/set protocol api. + * the protocol mode is one of the value from bthh_protocol_mode_t + */ +static void protocol_mode_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, + bthh_protocol_mode_t mode) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bd_addr=%s hh_status=%s mode=%s\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_status_t2str(hh_status), + bthh_protocol_mode_t2str(mode)); +} + +/* Callback for get/set_idle_time api. */ +static void idle_time_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, + int idle_rate) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: bd_addr=%s hh_status=%s idle_rate=%d\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_status_t2str(hh_status), idle_rate); +} + + +/* + * Callback for get report api. + * if status is ok rpt_data contains the report data + */ +static void get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, + uint8_t *rpt_data, int rpt_size) +{ + char addr[MAX_ADDR_STR_LEN]; + + /* TODO: print actual report */ + haltest_info("%s: bd_addr=%s hh_status=%s rpt_size=%d\n", __func__, + bt_bdaddr_t2str(bd_addr, addr), + bthh_status_t2str(hh_status), rpt_size); +} + +static bthh_callbacks_t bthh_callbacks = { + .size = sizeof(bthh_callbacks), + .connection_state_cb = connection_state_cb, + .hid_info_cb = hid_info_cb, + .protocol_mode_cb = protocol_mode_cb, + .idle_time_cb = idle_time_cb, + .get_report_cb = get_report_cb, + .virtual_unplug_cb = virtual_unplug_cb +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hh); + + EXEC(if_hh->init, &bthh_callbacks); +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = (void *) connected_device_addr; + *enum_func = enum_one_string; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hh->connect, &addr); +} + +/* disconnect */ + +/* Same completion as connect_c */ +#define disconnect_c connect_c + +static void disconnect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hh->disconnect, &addr); +} + +/* virtual_unplug */ + +/* Same completion as connect_c */ +#define virtual_unplug_c connect_c + +static void virtual_unplug_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_hh->virtual_unplug, &addr); +} + +/* set_info */ + +/* Same completion as connect_c */ +#define set_info_c connect_c + +static void set_info_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_hid_info_t hid_info; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + memset(&hid_info, 0, sizeof(hid_info)); + + /* + * This command is intentionally not supported. See comment from + * bt_hid_info() in android/hidhost.c + */ + EXEC(if_hh->set_info, &addr, hid_info); +} + +/* get_protocol */ + +static void get_protocol_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = connected_device_addr; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = TYPE_ENUM(bthh_protocol_mode_t); + *enum_func = enum_defines; + } +} + +static void get_protocol_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_protocol_mode_t protocolMode; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc < 4) { + haltest_error("No protocol mode specified\n"); + return; + } + protocolMode = str2bthh_protocol_mode_t(argv[3]); + + EXEC(if_hh->get_protocol, &addr, protocolMode); +} + +/* set_protocol */ + +/* Same completion as get_protocol_c */ +#define set_protocol_c get_protocol_c + +static void set_protocol_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_protocol_mode_t protocolMode; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc < 4) { + haltest_error("No protocol mode specified\n"); + return; + } + protocolMode = str2bthh_protocol_mode_t(argv[3]); + + EXEC(if_hh->set_protocol, &addr, protocolMode); +} + +/* get_report */ + +static void get_report_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = connected_device_addr; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = TYPE_ENUM(bthh_report_type_t); + *enum_func = enum_defines; + } +} + +static void get_report_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_report_type_t reportType; + uint8_t reportId; + int bufferSize; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc < 4) { + haltest_error("No report type specified\n"); + return; + } + reportType = str2bthh_report_type_t(argv[3]); + + if (argc < 5) { + haltest_error("No reportId specified\n"); + return; + } + reportId = (uint8_t) atoi(argv[4]); + + if (argc < 6) { + haltest_error("No bufferSize specified\n"); + return; + } + bufferSize = atoi(argv[5]); + + EXEC(if_hh->get_report, &addr, reportType, reportId, bufferSize); +} + +/* set_report */ + +static void set_report_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = connected_device_addr; + *enum_func = enum_one_string; + } else if (argc == 4) { + *user = TYPE_ENUM(bthh_report_type_t); + *enum_func = enum_defines; + } +} + +static void set_report_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + bthh_report_type_t reportType; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc <= 3) { + haltest_error("No report type specified\n"); + return; + } + reportType = str2bthh_report_type_t(argv[3]); + + if (argc <= 4) { + haltest_error("No report specified\n"); + return; + } + + EXEC(if_hh->set_report, &addr, reportType, (char *) argv[4]); +} + +/* send_data */ + +static void send_data_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = connected_device_addr; + *enum_func = enum_one_string; + } +} + +static void send_data_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_hh); + VERIFY_ADDR_ARG(2, &addr); + + if (argc <= 3) { + haltest_error("No data to send specified\n"); + return; + } + + EXEC(if_hh->send_data, &addr, (char *) argv[3]); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hh); + + EXECV(if_hh->cleanup); +} + +/* Methods available in bthh_interface_t */ +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(connect, ""), + STD_METHODCH(disconnect, ""), + STD_METHODCH(virtual_unplug, ""), + STD_METHODCH(set_info, ""), + STD_METHODCH(get_protocol, " "), + STD_METHODCH(set_protocol, " "), + STD_METHODCH(get_report, " "), + STD_METHODCH(set_report, " "), + STD_METHODCH(send_data, " "), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface hh_if = { + .name = "hidhost", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-hl.c bluez-5.23/android/client/if-hl.c --- bluez-4.101/android/client/if-hl.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-hl.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include +#include + +#include "if-main.h" +#include "pollhandler.h" +#include "../hal-utils.h" + +SINTMAP(bthl_mdep_role_t, -1, "(unknown)") + DELEMENT(BTHL_MDEP_ROLE_SOURCE), + DELEMENT(BTHL_MDEP_ROLE_SINK), +ENDMAP + +SINTMAP(bthl_channel_type_t, -1, "(unknown)") + DELEMENT(BTHL_CHANNEL_TYPE_RELIABLE), + DELEMENT(BTHL_CHANNEL_TYPE_STREAMING), + DELEMENT(BTHL_CHANNEL_TYPE_ANY), +ENDMAP + +SINTMAP(bthl_app_reg_state_t, -1, "(unknown)") + DELEMENT(BTHL_APP_REG_STATE_REG_SUCCESS), + DELEMENT(BTHL_APP_REG_STATE_REG_FAILED), + DELEMENT(BTHL_APP_REG_STATE_DEREG_SUCCESS), + DELEMENT(BTHL_APP_REG_STATE_DEREG_FAILED), +ENDMAP + +SINTMAP(bthl_channel_state_t, -1, "(unknown)") + DELEMENT(BTHL_CONN_STATE_CONNECTING), + DELEMENT(BTHL_CONN_STATE_CONNECTED), + DELEMENT(BTHL_CONN_STATE_DISCONNECTING), + DELEMENT(BTHL_CONN_STATE_DISCONNECTED), + DELEMENT(BTHL_CONN_STATE_DESTROYED), +ENDMAP + +#define APP_ID_SIZE 20 +#define MDEP_CFG_SIZE 10 +#define CHANNEL_ID_SIZE 50 + +struct channel_info { + int fd; +}; + +struct mdep_cfg { + uint8_t role; + struct channel_info channel[CHANNEL_ID_SIZE]; +}; + +struct { + struct mdep_cfg mdep[MDEP_CFG_SIZE]; +} app[APP_ID_SIZE]; + +const bthl_interface_t *if_hl = NULL; + +static void app_reg_state_cb(int app_id, bthl_app_reg_state_t state) +{ + haltest_info("%s: app_id=%d app_reg_state=%s\n", __func__, + app_id, bthl_app_reg_state_t2str(state)); +} + +static void channel_state_cb(int app_id, bt_bdaddr_t *bd_addr, + int index, int channel_id, + bthl_channel_state_t state, int fd) +{ + char addr[MAX_ADDR_STR_LEN]; + + haltest_info("%s: app_id=%d bd_addr=%s mdep_cfg_index=%d\n" + "channel_id=%d channel_state=%s fd=%d\n", __func__, + app_id, bt_bdaddr_t2str(bd_addr, addr), index, + channel_id, bthl_channel_state_t2str(state), fd); + + if (app_id >= APP_ID_SIZE || index >= MDEP_CFG_SIZE + || channel_id >= CHANNEL_ID_SIZE) { + haltest_error("exceeds maximum limit"); + return; + } + + if (state == BTHL_CONN_STATE_CONNECTED) { + app[app_id].mdep[index].channel[channel_id].fd = fd; + + /* + * PTS expects dummy data on fd when it + * connects in source role. + */ + if (app[app_id].mdep[index].role == BTHL_MDEP_ROLE_SOURCE) + if (write(fd, "0", sizeof("0")) < 0) + haltest_error("writing data on fd failed\n"); + + return; + } + + if (state == BTHL_CONN_STATE_DISCONNECTED || + state == BTHL_CONN_STATE_DESTROYED) { + if (app[app_id].mdep[index].channel[channel_id].fd >= 0) { + close(app[app_id].mdep[index].channel[channel_id].fd); + app[app_id].mdep[index].channel[channel_id].fd = -1; + } + } +} + +static bthl_callbacks_t hl_cbacks = { + .size = sizeof(hl_cbacks), + .app_reg_state_cb = app_reg_state_cb, + .channel_state_cb = channel_state_cb, +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + int i, j, k; + + for (i = 0; i < APP_ID_SIZE; i++) { + for (j = 0; j < MDEP_CFG_SIZE; j++) { + app[i].mdep[j].role = 0; + for (k = 0; k < CHANNEL_ID_SIZE; k++) + app[i].mdep[j].channel[k].fd = -1; + } + } + + + RETURN_IF_NULL(if_hl); + + EXEC(if_hl->init, &hl_cbacks); +} + +/* register_application */ + +static void register_application_p(int argc, const char **argv) +{ + bthl_reg_param_t reg; + uint16_t mdep_argc_init, mdep_argc_off; + int app_id = -1; + int i; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No app name is specified\n"); + return; + } + + if (argc <= 3) { + haltest_error("No provider is specified\n"); + return; + } + + if (argc <= 4) { + haltest_error("No service name is specified\n"); + return; + } + + if (argc <= 5) { + haltest_error("No service description is specified\n"); + return; + } + + if (argc <= 6) { + haltest_error("No num of mdeps is specified\n"); + return; + } + + memset(®, 0, sizeof(reg)); + + if (argc != ((atoi(argv[6]) * 4) + 7)) { + haltest_error("mdep cfg argumetns are not proper\n"); + return; + } + + reg.application_name = argv[2]; + + if (strcmp("-", argv[3])) + reg.provider_name = argv[3]; + + if (strcmp("-", argv[4])) + reg.srv_name = argv[4]; + + if (strcmp("-", argv[5])) + reg.srv_desp = argv[5]; + + reg.number_of_mdeps = atoi(argv[6]); + + reg.mdep_cfg = malloc(reg.number_of_mdeps * sizeof(bthl_mdep_cfg_t)); + mdep_argc_init = 7; + + for (i = 0; i < reg.number_of_mdeps; i++) { + mdep_argc_off = mdep_argc_init + (4 * i); + reg.mdep_cfg[i].mdep_role = + str2bthl_mdep_role_t(argv[mdep_argc_off]); + reg.mdep_cfg[i].data_type = atoi(argv[mdep_argc_off + 1]); + reg.mdep_cfg[i].channel_type = + str2bthl_channel_type_t(argv[mdep_argc_off + 2]); + + if (!strcmp("-", argv[mdep_argc_off + 3])) { + reg.mdep_cfg[i].mdep_description = NULL; + continue; + } + + reg.mdep_cfg[i].mdep_description = argv[mdep_argc_off + 3]; + } + + EXEC(if_hl->register_application, ®, &app_id); + + for (i = 0; i < reg.number_of_mdeps; i++) + app[app_id].mdep[i].role = reg.mdep_cfg[i].mdep_role; + + free(reg.mdep_cfg); +} + +/* unregister_application */ + +static void unregister_application_p(int argc, const char **argv) +{ + uint32_t app_id; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No app id is specified"); + return; + } + + app_id = (uint32_t) atoi(argv[2]); + + EXEC(if_hl->unregister_application, app_id); +} + +/* connect_channel */ + +static void connect_channel_p(int argc, const char **argv) +{ + uint32_t app_id, mdep_cfg_index; + int channel_id = -1; + bt_bdaddr_t bd_addr; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No app id is specified"); + return; + } + + VERIFY_ADDR_ARG(3, &bd_addr); + + if (argc <= 4) { + haltest_error("No mdep cfg index is specified"); + return; + } + + app_id = (uint32_t) atoi(argv[2]); + mdep_cfg_index = (uint32_t) atoi(argv[4]); + + EXEC(if_hl->connect_channel, app_id, &bd_addr, mdep_cfg_index, + &channel_id); +} + +/* destroy_channel */ + +static void destroy_channel_p(int argc, const char **argv) +{ + uint32_t channel_id; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No channel id is specified"); + return; + } + + channel_id = (uint32_t) atoi(argv[2]); + + EXEC(if_hl->destroy_channel, channel_id); +} + +/* close_channel */ + +static void close_channel_p(int argc, const char **argv) +{ + uint32_t app_id; + uint8_t index; + int channel_id; + + RETURN_IF_NULL(if_hl); + + if (argc <= 2) { + haltest_error("No app id is specified"); + return; + } + + if (argc <= 3) { + haltest_error("No mdep_cfg_index is specified"); + return; + } + + if (argc <= 4) { + haltest_error("No channel_id is specified"); + return; + } + + app_id = (uint32_t) atoi(argv[2]); + if (app_id >= APP_ID_SIZE) { + haltest_error("Wrong app_id specified: %u\n", app_id); + return; + } + + index = (uint8_t) atoi(argv[3]); + if (index >= MDEP_CFG_SIZE) { + haltest_error("Wrong mdep cfg index: %u\n", index); + return; + } + + channel_id = atoi(argv[4]); + if (channel_id >= CHANNEL_ID_SIZE) { + haltest_error("Wrong channel id: %u\n", channel_id); + return; + } + + if (app[app_id].mdep[index].channel[channel_id].fd >= 0) { + shutdown(app[app_id].mdep[index].channel[channel_id].fd, + SHUT_RDWR); + app[app_id].mdep[index].channel[channel_id].fd = -1; + } +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_hl); + + EXECV(if_hl->cleanup); + if_hl = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODH(register_application, + " \n" + "\n" + "[[] [] [] []]" + "..."), + STD_METHODH(unregister_application, ""), + STD_METHODH(connect_channel, " "), + STD_METHODH(destroy_channel, ""), + STD_METHODH(close_channel, " "), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface hl_if = { + .name = "hl", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-main.h bluez-5.23/android/client/if-main.h --- bluez-4.101/android/client/if-main.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-main.h 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern audio_hw_device_t *if_audio; + +/* Interfaces from hal that can be populated during application lifetime */ +extern const bt_interface_t *if_bluetooth; +extern const btav_interface_t *if_av; +extern const btrc_interface_t *if_rc; +extern const bthf_interface_t *if_hf; +extern const bthh_interface_t *if_hh; +extern const btpan_interface_t *if_pan; +extern const bthl_interface_t *if_hl; +extern const btsock_interface_t *if_sock; +extern const btgatt_interface_t *if_gatt; +extern const btgatt_server_interface_t *if_gatt_server; +extern const btgatt_client_interface_t *if_gatt_client; + +/* + * Structure defines top level interfaces that can be used in test tool + * this will contain values as: bluetooth, av, gatt, socket, pan... + */ +struct interface { + const char *name; /* interface name */ + struct method *methods; /* methods available for this interface */ +}; + +extern const struct interface audio_if; +extern const struct interface sco_if; +extern const struct interface bluetooth_if; +extern const struct interface av_if; +extern const struct interface rc_if; +extern const struct interface gatt_if; +extern const struct interface gatt_client_if; +extern const struct interface gatt_server_if; +extern const struct interface pan_if; +extern const struct interface sock_if; +extern const struct interface hf_if; +extern const struct interface hh_if; +extern const struct interface hl_if; + +/* Interfaces that will show up in tool (first part of command line) */ +extern const struct interface *interfaces[]; + +#define METHOD(name, func, comp, help) {name, func, comp, help} +#define STD_METHOD(m) {#m, m##_p, NULL, NULL} +#define STD_METHODC(m) {#m, m##_p, m##_c, NULL} +#define STD_METHODH(m, h) {#m, m##_p, NULL, h} +#define STD_METHODCH(m, h) {#m, m##_p, m##_c, h} +#define END_METHOD {"", NULL, NULL, NULL} + +/* + * Function to parse argument for function, argv[0] and argv[1] are already + * parsed before this function is called and contain interface and method name + * up to argc - 1 arguments are finished and should be used to decide which + * function enumeration function to return + */ +typedef void (*parse_and_call)(int argc, const char **argv); + +/* + * This is prototype of function that will return string for given number. + * Purpose is to enumerate string for auto completion. + * Function of this type will always be called in loop. + * First time function is called i = 0, then if function returns non-NULL + * it will be called again till for some value of i it will return NULL + */ +typedef const char *(*enum_func)(void *user, int i); + +/* + * This is prototype of function that when given argc, argv will + * fill enum_func with pointer to function that will enumerate + * parameters for argc argument, user will be passed to enum_func. + */ +typedef void (*tab_complete)(int argc, const char **argv, enum_func *enum_func, + void **user); + +/* + * For each method there is name and two functions to parse command line + * and call proper hal function on. + */ +struct method { + const char *name; + parse_and_call func; + tab_complete complete; + const char *help; +}; + +int haltest_error(const char *format, ...) + __attribute__((format(printf, 1, 2))); +int haltest_info(const char *format, ...)__attribute__((format(printf, 1, 2))); +int haltest_warn(const char *format, ...)__attribute__((format(printf, 1, 2))); + +/* Enumerator for discovered devices, to be used as tab completion enum_func */ +const char *enum_devices(void *v, int i); +const char *interface_name(void *v, int i); +const char *command_name(void *v, int i); +void add_remote_device(const bt_bdaddr_t *addr); + +const struct interface *get_interface(const char *name); +struct method *get_method(struct method *methods, const char *name); +struct method *get_command(const char *name); +const struct method *get_interface_method(const char *iname, + const char *mname); + +#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) + +/* Helper macro for executing function on interface and printing BT_STATUS */ +#define EXEC(f, ...) \ + { \ + if (f) { \ + int err = f(__VA_ARGS__); \ + haltest_info("%s: %s\n", #f, bt_status_t2str(err)); \ + } else { \ + haltest_info("%s is NULL\n", #f); \ + } \ + } + +/* Helper macro for executing void function on interface */ +#define EXECV(f, ...) \ + { \ + (void) f(__VA_ARGS__); \ + haltest_info("%s: void\n", #f); \ + } + +#define RETURN_IF_NULL(x) \ + do { if (!x) { haltest_error("%s is NULL\n", #x); return; } } while (0) + +#define VERIFY_ADDR_ARG(n, adr) \ + do { \ + if (n < argc) {\ + str2bt_bdaddr_t(argv[n], adr); \ + } else { \ + haltest_error("No address specified\n");\ + return;\ + } \ + } while (0) diff -Nru bluez-4.101/android/client/if-pan.c bluez-5.23/android/client/if-pan.c --- bluez-4.101/android/client/if-pan.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-pan.c 2014-01-21 00:12:58.000000000 +0000 @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "if-main.h" +#include "../hal-utils.h" + +const btpan_interface_t *if_pan = NULL; + +typedef int btpan_role_t; + +SINTMAP(btpan_role_t, -1, "(unknown)") + DELEMENT(BTPAN_ROLE_NONE), + DELEMENT(BTPAN_ROLE_PANNAP), + DELEMENT(BTPAN_ROLE_PANU), +ENDMAP + +SINTMAP(btpan_connection_state_t, -1, "(unknown)") + DELEMENT(BTPAN_STATE_CONNECTED), + DELEMENT(BTPAN_STATE_CONNECTING), + DELEMENT(BTPAN_STATE_DISCONNECTED), + DELEMENT(BTPAN_STATE_DISCONNECTING), +ENDMAP + +SINTMAP(btpan_control_state_t, -1, "(unknown)") + DELEMENT(BTPAN_STATE_ENABLED), + DELEMENT(BTPAN_STATE_DISABLED), +ENDMAP + +static void control_state_cb(btpan_control_state_t state, bt_status_t error, + int local_role, const char *ifname) +{ + haltest_info("%s: state=%s error=%s local_role=%s ifname=%s\n", + __func__, btpan_control_state_t2str(state), + bt_status_t2str(error), btpan_role_t2str(local_role), + ifname); +} + +static char last_used_addr[MAX_ADDR_STR_LEN]; + +static void connection_state_cb(btpan_connection_state_t state, + bt_status_t error, const bt_bdaddr_t *bd_addr, + int local_role, int remote_role) +{ + haltest_info("%s: state=%s error=%s bd_addr=%s local_role=%s remote_role=%s\n", + __func__, btpan_connection_state_t2str(state), + bt_status_t2str(error), + bt_bdaddr_t2str(bd_addr, last_used_addr), + btpan_role_t2str(local_role), + btpan_role_t2str(remote_role)); +} + +static btpan_callbacks_t pan_cbacks = { + .size = sizeof(pan_cbacks), + .control_state_cb = control_state_cb, + .connection_state_cb = connection_state_cb +}; + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_pan); + + EXEC(if_pan->init, &pan_cbacks); +} + +/* enable */ + +static void enable_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btpan_role_t); + *enum_func = enum_defines; + } +} + +static void enable_p(int argc, const char **argv) +{ + int local_role; + + RETURN_IF_NULL(if_pan); + + /* local role */ + if (argc < 3) { + haltest_error("No local mode specified\n"); + return; + } + local_role = str2btpan_role_t(argv[2]); + if (local_role == -1) + local_role = atoi(argv[2]); + + EXEC(if_pan->enable, local_role); +} + +/* get_local_role */ + +static void get_local_role_p(int argc, const char **argv) +{ + int local_role; + + RETURN_IF_NULL(if_pan); + + local_role = if_pan->get_local_role(); + haltest_info("local_role: %s\n", btpan_role_t2str(local_role)); +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = NULL; + *enum_func = enum_devices; + } else if (argc == 4 || argc == 5) { + *user = TYPE_ENUM(btpan_role_t); + *enum_func = enum_defines; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + int local_role; + int remote_role; + + RETURN_IF_NULL(if_pan); + VERIFY_ADDR_ARG(2, &addr); + + /* local role */ + if (argc < 4) { + haltest_error("No local mode specified\n"); + return; + } + local_role = str2btpan_role_t(argv[3]); + if (local_role == -1) + local_role = atoi(argv[3]); + + /* remote role */ + if (argc < 5) { + haltest_error("No remote mode specified\n"); + return; + } + remote_role = str2btpan_role_t(argv[4]); + if (remote_role == -1) + remote_role = atoi(argv[4]); + + EXEC(if_pan->connect, &addr, local_role, remote_role); +} + +/* disconnect */ + +static void disconnect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = last_used_addr; + *enum_func = enum_one_string; + } +} + +static void disconnect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + + RETURN_IF_NULL(if_pan); + VERIFY_ADDR_ARG(2, &addr); + + EXEC(if_pan->disconnect, &addr); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_pan); + + EXECV(if_pan->cleanup); + if_pan = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(connect, " "), + STD_METHODCH(enable, ""), + STD_METHOD(get_local_role), + STD_METHODCH(disconnect, ""), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface pan_if = { + .name = "pan", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-rc.c bluez-5.23/android/client/if-rc.c --- bluez-4.101/android/client/if-rc.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-rc.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include + +#include "if-main.h" +#include "pollhandler.h" +#include "../hal-utils.h" + +const btrc_interface_t *if_rc = NULL; + +SINTMAP(btrc_play_status_t, -1, "(unknown)") + DELEMENT(BTRC_PLAYSTATE_STOPPED), + DELEMENT(BTRC_PLAYSTATE_PLAYING), + DELEMENT(BTRC_PLAYSTATE_PAUSED), + DELEMENT(BTRC_PLAYSTATE_FWD_SEEK), + DELEMENT(BTRC_PLAYSTATE_REV_SEEK), + DELEMENT(BTRC_PLAYSTATE_ERROR), +ENDMAP + +SINTMAP(btrc_media_attr_t, -1, "(unknown)") + DELEMENT(BTRC_MEDIA_ATTR_TITLE), + DELEMENT(BTRC_MEDIA_ATTR_ARTIST), + DELEMENT(BTRC_MEDIA_ATTR_ALBUM), + DELEMENT(BTRC_MEDIA_ATTR_TRACK_NUM), + DELEMENT(BTRC_MEDIA_ATTR_NUM_TRACKS), + DELEMENT(BTRC_MEDIA_ATTR_GENRE), + DELEMENT(BTRC_MEDIA_ATTR_PLAYING_TIME), +ENDMAP + +SINTMAP(btrc_status_t, -1, "(unknown)") + DELEMENT(BTRC_STS_BAD_CMD), + DELEMENT(BTRC_STS_BAD_PARAM), + DELEMENT(BTRC_STS_NOT_FOUND), + DELEMENT(BTRC_STS_INTERNAL_ERR), + DELEMENT(BTRC_STS_NO_ERROR), +ENDMAP + +SINTMAP(btrc_event_id_t, -1, "(unknown)") + DELEMENT(BTRC_EVT_PLAY_STATUS_CHANGED), + DELEMENT(BTRC_EVT_TRACK_CHANGE), + DELEMENT(BTRC_EVT_TRACK_REACHED_END), + DELEMENT(BTRC_EVT_TRACK_REACHED_START), + DELEMENT(BTRC_EVT_PLAY_POS_CHANGED), + DELEMENT(BTRC_EVT_APP_SETTINGS_CHANGED), +ENDMAP + +SINTMAP(btrc_notification_type_t, -1, "(unknown)") + DELEMENT(BTRC_NOTIFICATION_TYPE_INTERIM), + DELEMENT(BTRC_NOTIFICATION_TYPE_CHANGED), +ENDMAP + +static char last_addr[MAX_ADDR_STR_LEN]; + +static void remote_features_cb(bt_bdaddr_t *bd_addr, + btrc_remote_features_t features) +{ + haltest_info("%s: remote_bd_addr=%s features=%u\n", __func__, + bt_bdaddr_t2str(bd_addr, last_addr), features); +} + +static void get_play_status_cb(void) +{ + haltest_info("%s\n", __func__); +} + +static void list_player_app_attr_cb(void) +{ + haltest_info("%s\n", __func__); +} + +static void list_player_app_values_cb(btrc_player_attr_t attr_id) +{ + haltest_info("%s, attr_id=%d\n", __func__, attr_id); +} + +static void get_player_app_value_cb(uint8_t num_attr, + btrc_player_attr_t *p_attrs) +{ + int i; + + haltest_info("%s, num_attr=%d\n", __func__, num_attr); + + for (i = 0; i < num_attr; i++) + haltest_info("attribute=%u\n", p_attrs[i]); +} + +static void get_player_app_attrs_text_cb(uint8_t num_attr, + btrc_player_attr_t *p_attrs) +{ + int i; + + haltest_info("%s, num_attr=%d\n", __func__, num_attr); + + for (i = 0; i < num_attr; i++) + haltest_info("attribute=%u\n", p_attrs[i]); + +} + +static void get_player_app_values_text_cb(uint8_t attr_id, uint8_t num_val, + uint8_t *p_vals) +{ + haltest_info("%s, attr_id=%d num_val=%d values=%p\n", __func__, + attr_id, num_val, p_vals); +} + +static void set_player_app_value_cb(btrc_player_settings_t *p_vals) +{ + int i; + + haltest_info("%s, num_attr=%u\n", __func__, p_vals->num_attr); + + for (i = 0; i < p_vals->num_attr; i++) + haltest_info("attr id=%u, values=%u\n", p_vals->attr_ids[i], + p_vals->attr_values[i]); +} + +static void get_element_attr_cb(uint8_t num_attr, btrc_media_attr_t *attrs) +{ + uint8_t i; + + haltest_info("%s, num_of_attributes=%d\n", __func__, num_attr); + + for (i = 0; i < num_attr; i++) + haltest_info("attr id=%s\n", btrc_media_attr_t2str(attrs[i])); +} + +static void register_notification_cb(btrc_event_id_t event_id, uint32_t param) +{ + haltest_info("%s, event=%u param=%u\n", __func__, event_id, param); +} + +static void volume_change_cb(uint8_t volume, uint8_t ctype) +{ + haltest_info("%s, volume=%d ctype=%d\n", __func__, volume, ctype); +} + +static void passthrough_cmd_cb(int id, int key_state) +{ + haltest_info("%s, id=%d key_state=%d\n", __func__, id, key_state); +} + +static btrc_callbacks_t rc_cbacks = { + .size = sizeof(rc_cbacks), + .remote_features_cb = remote_features_cb, + .get_play_status_cb = get_play_status_cb, + .list_player_app_attr_cb = list_player_app_attr_cb, + .list_player_app_values_cb = list_player_app_values_cb, + .get_player_app_value_cb = get_player_app_value_cb, + .get_player_app_attrs_text_cb = get_player_app_attrs_text_cb, + .get_player_app_values_text_cb = get_player_app_values_text_cb, + .set_player_app_value_cb = set_player_app_value_cb, + .get_element_attr_cb = get_element_attr_cb, + .register_notification_cb = register_notification_cb, + .volume_change_cb = volume_change_cb, + .passthrough_cmd_cb = passthrough_cmd_cb, +}; + +/* init */ + +static void init_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_rc); + + EXEC(if_rc->init, &rc_cbacks); +} + +/* get_play_status_rsp */ + +static void get_play_status_rsp_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btrc_play_status_t); + *enum_func = enum_defines; + } +} + +static void get_play_status_rsp_p(int argc, const char **argv) +{ + btrc_play_status_t play_status; + uint32_t song_len, song_pos; + + RETURN_IF_NULL(if_rc); + + if (argc <= 2) { + haltest_error("No play status specified"); + return; + } + + if (argc <= 3) { + haltest_error("No song length specified"); + return; + } + + if (argc <= 4) { + haltest_error("No song position specified"); + return; + } + + play_status = str2btrc_play_status_t(argv[2]); + song_len = (uint32_t) atoi(argv[3]); + song_pos = (uint32_t) atoi(argv[4]); + + EXEC(if_rc->get_play_status_rsp, play_status, song_len, song_pos); +} + +/* get_element_attr_rsp */ + +static void get_element_attr_rsp_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 4) { + *user = TYPE_ENUM(btrc_media_attr_t); + *enum_func = enum_defines; + } +} + +static void get_element_attr_rsp_p(int argc, const char **argv) +{ + uint8_t num_attr; + btrc_element_attr_val_t attrs; + + RETURN_IF_NULL(if_rc); + + if (argc <= 2) { + haltest_error("No number of attributes specified"); + return; + } + + if (argc <= 4) { + haltest_error("No attr id and value specified"); + return; + } + + num_attr = (uint8_t) atoi(argv[2]); + attrs.attr_id = str2btrc_media_attr_t(argv[3]); + strcpy((char *)attrs.text, argv[4]); + + EXEC(if_rc->get_element_attr_rsp, num_attr, &attrs); +} + +/* set_volume */ + +static void set_volume_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ +} + +static void set_volume_p(int argc, const char **argv) +{ + uint8_t volume; + + RETURN_IF_NULL(if_rc); + + if (argc <= 2) { + haltest_error("No volume specified"); + return; + } + + volume = (uint8_t) atoi(argv[2]); + + EXEC(if_rc->set_volume, volume); +} + +/* set_player_app_value_rsp */ + +static void set_player_app_value_rsp_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btrc_status_t); + *enum_func = enum_defines; + } +} + +static void set_player_app_value_rsp_p(int argc, const char **argv) +{ + btrc_status_t rsp_status; + + RETURN_IF_NULL(if_rc); + + if (argc <= 2) { + haltest_error("No response status specified"); + return; + } + + rsp_status = str2btrc_status_t(argv[2]); + + EXEC(if_rc->set_player_app_value_rsp, rsp_status); +} + +/* register_notification_rsp */ + +static void register_notification_rsp_c(int argc, const char **argv, + enum_func *enum_func, void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btrc_event_id_t); + *enum_func = enum_defines; + } + + if (argc == 4) { + *user = TYPE_ENUM(btrc_notification_type_t); + *enum_func = enum_defines; + } +} + +static void register_notification_rsp_p(int argc, const char **argv) +{ + btrc_event_id_t event_id; + btrc_notification_type_t type; + btrc_register_notification_t reg; + uint32_t song_pos; + uint64_t track; + + RETURN_IF_NULL(if_rc); + + memset(®, 0, sizeof(reg)); + event_id = str2btrc_event_id_t(argv[2]); + type = str2btrc_notification_type_t(argv[3]); + + switch (event_id) { + case BTRC_EVT_PLAY_STATUS_CHANGED: + reg.play_status = str2btrc_play_status_t(argv[4]); + break; + + case BTRC_EVT_TRACK_CHANGE: + track = strtoull(argv[5], NULL, 10); + memcpy(reg.track, &track, sizeof(btrc_uid_t)); + break; + + case BTRC_EVT_TRACK_REACHED_END: + case BTRC_EVT_TRACK_REACHED_START: + break; + + case BTRC_EVT_PLAY_POS_CHANGED: + song_pos = strtoul(argv[4], NULL, 10); + memcpy(®.song_pos, &song_pos, sizeof(uint32_t)); + break; + + case BTRC_EVT_APP_SETTINGS_CHANGED: + haltest_error("not supported"); + return; + } + + EXEC(if_rc->register_notification_rsp, event_id, type, ®); +} + +/* cleanup */ + +static void cleanup_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_rc); + + EXECV(if_rc->cleanup); + if_rc = NULL; +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHODCH(get_play_status_rsp, + " "), + STD_METHODCH(get_element_attr_rsp, " "), + STD_METHODCH(set_player_app_value_rsp, ""), + STD_METHODCH(set_volume, ""), + STD_METHODCH(register_notification_rsp, + " \n" + "BTRC_EVT_PLAY_STATUS_CHANGED \n" + "BTRC_EVT_TRACK_CHANGE \n" + "BTRC_EVT_TRACK_REACHED_END \n" + "BTRC_EVT_TRACK_REACHED_START \n" + "BTRC_EVT_PLAY_POS_CHANGED \n"), + STD_METHOD(cleanup), + END_METHOD +}; + +const struct interface rc_if = { + .name = "rc", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-sco.c bluez-5.23/android/client/if-sco.c --- bluez-4.101/android/client/if-sco.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-sco.c 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,798 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "../src/shared/util.h" +#include "if-main.h" +#include "../hal-utils.h" + +audio_hw_device_t *if_audio_sco = NULL; +static struct audio_stream_out *stream_out = NULL; +static struct audio_stream_in *stream_in = NULL; + +static size_t buffer_size = 0; +static size_t buffer_size_in = 0; +static pthread_t play_thread = 0; +static pthread_mutex_t outstream_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER; + +enum state { + STATE_STOPPED, + STATE_STOPPING, + STATE_PLAYING, + STATE_SUSPENDED, + STATE_MAX +}; + +SINTMAP(audio_channel_mask_t, -1, "(AUDIO_CHANNEL_INVALID)") + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_LOW_FREQUENCY), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_BACK_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_SIDE_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_SIDE_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_CENTER), + DELEMENT(AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), + DELEMENT(AUDIO_CHANNEL_OUT_MONO), + DELEMENT(AUDIO_CHANNEL_OUT_STEREO), + DELEMENT(AUDIO_CHANNEL_OUT_QUAD), + DELEMENT(AUDIO_CHANNEL_OUT_SURROUND), + DELEMENT(AUDIO_CHANNEL_OUT_5POINT1), + DELEMENT(AUDIO_CHANNEL_OUT_7POINT1), + DELEMENT(AUDIO_CHANNEL_OUT_ALL), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), + DELEMENT(AUDIO_CHANNEL_OUT_FRONT_LEFT), +ENDMAP + +SINTMAP(audio_format_t, -1, "(AUDIO_FORMAT_INVALID)") + DELEMENT(AUDIO_FORMAT_DEFAULT), + DELEMENT(AUDIO_FORMAT_PCM), + DELEMENT(AUDIO_FORMAT_MP3), + DELEMENT(AUDIO_FORMAT_AMR_NB), + DELEMENT(AUDIO_FORMAT_AMR_WB), + DELEMENT(AUDIO_FORMAT_AAC), + DELEMENT(AUDIO_FORMAT_HE_AAC_V1), + DELEMENT(AUDIO_FORMAT_HE_AAC_V2), + DELEMENT(AUDIO_FORMAT_VORBIS), + DELEMENT(AUDIO_FORMAT_MAIN_MASK), + DELEMENT(AUDIO_FORMAT_SUB_MASK), + DELEMENT(AUDIO_FORMAT_PCM_16_BIT), + DELEMENT(AUDIO_FORMAT_PCM_8_BIT), + DELEMENT(AUDIO_FORMAT_PCM_32_BIT), + DELEMENT(AUDIO_FORMAT_PCM_8_24_BIT), +ENDMAP + +static int current_state = STATE_STOPPED; + +#define SAMPLERATE 44100 +static short sample[SAMPLERATE]; +static uint16_t sample_pos; + +static void init_p(int argc, const char **argv) +{ + int err; + const hw_module_t *module; + audio_hw_device_t *device; + + err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, "sco", &module); + if (err) { + haltest_error("hw_get_module_by_class returned %d\n", err); + return; + } + + err = audio_hw_device_open(module, &device); + if (err) { + haltest_error("audio_hw_device_open returned %d\n", err); + return; + } + + if_audio_sco = device; +} + +static int feed_from_file(short *buffer, void *data) +{ + FILE *in = data; + return fread(buffer, buffer_size, 1, in); +} + +static int feed_from_generator(short *buffer, void *data) +{ + size_t i = 0; + float volume = 0.5; + float *freq = data; + float f = 1; + + if (freq) + f = *freq; + + /* buffer_size is in bytes but we are using buffer of shorts (2 bytes)*/ + for (i = 0; i < buffer_size / sizeof(*buffer) - 1;) { + if (sample_pos >= SAMPLERATE) + sample_pos = sample_pos % SAMPLERATE; + + /* Use the same sample for both channels */ + buffer[i++] = sample[sample_pos] * volume; + buffer[i++] = sample[sample_pos] * volume; + + sample_pos += f; + } + + return buffer_size; +} + +static int feed_from_in(short *buffer, void *data) +{ + return stream_in->read(stream_in, buffer, buffer_size_in); +} + +static void prepare_sample(void) +{ + int x; + double s; + + haltest_info("Preparing audio sample...\n"); + + for (x = 0; x < SAMPLERATE; x++) { + /* prepare sinusoidal 1Hz sample */ + s = (2.0 * 3.14159) * ((double)x / SAMPLERATE); + s = sin(s); + + /* remap <-1, 1> to signed 16bit PCM range */ + sample[x] = s * 32767; + } + + sample_pos = 0; +} + +static void mono_to_stereo_pcm16(const int16_t *in, int16_t *out, size_t samples) +{ + int16_t mono; + size_t i; + + for (i = 0; i < samples; i++) { + mono = get_unaligned(&in[i]); + + put_unaligned(mono, &out[2 * i]); + put_unaligned(mono, &out[2 * i + 1]); + } +} + +static void *playback_thread(void *data) +{ + int (*filbuff_cb) (short*, void*); + short buffer[buffer_size / sizeof(short)]; + short buffer_in[buffer_size_in / sizeof(short)]; + size_t len = 0; + ssize_t w_len = 0; + FILE *in = data; + void *cb_data = NULL; + float freq = 440.0; + + /* Use file or fall back to generator */ + if (in) { + if (data == stream_in) + filbuff_cb = feed_from_in; + else { + filbuff_cb = feed_from_file; + cb_data = in; + } + } else { + prepare_sample(); + filbuff_cb = feed_from_generator; + cb_data = &freq; + } + + pthread_mutex_lock(&state_mutex); + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); + + do { + pthread_mutex_lock(&state_mutex); + + if (current_state == STATE_STOPPING) { + haltest_info("Detected stopping\n"); + pthread_mutex_unlock(&state_mutex); + break; + } else if (current_state == STATE_SUSPENDED) { + pthread_mutex_unlock(&state_mutex); + usleep(500); + continue; + } + + pthread_mutex_unlock(&state_mutex); + + if (data && data == stream_in) { + int chan_in = popcount(stream_in->common.get_channels(&stream_in->common)); + int chan_out = popcount(stream_out->common.get_channels(&stream_out->common)); + + len = filbuff_cb(buffer_in, cb_data); + + if (chan_in == 1 && chan_out == 2) { + mono_to_stereo_pcm16(buffer_in, + buffer, + buffer_size_in / 2); + } + } else + len = filbuff_cb(buffer, cb_data); + + pthread_mutex_lock(&outstream_mutex); + if (!stream_out) { + pthread_mutex_unlock(&outstream_mutex); + break; + } + + w_len = stream_out->write(stream_out, buffer, buffer_size); + pthread_mutex_unlock(&outstream_mutex); + } while (len && w_len > 0); + + if (in && data != stream_in) + fclose(in); + + pthread_mutex_lock(&state_mutex); + current_state = STATE_STOPPED; + pthread_mutex_unlock(&state_mutex); + + haltest_info("Done playing.\n"); + + return NULL; +} + +static void write_stereo_pcm16(char *buffer, size_t len, FILE *out) +{ + const int16_t *input = (const void *) buffer; + int16_t sample[2]; + size_t i; + + for (i = 0; i < len / 2; i++) { + int16_t mono = get_unaligned(&input[i]); + + put_unaligned(mono, &sample[0]); + put_unaligned(mono, &sample[1]); + + fwrite(sample, sizeof(sample), 1, out); + } +} + +static void *read_thread(void *data) +{ + int (*filbuff_cb) (short*, void*) = feed_from_in; + short buffer[buffer_size_in / sizeof(short)]; + ssize_t len = 0; + void *cb_data = NULL; + FILE *out = data; + + pthread_mutex_lock(&state_mutex); + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); + + do { + pthread_mutex_lock(&state_mutex); + + if (current_state == STATE_STOPPING) { + haltest_info("Detected stopping\n"); + pthread_mutex_unlock(&state_mutex); + break; + } else if (current_state == STATE_SUSPENDED) { + pthread_mutex_unlock(&state_mutex); + usleep(500); + continue; + } + + pthread_mutex_unlock(&state_mutex); + + len = filbuff_cb(buffer, cb_data); + if (len < 0) { + haltest_error("Error receiving SCO data"); + break; + } + + haltest_info("Read %zd bytes\n", len); + + if (out) { + write_stereo_pcm16((char *) buffer, len, out); + haltest_info("Written %zd bytes\n", len * 2); + } + } while (len); + + if (out) + fclose(out); + + pthread_mutex_lock(&state_mutex); + current_state = STATE_STOPPED; + pthread_mutex_unlock(&state_mutex); + + haltest_info("Done reading.\n"); + + return NULL; +} + +static void play_p(int argc, const char **argv) +{ + const char *fname = NULL; + FILE *in = NULL; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_error("Invalid audio file path.\n"); + haltest_info("Using sound generator.\n"); + } else { + fname = argv[2]; + in = fopen(fname, "r"); + + if (in == NULL) { + haltest_error("Cannot open file: %s\n", fname); + return; + } + haltest_info("Playing file: %s\n", fname); + } + + if (buffer_size == 0) { + haltest_error("Invalid buffer size. Was stream_out opened?\n"); + goto fail; + } + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + haltest_error("Already playing or stream suspended!\n"); + pthread_mutex_unlock(&state_mutex); + goto fail; + } + pthread_mutex_unlock(&state_mutex); + + if (pthread_create(&play_thread, NULL, playback_thread, in) != 0) { + haltest_error("Cannot create playback thread!\n"); + goto fail; + } + + return; +fail: + if (in) + fclose(in); +} + +static void loop_p(int argc, const char **argv) +{ + int chan_out, chan_in; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + RETURN_IF_NULL(stream_in); + + chan_out = popcount(stream_out->common.get_channels(&stream_out->common)); + chan_in = popcount(stream_in->common.get_channels(&stream_in->common)); + + if (!buffer_size || !buffer_size_in) { + haltest_error("Invalid buffer sizes. Streams opened\n"); + return; + } + + if (buffer_size / chan_out != buffer_size_in / chan_in) { + haltest_error("read/write buffers differ, not supported\n"); + return; + } + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + haltest_error("Already playing or stream suspended!\n"); + pthread_mutex_unlock(&state_mutex); + return; + } + pthread_mutex_unlock(&state_mutex); + + if (pthread_create(&play_thread, NULL, playback_thread, + stream_in) != 0) + haltest_error("Cannot create playback thread!\n"); +} + +static void read_p(int argc, const char **argv) +{ + const char *fname = NULL; + FILE *out = NULL; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_in); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + haltest_error("Already playing or stream suspended!\n"); + pthread_mutex_unlock(&state_mutex); + return; + } + pthread_mutex_unlock(&state_mutex); + + if (argc < 3) { + haltest_error("Invalid audio file path.\n"); + haltest_info("Using read and through away\n"); + } else { + fname = argv[2]; + out = fopen(fname, "w"); + if (!out) { + haltest_error("Cannot open file: %s\n", fname); + return; + } + + haltest_info("Reading to file: %s\n", fname); + } + + if (!buffer_size_in) { + haltest_error("Invalid buffer size.\n"); + goto failed; + } + + if (pthread_create(&play_thread, NULL, read_thread, out) != 0) { + haltest_error("Cannot create playback thread!\n"); + goto failed; + } + + return; +failed: + if (out) + fclose(out); +} + +static void stop_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(play_thread); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_STOPPED || current_state == STATE_STOPPING) { + pthread_mutex_unlock(&state_mutex); + return; + } + + if (stream_out) { + pthread_mutex_lock(&outstream_mutex); + stream_out->common.standby(&stream_out->common); + pthread_mutex_unlock(&outstream_mutex); + } + + current_state = STATE_STOPPING; + pthread_mutex_unlock(&state_mutex); + + pthread_join(play_thread, NULL); + play_thread = 0; + + haltest_info("Ended %s\n", __func__); +} + +static void open_output_stream_p(int argc, const char **argv) +{ + struct audio_config *config; + int err; + + RETURN_IF_NULL(if_audio_sco); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_PLAYING) { + haltest_error("Already playing!\n"); + pthread_mutex_unlock(&state_mutex); + return; + } + pthread_mutex_unlock(&state_mutex); + + if (argc < 3) { + haltest_info("No sampling rate specified. Use default conf\n"); + config = NULL; + } else { + config = calloc(1, sizeof(struct audio_config)); + if (!config) + return; + + config->sample_rate = atoi(argv[2]); + config->channel_mask = AUDIO_CHANNEL_OUT_STEREO; + config->format = AUDIO_FORMAT_PCM_16_BIT; + } + + err = if_audio_sco->open_output_stream(if_audio_sco, + 0, + AUDIO_DEVICE_OUT_ALL_SCO, + AUDIO_OUTPUT_FLAG_NONE, + config, + &stream_out); + if (err < 0) { + haltest_error("open output stream returned %d\n", err); + goto failed; + } + + buffer_size = stream_out->common.get_buffer_size(&stream_out->common); + if (buffer_size == 0) + haltest_error("Invalid buffer size received!\n"); + else + haltest_info("Using buffer size: %zu\n", buffer_size); +failed: + if (config) + free(config); +} + +static void close_output_stream_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (play_thread) + stop_p(argc, argv); + + if_audio_sco->close_output_stream(if_audio_sco, stream_out); + + stream_out = NULL; + buffer_size = 0; +} + +static void open_input_stream_p(int argc, const char **argv) +{ + struct audio_config *config; + int err; + + RETURN_IF_NULL(if_audio_sco); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_PLAYING) { + haltest_error("Already playing!\n"); + pthread_mutex_unlock(&state_mutex); + return; + } + pthread_mutex_unlock(&state_mutex); + + if (argc < 3) { + haltest_info("No sampling rate specified. Use default conf\n"); + config = NULL; + } else { + config = calloc(1, sizeof(struct audio_config)); + if (!config) + return; + + config->sample_rate = atoi(argv[2]); + config->channel_mask = AUDIO_CHANNEL_OUT_MONO; + config->format = AUDIO_FORMAT_PCM_16_BIT; + } + + err = if_audio_sco->open_input_stream(if_audio_sco, + 0, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, + config, + &stream_in); + if (err < 0) { + haltest_error("open output stream returned %d\n", err); + goto failed; + } + + buffer_size_in = stream_in->common.get_buffer_size(&stream_in->common); + if (buffer_size_in == 0) + haltest_error("Invalid buffer size received!\n"); + else + haltest_info("Using buffer size: %zu\n", buffer_size_in); +failed: + if (config) + free(config); +} + +static void close_input_stream_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_in); + + if (play_thread) + stop_p(argc, argv); + + if_audio_sco->close_input_stream(if_audio_sco, stream_in); + + stream_in = NULL; + buffer_size_in = 0; +} + +static void cleanup_p(int argc, const char **argv) +{ + int err; + + RETURN_IF_NULL(if_audio_sco); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_STOPPED) { + pthread_mutex_unlock(&state_mutex); + close_output_stream_p(0, NULL); + } else { + pthread_mutex_unlock(&state_mutex); + } + + err = audio_hw_device_close(if_audio_sco); + if (err < 0) { + haltest_error("audio_hw_device_close returned %d\n", err); + return; + } + + if_audio_sco = NULL; +} + +static void suspend_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + pthread_mutex_lock(&state_mutex); + if (current_state != STATE_PLAYING) { + pthread_mutex_unlock(&state_mutex); + return; + } + current_state = STATE_SUSPENDED; + pthread_mutex_unlock(&state_mutex); + + pthread_mutex_lock(&outstream_mutex); + stream_out->common.standby(&stream_out->common); + pthread_mutex_unlock(&outstream_mutex); +} + +static void resume_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + pthread_mutex_lock(&state_mutex); + if (current_state == STATE_SUSPENDED) + current_state = STATE_PLAYING; + pthread_mutex_unlock(&state_mutex); +} + +static void get_latency_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + haltest_info("Output audio stream latency: %d\n", + stream_out->get_latency(stream_out)); +} + +static void get_buffer_size_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + haltest_info("Current output buffer size: %zu\n", + stream_out->common.get_buffer_size(&stream_out->common)); +} + +static void get_channels_p(int argc, const char **argv) +{ + audio_channel_mask_t channels; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + channels = stream_out->common.get_channels(&stream_out->common); + + haltest_info("Channels: %s\n", audio_channel_mask_t2str(channels)); +} + +static void get_format_p(int argc, const char **argv) +{ + audio_format_t format; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + format = stream_out->common.get_format(&stream_out->common); + + haltest_info("Format: %s\n", audio_format_t2str(format)); +} + +static void get_sample_rate_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + haltest_info("Current sample rate: %d\n", + stream_out->common.get_sample_rate(&stream_out->common)); +} + +static void get_parameters_p(int argc, const char **argv) +{ + const char *keystr; + + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_info("No keys given.\n"); + keystr = ""; + } else { + keystr = argv[2]; + } + + haltest_info("Current parameters: %s\n", + stream_out->common.get_parameters(&stream_out->common, + keystr)); +} + +static void set_parameters_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (argc < 3) { + haltest_error("No key=value; pairs given.\n"); + return; + } + + stream_out->common.set_parameters(&stream_out->common, argv[2]); +} + +static void set_sample_rate_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + RETURN_IF_NULL(stream_out); + + if (argc < 3) + return; + + stream_out->common.set_sample_rate(&stream_out->common, atoi(argv[2])); +} + +static void init_check_p(int argc, const char **argv) +{ + RETURN_IF_NULL(if_audio_sco); + + haltest_info("Init check result: %d\n", + if_audio_sco->init_check(if_audio_sco)); +} + +static struct method methods[] = { + STD_METHOD(init), + STD_METHOD(cleanup), + STD_METHODH(open_output_stream, "sample_rate"), + STD_METHOD(close_output_stream), + STD_METHODH(open_input_stream, "sampling rate"), + STD_METHOD(close_input_stream), + STD_METHODH(play, ""), + STD_METHOD(read), + STD_METHOD(loop), + STD_METHOD(stop), + STD_METHOD(suspend), + STD_METHOD(resume), + STD_METHOD(get_latency), + STD_METHOD(get_buffer_size), + STD_METHOD(get_channels), + STD_METHOD(get_format), + STD_METHOD(get_sample_rate), + STD_METHODH(get_parameters, ""), + STD_METHODH(set_parameters, ""), + STD_METHODH(set_sample_rate, ""), + STD_METHOD(init_check), + END_METHOD +}; + +const struct interface sco_if = { + .name = "sco", + .methods = methods +}; diff -Nru bluez-4.101/android/client/if-sock.c bluez-5.23/android/client/if-sock.c --- bluez-4.101/android/client/if-sock.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/if-sock.c 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "if-main.h" +#include "pollhandler.h" +#include "../hal-utils.h" + +const btsock_interface_t *if_sock = NULL; + +SINTMAP(btsock_type_t, -1, "(unknown)") + DELEMENT(BTSOCK_RFCOMM), + DELEMENT(BTSOCK_SCO), + DELEMENT(BTSOCK_L2CAP), +ENDMAP + +#define MAX_LISTEN_FD 15 +static int listen_fd[MAX_LISTEN_FD]; +static int listen_fd_count; + +static const char * const uuids[] = { + "00001101", "00001105", "0000112f", NULL +}; + +/* + * This function reads data from file descriptor and + * prints it to the user + */ +static void receive_from_client(struct pollfd *pollfd) +{ + char buf[16]; + /* + * Buffer for lines: + * 41 42 43 20 20 00 31 32 00 07 04 00 00 00 00 00 ABC .12..... + */ + char outbuf[sizeof(buf) * 4 + 2]; + int i; + int ret; + + if (pollfd->revents & POLLHUP) { + haltest_error("Disconnected fd=%d\n", pollfd->fd); + poll_unregister_fd(pollfd->fd, receive_from_client); + } else if (pollfd->revents & POLLIN) { + haltest_info("receiving from client fd=%d\n", pollfd->fd); + + do { + memset(outbuf, ' ', sizeof(outbuf)); + outbuf[sizeof(outbuf) - 1] = 0; + ret = recv(pollfd->fd, buf, sizeof(buf), MSG_DONTWAIT); + + for (i = 0; i < ret; ++i) + sprintf(outbuf + i * 3, "%02X ", + (unsigned) buf[i]); + outbuf[i * 3] = ' '; + for (i = 0; i < ret; ++i) + sprintf(outbuf + 48 + i, "%c", + (isprint(buf[i]) ? buf[i] : '.')); + if (ret > 0) + haltest_info("%s\n", outbuf); + } while (ret > 0); + } else { + /* For now disconnect on all other events */ + haltest_error("Poll event %x\n", pollfd->revents); + poll_unregister_fd(pollfd->fd, receive_from_client); + } +} + +/* + * This function read from fd socket information about + * connected socket + */ +static void receive_sock_connect_signal(struct pollfd *pollfd) +{ + sock_connect_signal_t cs; + char addr_str[MAX_ADDR_STR_LEN]; + + if (pollfd->revents & POLLIN) { + int ret; + + poll_unregister_fd(pollfd->fd, receive_sock_connect_signal); + ret = read(pollfd->fd, &cs, sizeof(cs)); + if (ret != sizeof(cs)) { + haltest_info("Read on connect return %d\n", ret); + return; + } + + haltest_info("Connection to %s channel %d status=%d\n", + bt_bdaddr_t2str(&cs.bd_addr, addr_str), + cs.channel, cs.status); + + if (cs.status == 0) + poll_register_fd(pollfd->fd, POLLIN, + receive_from_client); + } + + if (pollfd->revents & POLLHUP) { + haltest_error("Disconnected fd=%d revents=0x%X\n", pollfd->fd, + pollfd->revents); + poll_unregister_fd(pollfd->fd, receive_sock_connect_signal); + } +} + +/* + * This function read from fd socket information about + * incoming connection and starts monitoring new connection + * on file descriptor read from fd. + */ +static void read_accepted(int fd) +{ + int ret; + struct msghdr msg; + struct iovec iv; + char cmsgbuf[CMSG_SPACE(1)]; + struct cmsghdr *cmsgptr; + sock_connect_signal_t cs; + int accepted_fd = -1; + char addr_str[MAX_ADDR_STR_LEN]; + + memset(&msg, 0, sizeof(msg)); + memset(&iv, 0, sizeof(iv)); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + + iv.iov_base = &cs; + iv.iov_len = sizeof(cs); + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + do { + ret = recvmsg(fd, &msg, MSG_NOSIGNAL); + } while (ret < 0 && errno == EINTR); + + if (ret < 16 || + (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) + haltest_error("Failed to accept connection\n"); + + for (cmsgptr = CMSG_FIRSTHDR(&msg); + cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) { + int count; + + if (cmsgptr->cmsg_level != SOL_SOCKET || + cmsgptr->cmsg_type != SCM_RIGHTS) + continue; + + memcpy(&accepted_fd, CMSG_DATA(cmsgptr), sizeof(accepted_fd)); + count = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int)); + + if (count != 1) + haltest_error("Failed to accept descriptors count=%d\n", + count); + + break; + } + + haltest_info("Incoming connection from %s channel %d status=%d fd=%d\n", + bt_bdaddr_t2str(&cs.bd_addr, addr_str), + cs.channel, cs.status, accepted_fd); + poll_register_fd(accepted_fd, POLLIN, receive_from_client); +} + +/* handles incoming connections on socket */ +static void client_connected(struct pollfd *pollfd) +{ + haltest_info("client connected %x\n", pollfd->revents); + + if (pollfd->revents & POLLHUP) + poll_unregister_fd(pollfd->fd, client_connected); + else if (pollfd->revents & POLLIN) + read_accepted(pollfd->fd); +} + +/* listen */ + +static void listen_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *user = TYPE_ENUM(btsock_type_t); + *enum_func = enum_defines; + } else if (argc == 5) { + *user = (void *) uuids; + *enum_func = enum_strings; + } +} + +static void listen_p(int argc, const char **argv) +{ + btsock_type_t type; + const char *service_name; + bt_uuid_t service_uuid; + int channel; + int sock_fd = -1; + int flags; + + RETURN_IF_NULL(if_sock); + + /* Socket type */ + if (argc < 3) { + haltest_error("No socket type specified\n"); + return; + } + type = str2btsock_type_t(argv[2]); + if ((int) type == -1) + type = atoi(argv[2]); + + /* service name */ + if (argc < 4) { + haltest_error("No service name specified\n"); + return; + } + service_name = argv[3]; + + /* uuid */ + if (argc < 5) { + haltest_error("No uuid specified\n"); + return; + } + str2bt_uuid_t(argv[4], &service_uuid); + + /* channel */ + channel = argc > 5 ? atoi(argv[5]) : 0; + + /* flags */ + flags = argc > 6 ? atoi(argv[6]) : 0; + + if (listen_fd_count >= MAX_LISTEN_FD) { + haltest_error("Max (%d) listening sockets exceeded\n", + listen_fd_count); + return; + } + EXEC(if_sock->listen, type, service_name, + &service_uuid.uu[0], channel, &sock_fd, flags); + if (sock_fd > 0) { + int channel = 0; + int ret = read(sock_fd, &channel, 4); + if (ret != 4) + haltest_info("Read channel failed\n"); + haltest_info("Channel returned from first read %d\n", channel); + listen_fd[listen_fd_count++] = sock_fd; + poll_register_fd(sock_fd, POLLIN, client_connected); + } +} + +/* connect */ + +static void connect_c(int argc, const char **argv, enum_func *enum_func, + void **user) +{ + if (argc == 3) { + *enum_func = enum_devices; + } else if (argc == 4) { + *user = TYPE_ENUM(btsock_type_t); + *enum_func = enum_defines; + } else if (argc == 5) { + *user = (void *) uuids; + *enum_func = enum_strings; + } +} + +static void connect_p(int argc, const char **argv) +{ + bt_bdaddr_t addr; + btsock_type_t type; + bt_uuid_t uuid; + int channel; + int sock_fd = -1; + int flags; + + /* Address */ + if (argc <= 2) { + haltest_error("No address specified\n"); + return; + } + str2bt_bdaddr_t(argv[2], &addr); + + /* Socket type */ + if (argc <= 3) { + haltest_error("No socket type specified\n"); + return; + } + type = str2btsock_type_t(argv[3]); + if ((int) type == -1) + type = atoi(argv[3]); + + /* uuid */ + if (argc <= 4) { + haltest_error("No uuid specified\n"); + return; + } + str2bt_uuid_t(argv[4], &uuid); + + /* channel */ + if (argc <= 5) { + haltest_error("No channel specified\n"); + return; + } + channel = atoi(argv[5]); + + /* flags */ + flags = argc <= 6 ? 0 : atoi(argv[6]); + + RETURN_IF_NULL(if_sock); + + EXEC(if_sock->connect, &addr, type, &uuid.uu[0], channel, &sock_fd, + flags); + if (sock_fd > 0) { + int channel = 0; + int ret = read(sock_fd, &channel, 4); + + if (ret != 4) + haltest_info("Read channel failed\n"); + haltest_info("Channel returned from first read %d\n", channel); + listen_fd[listen_fd_count++] = sock_fd; + poll_register_fd(sock_fd, POLLIN, receive_sock_connect_signal); + } +} + +/* Methods available in btsock_interface_t */ +static struct method methods[] = { + STD_METHODCH(listen, + " [] []"), + STD_METHODCH(connect, + " []"), + END_METHOD +}; + +const struct interface sock_if = { + .name = "socket", + .methods = methods +}; diff -Nru bluez-4.101/android/client/pollhandler.c bluez-5.23/android/client/pollhandler.c --- bluez-4.101/android/client/pollhandler.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/pollhandler.c 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "pollhandler.h" + +/* + * Code that allows to poll multiply file descriptors for events + * File descriptors can be added and removed at runtime + * + * Call poll_register_fd function first to add file descriptors to monitor + * Then call poll_dispatch_loop that will poll all registered file descriptors + * as long as they are not unregistered. + * + * When event happen on given fd appropriate user supplied handler is called + */ + +/* Maximum number of files to monitor */ +#define MAX_OPEN_FD 10 + +/* Storage for pollfd structures for monitored file descriptors */ +static struct pollfd fds[MAX_OPEN_FD]; +static poll_handler fds_handler[MAX_OPEN_FD]; +/* Number of registered file descriptors */ +static int fds_count = 0; + +/* + * Function polls file descriptor in loop and calls appropriate handler + * on event. Function returns when there is no more file descriptor to + * monitor + */ +void poll_dispatch_loop(void) +{ + while (fds_count > 0) { + int i; + int cur_fds_count = fds_count; + int ready = poll(fds, fds_count, 1000); + + for (i = 0; i < fds_count && ready > 0; ++i) { + if (fds[i].revents == 0) + continue; + + fds_handler[i](fds + i); + ready--; + /* + * If handler was remove from table + * just skip the rest and poll again + * This is due to reordering of tables in + * register/unregister functions + */ + if (cur_fds_count != fds_count) + break; + } + } +} + +/* + * Registers file descriptor to be monitored for events (see man poll(2)) + * for events. + * + * return non negative value on success + * -EMFILE when there are to much descriptors + */ +int poll_register_fd(int fd, short events, poll_handler ph) +{ + if (fds_count >= MAX_OPEN_FD) + return -EMFILE; + + fds_handler[fds_count] = ph; + fds[fds_count].fd = fd; + fds[fds_count].events = events; + fds_count++; + + return fds_count; +} + +/* + * Unregisters file descriptor + * Both fd and ph must match previously registered data + * + * return 0 if unregister succeeded + * -EBADF if arguments do not match any register handler + */ +int poll_unregister_fd(int fd, poll_handler ph) +{ + int i; + + for (i = 0; i < fds_count; ++i) { + if (fds_handler[i] == ph && fds[i].fd == fd) { + fds_count--; + if (i < fds_count) { + fds[i].fd = fds[fds_count].fd; + fds[i].events = fds[fds_count].events; + fds_handler[i] = fds_handler[fds_count]; + } + return 0; + } + } + return -EBADF; +} diff -Nru bluez-4.101/android/client/pollhandler.h bluez-5.23/android/client/pollhandler.h --- bluez-4.101/android/client/pollhandler.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/pollhandler.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +/* Function to be called when there are event for some descriptor */ +typedef void (*poll_handler)(struct pollfd *pollfd); + +int poll_register_fd(int fd, short events, poll_handler ph); +int poll_unregister_fd(int fd, poll_handler ph); + +void poll_dispatch_loop(void); diff -Nru bluez-4.101/android/client/tabcompletion.c bluez-5.23/android/client/tabcompletion.c --- bluez-4.101/android/client/tabcompletion.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/tabcompletion.c 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include "if-main.h" +#include "terminal.h" + +/* how many times tab was hit */ +static int tab_hit_count; + +typedef struct split_arg { + struct split_arg *next; /* next argument in buffer */ + const char *origin; /* pointer to original argument */ + char ntcopy[1]; /* null terminated copy of argument */ +} split_arg_t; + +/* function returns method of given name or NULL if not found */ +const struct method *get_interface_method(const char *iname, + const char *mname) +{ + const struct interface *iface = get_interface(iname); + + if (iface == NULL) + return NULL; + + return get_method(iface->methods, mname); +} + +/* prints matching elements */ +static void print_matches(enum_func f, void *user, const char *prefix, int len) +{ + int i; + const char *enum_name; + + putchar('\n'); + for (i = 0; NULL != (enum_name = f(user, i)); ++i) { + if (strncmp(enum_name, prefix, len) == 0) + printf("%s\t", enum_name); + } + putchar('\n'); + terminal_draw_command_line(); +} + +/* + * This function splits command line into linked list of arguments. + * line_buffer - pointer to input command line + * size - size of command line to parse + * buf - output buffer to keep split arguments list + * buf_size_in_bytes - size of buf + */ +static int split_command(const char *line_buffer, int size, split_arg_t *buf, + int buf_size_in_bytes) +{ + split_arg_t *prev = NULL; + split_arg_t *arg = buf; + int argc = 0; + const char *p = line_buffer; + const char *e = p + (size > 0 ? size : (int) strlen(p)); + int len; + + do { + while (p < e && isspace(*p)) + p++; + arg->origin = p; + arg->next = NULL; + while (p < e && !isspace(*p)) + p++; + len = p - arg->origin; + if (&arg->ntcopy[0] + len + 1 > + (const char *) buf + buf_size_in_bytes) + break; + strncpy(arg->ntcopy, arg->origin, len); + arg->ntcopy[len] = 0; + if (prev != NULL) + prev->next = arg; + prev = arg; + arg += (2 * sizeof(*arg) + len) / sizeof(*arg); + argc++; + } while (p < e); + + return argc; +} + +/* Function to enumerate method names */ +static const char *methods_name(void *v, int i) +{ + const struct interface *iface = v; + + return iface->methods[i].name[0] ? iface->methods[i].name : NULL; +} + +struct command_completion_args; +typedef void (*short_help)(struct command_completion_args *args); + +struct command_completion_args { + const split_arg_t *arg; /* list of arguments */ + const char *typed; /* last typed element */ + enum_func func; /* enumerating function */ + void *user; /* argument to enumerating function */ + short_help help; /* help function */ + const char *user_help; /* additional data (used by short_help) */ +}; + +/* complete command line */ +static void tab_completion(struct command_completion_args *args) +{ + const char *name = args->typed; + const int len = strlen(name); + int i; + int j; + char prefix[128] = {0}; + int prefix_len = 0; + int count = 0; + const char *enum_name; + + for (i = 0; NULL != (enum_name = args->func(args->user, i)); ++i) { + /* prefix does not match */ + if (strncmp(enum_name, name, len) != 0) + continue; + + /* prefix matches first time */ + if (count++ == 0) { + strcpy(prefix, enum_name); + prefix_len = strlen(prefix); + continue; + } + + /* Prefix matches next time reduce prefix to common part */ + for (j = 0; prefix[j] != 0 + && prefix[j] == enum_name[j];) + ++j; + prefix_len = j; + prefix[j] = 0; + } + + if (count == 0) { + /* no matches */ + if (args->help != NULL) + args->help(args); + tab_hit_count = 0; + return; + } + + /* len == prefix_len => nothing new was added */ + if (len == prefix_len) { + if (count != 1) { + if (tab_hit_count == 1) { + putchar('\a'); + } else if (tab_hit_count == 2 || + args->help == NULL) { + print_matches(args->func, + args->user, name, len); + } else { + args->help(args); + tab_hit_count = 1; + } + } else if (count == 1) { + /* nothing to add, exact match add space */ + terminal_insert_into_command_line(" "); + } + } else { + /* new chars can be added from some interface name(s) */ + if (count == 1) { + /* exact match, add space */ + prefix[prefix_len++] = ' '; + prefix[prefix_len] = '\0'; + } + + terminal_insert_into_command_line(prefix + len); + tab_hit_count = 0; + } +} + +/* interface completion */ +static void command_completion(split_arg_t *arg) +{ + struct command_completion_args args = { + .arg = arg, + .typed = arg->ntcopy, + .func = command_name + }; + + tab_completion(&args); +} + +/* method completion */ +static void method_completion(const struct interface *iface, split_arg_t *arg) +{ + struct command_completion_args args = { + .arg = arg, + .typed = arg->next->ntcopy, + .func = methods_name, + .user = (void *) iface + }; + + if (iface == NULL) + return; + + tab_completion(&args); +} + +static const char *bold = "\x1b[1m"; +static const char *normal = "\x1b[0m"; + +static bool find_nth_argument(const char *str, int n, const char **s, + const char **e) +{ + const char *p = str; + int argc = 0; + *e = NULL; + + while (p != NULL && *p != 0) { + + while (isspace(*p)) + ++p; + + if (n == argc) + *s = p; + + if (*p == '[') { + p = strchr(p, ']'); + if (p != NULL) + *e = ++p; + } else if (*p == '<') { + p = strchr(p, '>'); + if (p != NULL) + *e = ++p; + } else { + *e = strchr(p, ' '); + if (*e == NULL) + *e = p + strlen(p); + p = *e; + } + + if (n == argc) + break; + + argc++; + *e = NULL; + } + return *e != NULL; +} + +/* prints short help on method for interface */ +static void method_help(struct command_completion_args *args) +{ + int argc; + const split_arg_t *arg = args->arg; + const char *sb = NULL; + const char *eb = NULL; + const char *arg1 = ""; + int arg1_size = 0; /* size of method field (for methods > 0) */ + + if (args->user_help == NULL) + return; + + for (argc = 0; arg != NULL; argc++) + arg = arg->next; + + /* Check if this is method from interface */ + if (get_command(args->arg->ntcopy) == NULL) { + /* if so help is missing interface and method name */ + arg1 = args->arg->next->ntcopy; + arg1_size = strlen(arg1) + 1; + } + + find_nth_argument(args->user_help, argc - (arg1_size ? 3 : 2), + &sb, &eb); + + if (eb != NULL) + haltest_info("%s %-*s%.*s%s%.*s%s%s\n", args->arg->ntcopy, + arg1_size, arg1, (int) (sb - args->user_help), + args->user_help, bold, (int) (eb - sb), + sb, normal, eb); + else + haltest_info("%s %-*s%s\n", args->arg->ntcopy, + arg1_size, arg1, args->user_help); +} + +/* So we have empty enumeration */ +static const char *return_null(void *user, int i) +{ + return NULL; +} + +/* + * parameter completion function + * argc - number of elements in arg list + * arg - list of arguments + * method - method to get completion from (can be NULL) + */ +static void param_completion(int argc, const split_arg_t *arg, + const struct method *method, int hlpix) +{ + int i; + const char *argv[argc]; + const split_arg_t *tmp = arg; + struct command_completion_args args = { + .arg = arg, + .func = return_null + }; + + /* prepare standard argv from arg */ + for (i = 0; i < argc; ++i) { + argv[i] = tmp->ntcopy; + tmp = tmp->next; + } + + if (method != NULL && method->complete != NULL) { + /* ask method for completion function */ + method->complete(argc, argv, &args.func, &args.user); + } + + /* If method provided enumeration function call try to complete */ + if (args.func != NULL) { + args.typed = argv[argc - 1]; + args.help = method_help; + args.user_help = method ? method->help : NULL; + + tab_completion(&args); + } +} + +/* + * This method gets called when user tapped tab key. + * line - points to command line + * len - size of line that should be used for completions. This should be + * cursor position during tab hit. + */ +void process_tab(const char *line, int len) +{ + int argc; + static split_arg_t buf[(LINE_BUF_MAX * 2) / sizeof(split_arg_t)]; + const struct method *method; + + argc = split_command(line, len, buf, sizeof(buf)); + tab_hit_count++; + + if (argc == 0) + return; + + if (argc == 1) { + command_completion(buf); + return; + } + + method = get_command(buf[0].ntcopy); + if (method != NULL) { + param_completion(argc, buf, method, 1); + } else if (argc == 2) { + method_completion(get_interface(buf[0].ntcopy), buf); + } else { + /* Find method for pair */ + method = get_interface_method(buf[0].ntcopy, + buf[0].next->ntcopy); + param_completion(argc, buf, method, 2); + } +} diff -Nru bluez-4.101/android/client/terminal.c bluez-5.23/android/client/terminal.c --- bluez-4.101/android/client/terminal.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/terminal.c 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,824 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "terminal.h" +#include "history.h" + +/* + * Character sequences recognized by code in this file + * Leading ESC 0x1B is not included + */ +#define SEQ_INSERT "[2~" +#define SEQ_DELETE "[3~" +#define SEQ_HOME "OH" +#define SEQ_END "OF" +#define SEQ_PGUP "[5~" +#define SEQ_PGDOWN "[6~" +#define SEQ_LEFT "[D" +#define SEQ_RIGHT "[C" +#define SEQ_UP "[A" +#define SEQ_DOWN "[B" +#define SEQ_STAB "[Z" +#define SEQ_M_n "n" +#define SEQ_M_p "p" +#define SEQ_CLEFT "[1;5D" +#define SEQ_CRIGHT "[1;5C" +#define SEQ_CUP "[1;5A" +#define SEQ_CDOWN "[1;5B" +#define SEQ_SLEFT "[1;2D" +#define SEQ_SRIGHT "[1;2C" +#define SEQ_SUP "[1;2A" +#define SEQ_SDOWN "[1;2B" +#define SEQ_MLEFT "[1;3D" +#define SEQ_MRIGHT "[1;3C" +#define SEQ_MUP "[1;3A" +#define SEQ_MDOWN "[1;3B" + +#define KEY_SEQUENCE(k) { KEY_##k, SEQ_##k } +struct ansii_sequence { + int code; + const char *sequence; +}; + +/* Table connects single int key codes with character sequences */ +static const struct ansii_sequence ansii_sequnces[] = { + KEY_SEQUENCE(INSERT), + KEY_SEQUENCE(DELETE), + KEY_SEQUENCE(HOME), + KEY_SEQUENCE(END), + KEY_SEQUENCE(PGUP), + KEY_SEQUENCE(PGDOWN), + KEY_SEQUENCE(LEFT), + KEY_SEQUENCE(RIGHT), + KEY_SEQUENCE(UP), + KEY_SEQUENCE(DOWN), + KEY_SEQUENCE(CLEFT), + KEY_SEQUENCE(CRIGHT), + KEY_SEQUENCE(CUP), + KEY_SEQUENCE(CDOWN), + KEY_SEQUENCE(SLEFT), + KEY_SEQUENCE(SRIGHT), + KEY_SEQUENCE(SUP), + KEY_SEQUENCE(SDOWN), + KEY_SEQUENCE(MLEFT), + KEY_SEQUENCE(MRIGHT), + KEY_SEQUENCE(MUP), + KEY_SEQUENCE(MDOWN), + KEY_SEQUENCE(STAB), + KEY_SEQUENCE(M_p), + KEY_SEQUENCE(M_n), + { 0, NULL } +}; + +#define KEY_SEQUNCE_NOT_FINISHED -1 +#define KEY_C_C 3 +#define KEY_C_D 4 +#define KEY_C_L 12 + +#define isseqence(c) ((c) == 0x1B) + +/* + * Number of characters that consist of ANSI sequence + * Should not be less then longest string in ansi_sequences + */ +#define MAX_ASCII_SEQUENCE 10 + +static char current_sequence[MAX_ASCII_SEQUENCE]; +static int current_sequence_len = -1; + +/* single line typed by user goes here */ +static char line_buf[LINE_BUF_MAX]; +/* index of cursor in input line */ +static int line_buf_ix = 0; +/* current length of input line */ +static int line_len = 0; + +/* line index used for fetching lines from history */ +static int line_index = 0; + +static char prompt_buf[10] = "> "; +static const char *const noprompt = ""; +static const char *current_prompt = prompt_buf; +static const char *prompt = prompt_buf; +/* + * Moves cursor to right or left + * + * n - positive - moves cursor right + * n - negative - moves cursor left + */ +static void terminal_move_cursor(int n) +{ + if (n < 0) { + for (; n < 0; n++) + putchar('\b'); + } else if (n > 0) { + printf("%*s", n, line_buf + line_buf_ix); + } +} + +/* Draw command line */ +void terminal_draw_command_line(void) +{ + /* + * this needs to be checked here since line_buf is not cleared + * before parsing event though line_len and line_buf_ix are + */ + if (line_len > 0) + printf("%s%s", prompt, line_buf); + else + printf("%s", prompt); + + /* move cursor to it's place */ + terminal_move_cursor(line_buf_ix - line_len); +} + +/* inserts string into command line at cursor position */ +void terminal_insert_into_command_line(const char *p) +{ + int len = strlen(p); + + if (line_len == line_buf_ix) { + strcat(line_buf, p); + printf("%s", p); + line_len = line_len + len; + line_buf_ix = line_len; + } else { + memmove(line_buf + line_buf_ix + len, + line_buf + line_buf_ix, line_len - line_buf_ix + 1); + memmove(line_buf + line_buf_ix, p, len); + printf("%s", line_buf + line_buf_ix); + line_buf_ix += len; + line_len += len; + terminal_move_cursor(line_buf_ix - line_len); + } +} + +/* Prints string and redraws command line */ +int terminal_print(const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + + ret = terminal_vprint(format, args); + + va_end(args); + return ret; +} + +/* Prints string and redraws command line */ +int terminal_vprint(const char *format, va_list args) +{ + int ret; + + printf("\r%*s\r", (int) line_len + 1, " "); + + ret = vprintf(format, args); + + terminal_draw_command_line(); + + fflush(stdout); + + return ret; +} + +/* + * Call this when text in line_buf was changed + * and line needs to be redrawn + */ +static void terminal_line_replaced(void) +{ + int len = strlen(line_buf); + + /* line is shorter that previous */ + if (len < line_len) { + /* if new line is shorter move cursor to end of new end */ + while (line_buf_ix > len) { + putchar('\b'); + line_buf_ix--; + } + + /* If cursor was not at the end, move it to the end */ + if (line_buf_ix < line_len) + printf("%.*s", line_len - line_buf_ix, + line_buf + line_buf_ix); + /* over write end of previous line */ + while (line_len >= len++) + putchar(' '); + } + + /* draw new line */ + printf("\r%s%s", prompt, line_buf); + /* set up indexes to new line */ + line_len = strlen(line_buf); + line_buf_ix = line_len; + fflush(stdout); +} + +static void terminal_clear_line(void) +{ + line_buf[0] = '\0'; + terminal_line_replaced(); +} + +static void terminal_clear_screen(void) +{ + line_buf[0] = '\0'; + line_buf_ix = 0; + line_len = 0; + + printf("\x1b[2J\x1b[1;1H%s", prompt); +} + +static void terminal_delete_char(void) +{ + /* delete character under cursor if not at the very end */ + if (line_buf_ix >= line_len) + return; + /* + * Prepare buffer with one character missing + * trailing 0 is moved + */ + line_len--; + memmove(line_buf + line_buf_ix, line_buf + line_buf_ix + 1, + line_len - line_buf_ix + 1); + /* print rest of line from current cursor position */ + printf("%s \b", line_buf + line_buf_ix); + /* move back cursor */ + terminal_move_cursor(line_buf_ix - line_len); +} + +/* + * Function tries to replace current line with specified line in history + * new_line_index - new line to show, -1 to show oldest + */ +static void terminal_get_line_from_history(int new_line_index) +{ + new_line_index = history_get_line(new_line_index, + line_buf, LINE_BUF_MAX); + + if (new_line_index >= 0) { + terminal_line_replaced(); + line_index = new_line_index; + } +} + +/* + * Function searches history back or forward for command line that starts + * with characters up to cursor position + * + * back - true - searches backward + * back - false - searches forward (more recent commands) + */ +static void terminal_match_hitory(bool back) +{ + char buf[line_buf_ix + 1]; + int line; + int matching_line = -1; + int dir = back ? 1 : -1; + + line = line_index + dir; + while (matching_line == -1 && line >= 0) { + int new_line_index; + + new_line_index = history_get_line(line, buf, line_buf_ix + 1); + if (new_line_index < 0) + break; + + if (0 == strncmp(line_buf, buf, line_buf_ix)) + matching_line = line; + line += dir; + } + + if (matching_line >= 0) { + int pos = line_buf_ix; + terminal_get_line_from_history(matching_line); + /* move back to cursor position to original place */ + line_buf_ix = pos; + terminal_move_cursor(pos - line_len); + } +} + +/* + * Converts terminal character sequences to single value representing + * keyboard keys + */ +static int terminal_convert_sequence(int c) +{ + int i; + + /* Not in sequence yet? */ + if (current_sequence_len == -1) { + /* Is ansi sequence detected by 0x1B ? */ + if (isseqence(c)) { + current_sequence_len++; + return KEY_SEQUNCE_NOT_FINISHED; + } + + return c; + } + + /* Inside sequence */ + current_sequence[current_sequence_len++] = c; + current_sequence[current_sequence_len] = '\0'; + for (i = 0; ansii_sequnces[i].code; ++i) { + /* Matches so far? */ + if (0 != strncmp(current_sequence, ansii_sequnces[i].sequence, + current_sequence_len)) + continue; + + /* Matches as a whole? */ + if (ansii_sequnces[i].sequence[current_sequence_len] == 0) { + current_sequence_len = -1; + return ansii_sequnces[i].code; + } + + /* partial match (not whole sequence yet) */ + return KEY_SEQUNCE_NOT_FINISHED; + } + + terminal_print("ansi char 0x%X %c\n", c); + /* + * Sequence does not match + * mark that no in sequence any more, return char + */ + current_sequence_len = -1; + return c; +} + +typedef void (*terminal_action)(int c, line_callback process_line); + +#define TERMINAL_ACTION(n) \ + static void n(int c, void (*process_line)(char *line)) + +TERMINAL_ACTION(terminal_action_null) +{ +} + +/* Mapping between keys and function */ +typedef struct { + int key; + terminal_action func; +} KeyAction; + +int action_keys[] = { + KEY_SEQUNCE_NOT_FINISHED, + KEY_LEFT, + KEY_RIGHT, + KEY_HOME, + KEY_END, + KEY_DELETE, + KEY_CLEFT, + KEY_CRIGHT, + KEY_SUP, + KEY_SDOWN, + KEY_UP, + KEY_DOWN, + KEY_BACKSPACE, + KEY_INSERT, + KEY_PGUP, + KEY_PGDOWN, + KEY_CUP, + KEY_CDOWN, + KEY_SLEFT, + KEY_SRIGHT, + KEY_MLEFT, + KEY_MRIGHT, + KEY_MUP, + KEY_MDOWN, + KEY_STAB, + KEY_M_n, + KEY_M_p, + KEY_C_C, + KEY_C_D, + KEY_C_L, + '\t', + '\r', + '\n', +}; + +#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) + +/* + * current_actions holds all recognizable kes and actions for them + * additional element (index 0) is used for default action + */ +static KeyAction current_actions[NELEM(action_keys) + 1]; + +/* KeyAction comparator by key, for qsort and bsearch */ +static int KeyActionKeyCompare(const void *a, const void *b) +{ + return ((const KeyAction *) a)->key - ((const KeyAction *) b)->key; +} + +/* Find action by key, NULL if no action for this key */ +static KeyAction *terminal_get_action(int key) +{ + KeyAction a = { .key = key }; + + return bsearch(&a, current_actions + 1, NELEM(action_keys), sizeof(a), + KeyActionKeyCompare); +} + +/* Sets new set of actions to use */ +static void terminal_set_actions(const KeyAction *actions) +{ + int i; + + /* Make map with empty function for every key */ + for (i = 0; i < NELEM(action_keys); ++i) { + /* + * + 1 due to 0 index reserved for default action that is + * called for non mapped key + */ + current_actions[i + 1].key = action_keys[i]; + current_actions[i + 1].func = terminal_action_null; + } + + /* Sort action from 1 (index 0 - default action) */ + qsort(current_actions + 1, NELEM(action_keys), sizeof(KeyAction), + KeyActionKeyCompare); + /* Set default action (first in array) */ + current_actions[0] = *actions++; + + /* Copy rest of actions into their places */ + for (; actions->key; ++actions) { + KeyAction *place = terminal_get_action(actions->key); + + if (place) + place->func = actions->func; + } +} + +TERMINAL_ACTION(terminal_action_left) +{ + /* if not at the beginning move to previous character */ + if (line_buf_ix <= 0) + return; + line_buf_ix--; + terminal_move_cursor(-1); +} + +TERMINAL_ACTION(terminal_action_right) +{ + /* + * If not at the end, just print current character + * and modify position + */ + if (line_buf_ix < line_len) + putchar(line_buf[line_buf_ix++]); +} + +TERMINAL_ACTION(terminal_action_home) +{ + /* move to beginning of line and update position */ + printf("\r%s", prompt); + line_buf_ix = 0; +} + +TERMINAL_ACTION(terminal_action_end) +{ + /* if not at the end of line */ + if (line_buf_ix < line_len) { + /* print everything from cursor */ + printf("%s", line_buf + line_buf_ix); + /* just modify current position */ + line_buf_ix = line_len; + } +} + +TERMINAL_ACTION(terminal_action_del) +{ + terminal_delete_char(); +} + +TERMINAL_ACTION(terminal_action_word_left) +{ + int old_pos; + /* + * Move by word left + * + * Are we at the beginning of line? + */ + if (line_buf_ix <= 0) + return; + + old_pos = line_buf_ix; + line_buf_ix--; + /* skip spaces left */ + while (line_buf_ix && isspace(line_buf[line_buf_ix])) + line_buf_ix--; + + /* skip all non spaces to the left */ + while (line_buf_ix > 0 && + !isspace(line_buf[line_buf_ix - 1])) + line_buf_ix--; + + /* move cursor to new position */ + terminal_move_cursor(line_buf_ix - old_pos); +} + +TERMINAL_ACTION(terminal_action_word_right) +{ + int old_pos; + /* + * Move by word right + * + * are we at the end of line? + */ + if (line_buf_ix >= line_len) + return; + + old_pos = line_buf_ix; + /* skip all spaces */ + while (line_buf_ix < line_len && isspace(line_buf[line_buf_ix])) + line_buf_ix++; + + /* skip all non spaces */ + while (line_buf_ix < line_len && !isspace(line_buf[line_buf_ix])) + line_buf_ix++; + /* + * Move cursor to right by printing text + * between old cursor and new + */ + if (line_buf_ix > old_pos) + printf("%.*s", (int) (line_buf_ix - old_pos), + line_buf + old_pos); +} + +TERMINAL_ACTION(terminal_action_history_begin) +{ + terminal_get_line_from_history(-1); +} + +TERMINAL_ACTION(terminal_action_history_end) +{ + if (line_index > 0) + terminal_get_line_from_history(0); +} + +TERMINAL_ACTION(terminal_action_history_up) +{ + terminal_get_line_from_history(line_index + 1); +} + +TERMINAL_ACTION(terminal_action_history_down) +{ + if (line_index > 0) + terminal_get_line_from_history(line_index - 1); +} + +TERMINAL_ACTION(terminal_action_tab) +{ + /* tab processing */ + process_tab(line_buf, line_buf_ix); +} + + +TERMINAL_ACTION(terminal_action_backspace) +{ + if (line_buf_ix <= 0) + return; + + if (line_buf_ix == line_len) { + printf("\b \b"); + line_len = --line_buf_ix; + line_buf[line_len] = 0; + } else { + putchar('\b'); + line_buf_ix--; + line_len--; + memmove(line_buf + line_buf_ix, + line_buf + line_buf_ix + 1, + line_len - line_buf_ix + 1); + printf("%s \b", line_buf + line_buf_ix); + terminal_move_cursor(line_buf_ix - line_len); + } +} + +TERMINAL_ACTION(terminal_action_find_history_forward) +{ + /* Search history forward */ + terminal_match_hitory(false); +} + +TERMINAL_ACTION(terminal_action_find_history_backward) +{ + /* Search history forward */ + terminal_match_hitory(true); +} + +TERMINAL_ACTION(terminal_action_ctrl_c) +{ + terminal_clear_line(); +} + +TERMINAL_ACTION(terminal_action_ctrl_d) +{ + if (line_len > 0) { + terminal_delete_char(); + } else { + puts(""); + exit(0); + } +} + +TERMINAL_ACTION(terminal_action_clear_screen) +{ + terminal_clear_screen(); +} + +TERMINAL_ACTION(terminal_action_enter) +{ + /* + * On new line add line to history + * forget history position + */ + history_add_line(line_buf); + line_len = 0; + line_buf_ix = 0; + line_index = -1; + /* print new line */ + putchar(c); + prompt = noprompt; + process_line(line_buf); + /* clear current line */ + line_buf[0] = '\0'; + prompt = current_prompt; + printf("%s", prompt); +} + +TERMINAL_ACTION(terminal_action_default) +{ + char str[2] = { c, 0 }; + + if (!isprint(c)) + /* + * TODO: remove this print once all meaningful sequences + * are identified + */ + printf("char-0x%02x\n", c); + else if (line_buf_ix < LINE_BUF_MAX - 1) + terminal_insert_into_command_line(str); +} + +/* Callback to call when user hit enter during prompt for */ +static line_callback prompt_callback; + +static KeyAction normal_actions[] = { + { 0, terminal_action_default }, + { KEY_LEFT, terminal_action_left }, + { KEY_RIGHT, terminal_action_right }, + { KEY_HOME, terminal_action_home }, + { KEY_END, terminal_action_end }, + { KEY_DELETE, terminal_action_del }, + { KEY_CLEFT, terminal_action_word_left }, + { KEY_CRIGHT, terminal_action_word_right }, + { KEY_SUP, terminal_action_history_begin }, + { KEY_SDOWN, terminal_action_history_end }, + { KEY_UP, terminal_action_history_up }, + { KEY_DOWN, terminal_action_history_down }, + { '\t', terminal_action_tab }, + { KEY_BACKSPACE, terminal_action_backspace }, + { KEY_M_n, terminal_action_find_history_forward }, + { KEY_M_p, terminal_action_find_history_backward }, + { KEY_C_C, terminal_action_ctrl_c }, + { KEY_C_D, terminal_action_ctrl_d }, + { KEY_C_L, terminal_action_clear_screen }, + { '\r', terminal_action_enter }, + { '\n', terminal_action_enter }, + { 0, NULL }, +}; + +TERMINAL_ACTION(terminal_action_answer) +{ + putchar(c); + + terminal_set_actions(normal_actions); + /* Restore default prompt */ + current_prompt = prompt_buf; + + /* No prompt for prints */ + prompt = noprompt; + line_buf_ix = 0; + line_len = 0; + /* Call user function with what was typed */ + prompt_callback(line_buf); + + line_buf[0] = 0; + /* promot_callback could change current_prompt */ + prompt = current_prompt; + + printf("%s", prompt); +} + +TERMINAL_ACTION(terminal_action_prompt_ctrl_c) +{ + printf("^C\n"); + line_buf_ix = 0; + line_len = 0; + line_buf[0] = 0; + + current_prompt = prompt_buf; + prompt = current_prompt; + terminal_set_actions(normal_actions); + + printf("%s", prompt); +} + +static KeyAction prompt_actions[] = { + { 0, terminal_action_default }, + { KEY_LEFT, terminal_action_left }, + { KEY_RIGHT, terminal_action_right }, + { KEY_HOME, terminal_action_home }, + { KEY_END, terminal_action_end }, + { KEY_DELETE, terminal_action_del }, + { KEY_CLEFT, terminal_action_word_left }, + { KEY_CRIGHT, terminal_action_word_right }, + { KEY_BACKSPACE, terminal_action_backspace }, + { KEY_C_C, terminal_action_prompt_ctrl_c }, + { KEY_C_D, terminal_action_ctrl_d }, + { '\r', terminal_action_answer }, + { '\n', terminal_action_answer }, + { 0, NULL }, +}; + +void terminal_process_char(int c, line_callback process_line) +{ + KeyAction *a; + + c = terminal_convert_sequence(c); + + /* Get action for this key */ + a = terminal_get_action(c); + + /* No action found, get default one */ + if (a == NULL) + a = ¤t_actions[0]; + + a->func(c, process_line); + fflush(stdout); +} + +void terminal_prompt_for(const char *s, line_callback process_line) +{ + current_prompt = s; + if (prompt != noprompt) { + prompt = s; + terminal_clear_line(); + } + prompt_callback = process_line; + terminal_set_actions(prompt_actions); +} + +static struct termios origianl_tios; + +static void terminal_cleanup(void) +{ + tcsetattr(0, TCSANOW, &origianl_tios); +} + +void terminal_setup(void) +{ + struct termios tios; + + terminal_set_actions(normal_actions); + + tcgetattr(0, &origianl_tios); + tios = origianl_tios; + + /* + * Turn off echo since all editing is done by hand, + * Ctrl-c handled internally + */ + tios.c_lflag &= ~(ICANON | ECHO | BRKINT | IGNBRK); + tcsetattr(0, TCSANOW, &tios); + + /* Restore terminal at exit */ + atexit(terminal_cleanup); + + printf("%s", prompt); + fflush(stdout); +} diff -Nru bluez-4.101/android/client/terminal.h bluez-5.23/android/client/terminal.h --- bluez-4.101/android/client/terminal.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/client/terminal.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +/* size of supported line */ +#define LINE_BUF_MAX 1024 + +enum key_codes { + KEY_BACKSPACE = 0x7F, + KEY_INSERT = 1000, /* arbitrary value */ + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PGUP, + KEY_PGDOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_UP, + KEY_DOWN, + KEY_CLEFT, + KEY_CRIGHT, + KEY_CUP, + KEY_CDOWN, + KEY_SLEFT, + KEY_SRIGHT, + KEY_SUP, + KEY_SDOWN, + KEY_MLEFT, + KEY_MRIGHT, + KEY_MUP, + KEY_MDOWN, + KEY_STAB, + KEY_M_p, + KEY_M_n +}; + +typedef void (*line_callback)(char *); + +void terminal_setup(void); +int terminal_print(const char *format, ...); +int terminal_vprint(const char *format, va_list args); +void terminal_process_char(int c, line_callback process_line); +void terminal_insert_into_command_line(const char *p); +void terminal_draw_command_line(void); +void terminal_prompt_for(const char *s, line_callback process_line); + +void process_tab(const char *line, int len); diff -Nru bluez-4.101/android/cts.txt bluez-5.23/android/cts.txt --- bluez-4.101/android/cts.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/cts.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,58 @@ +Android Compatibility Test Suite results + +Tested: 11-July-2014 +Android version: 4.4.4 +CTS version: 4.4 R3 + +Note: +CTS reliable write GATT tests require using CTS from master branch +or https://android-review.googlesource.com/99354 applied. + +(*) Due to bug in Bluedroid L2CAP implementation GATT server connect tests fails +without modified Linux kernel. Patches for Linux and/or Bluedroid are still TBD. + +------------------------------------------------------------------------------- +android.bluetooth.cts.BasicAdapterTest (automated tests) +Test Name Result Notes +------------------------------------------------------------------------------- +testAndroidTestCaseSetupProperly PASS +test_checkBluetoothAddress PASS +test_enableDisable PASS +test_getAddress PASS +test_getBondedDevices PASS +test_getDefaultAdapter PASS +test_getName PASS +test_getRemoteDevice PASS +test_listenUsingRfcommWithServiceRecord PASS +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +com.android.cts.verifier (manual tests) +Test Name Result Notes +------------------------------------------------------------------------------- +Toggle Bluetooth PASS +BLE Client Test: + connect PASS + discover service PASS + read/write characteristic PASS + reliable write PASS + notify characteristic PASS + read/write descriptor PASS + read RSSI PASS + disconnect PASS +BLE Server Test: + add service PASS + connection FAIL (*) bluedroid issue. + read characteristic request FAIL (*) + write characteristic request FAIL (*) + read descriptor request FAIL (*) + write descriptor request FAIL (*) + reliable write FAIL (*) + disconnection FAIL (*) +Insecure Client PASS +Insecure Server PASS Required kernel patch commit ID: + ba15a58b179ed76a7e887177f2b06de1 +Secure Client PASS +Secure Server PASS +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/cutils/properties.h bluez-5.23/android/cutils/properties.h --- bluez-4.101/android/cutils/properties.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/cutils/properties.h 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,94 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#define PROPERTY_VALUE_MAX 32 + +#define BLUETOOTH_MODE_PROPERTY_NAME "persist.sys.bluetooth.mode" +#define BLUETOOTH_MODE_PROPERTY_HANDSFREE "persist.sys.bluetooth.handsfree" + +static inline int property_get(const char *key, char *value, + const char *default_value) +{ + const char *prop = NULL; + + if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_NAME)) + prop = getenv("BLUETOOTH_MODE"); + + if (!strcmp(key, BLUETOOTH_MODE_PROPERTY_HANDSFREE)) + prop = getenv("BLUETOOTH_HANDSFREE_MODE"); + + if (!prop) + prop = default_value; + + if (prop) { + strncpy(value, prop, PROPERTY_VALUE_MAX); + + value[PROPERTY_VALUE_MAX - 1] = '\0'; + + return strlen(value); + } + + return 0; +} + +/* property_set: returns 0 on success, < 0 on failure +*/ +static inline int property_set(const char *key, const char *value) +{ + static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; + + struct sockaddr_un addr; + char msg[256]; + int fd, len; + + fd = socket(PF_LOCAL, SOCK_DGRAM, 0); + if (fd < 0) + return -1; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); + + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(fd); + return 0; + } + + len = snprintf(msg, sizeof(msg), "%s=%s", key, value); + + if (send(fd, msg, len + 1, 0) < 0) { + close(fd); + return -1; + } + + close(fd); + + return 0; +} diff -Nru bluez-4.101/android/dis.c bluez-5.23/android/dis.c --- bluez-4.101/android/dis.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/dis.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,201 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "src/log.h" + +#include "lib/uuid.h" +#include "src/shared/util.h" + +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" + +#include "android/dis.h" + +#define PNP_ID_SIZE 7 + +struct bt_dis { + int ref_count; + uint16_t handle; + uint8_t source; + uint16_t vendor; + uint16_t product; + uint16_t version; + GAttrib *attrib; /* GATT connection */ + struct gatt_primary *primary; /* Primary details */ + bt_dis_notify notify; + void *notify_data; +}; + +struct characteristic { + struct gatt_char attr; /* Characteristic */ + struct bt_dis *d; /* deviceinfo where the char belongs */ +}; + +static void dis_free(struct bt_dis *dis) +{ + bt_dis_detach(dis); + + g_free(dis->primary); + g_free(dis); +} + +struct bt_dis *bt_dis_new(void *primary) +{ + struct bt_dis *dis; + + dis = g_try_new0(struct bt_dis, 1); + if (!dis) + return NULL; + + if (primary) + dis->primary = g_memdup(primary, sizeof(*dis->primary)); + + return bt_dis_ref(dis); +} + +struct bt_dis *bt_dis_ref(struct bt_dis *dis) +{ + if (!dis) + return NULL; + + __sync_fetch_and_add(&dis->ref_count, 1); + + return dis; +} + +void bt_dis_unref(struct bt_dis *dis) +{ + if (!dis) + return; + + if (__sync_sub_and_fetch(&dis->ref_count, 1)) + return; + + dis_free(dis); +} + +static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct bt_dis *dis = user_data; + uint8_t value[PNP_ID_SIZE]; + ssize_t vlen; + + if (status != 0) { + error("Error reading PNP_ID value: %s", att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, len, value, sizeof(value)); + if (vlen < 0) { + error("Error reading PNP_ID: Protocol error"); + return; + } + + if (vlen < 7) { + error("Error reading PNP_ID: Invalid pdu length received"); + return; + } + + dis->source = value[0]; + dis->vendor = get_le16(&value[1]); + dis->product = get_le16(&value[3]); + dis->version = get_le16(&value[5]); + + DBG("source: 0x%02X vendor: 0x%04X product: 0x%04X version: 0x%04X", + dis->source, dis->vendor, dis->product, dis->version); + + if (dis->notify) + dis->notify(dis->source, dis->vendor, dis->product, + dis->version, dis->notify_data); +} + +static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics, + void *user_data) +{ + struct bt_dis *d = user_data; + GSList *l; + + if (status != 0) { + error("Discover deviceinfo characteristics: %s", + att_ecode2str(status)); + return; + } + + for (l = characteristics; l; l = l->next) { + struct gatt_char *c = l->data; + + if (strcmp(c->uuid, PNPID_UUID) == 0) { + d->handle = c->value_handle; + gatt_read_char(d->attrib, d->handle, read_pnpid_cb, d); + break; + } + } +} + +bool bt_dis_attach(struct bt_dis *dis, void *attrib) +{ + struct gatt_primary *primary = dis->primary; + + if (dis->attrib || !primary) + return false; + + dis->attrib = g_attrib_ref(attrib); + + if (!dis->handle) + gatt_discover_char(dis->attrib, primary->range.start, + primary->range.end, NULL, + configure_deviceinfo_cb, dis); + + return true; +} + +void bt_dis_detach(struct bt_dis *dis) +{ + if (!dis->attrib) + return; + + g_attrib_unref(dis->attrib); + dis->attrib = NULL; +} + +bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func, + void *user_data) +{ + if (!dis) + return false; + + dis->notify = func; + dis->notify_data = user_data; + + return true; +} diff -Nru bluez-4.101/android/dis.h bluez-5.23/android/dis.h --- bluez-4.101/android/dis.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/dis.h 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct bt_dis; + +struct bt_dis *bt_dis_new(void *primary); + +struct bt_dis *bt_dis_ref(struct bt_dis *dis); +void bt_dis_unref(struct bt_dis *dis); + +bool bt_dis_attach(struct bt_dis *dis, void *gatt); +void bt_dis_detach(struct bt_dis *dis); + +typedef void (*bt_dis_notify) (uint8_t source, uint16_t vendor, + uint16_t product, uint16_t version, + void *user_data); + +bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func, + void *user_data); diff -Nru bluez-4.101/android/gatt.c bluez-5.23/android/gatt.c --- bluez-4.101/android/gatt.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/gatt.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,6865 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "ipc.h" +#include "ipc-common.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "lib/uuid.h" +#include "bluetooth.h" +#include "gatt.h" +#include "src/log.h" +#include "hal-msg.h" +#include "utils.h" +#include "src/shared/util.h" +#include "src/shared/queue.h" +#include "src/shared/gatt-db.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "btio/btio.h" + +/* set according to Android bt_gatt_client.h */ +#define GATT_MAX_ATTR_LEN 600 + +#define GATT_SUCCESS 0x00000000 +#define GATT_FAILURE 0x00000101 + +#define BASE_UUID16_OFFSET 12 + +#define GATT_PERM_READ 0x00000001 +#define GATT_PERM_READ_ENCRYPTED 0x00000002 +#define GATT_PERM_READ_MITM 0x00000004 +#define GATT_PERM_READ_AUTHORIZATION 0x00000008 +#define GATT_PERM_WRITE 0x00000100 +#define GATT_PERM_WRITE_ENCRYPTED 0x00000200 +#define GATT_PERM_WRITE_MITM 0x00000400 +#define GATT_PERM_WRITE_AUTHORIZATION 0x00000800 +#define GATT_PERM_WRITE_SIGNED 0x00010000 +#define GATT_PERM_WRITE_SIGNED_MITM 0x00020000 +#define GATT_PERM_NONE 0x10000000 + +#define GATT_CONN_TIMEOUT 2 + +static const uint8_t BLUETOOTH_UUID[] = { + 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +typedef enum { + DEVICE_DISCONNECTED = 0, + DEVICE_CONNECT_INIT, /* connection procedure initiated */ + DEVICE_CONNECT_READY, /* dev found during LE scan */ + DEVICE_CONNECTED, /* connection has been established */ +} gatt_device_state_t; + +static const char *device_state_str[] = { + "DISCONNECTED", + "CONNECT INIT", + "CONNECT READY", + "CONNECTED", +}; + +struct pending_trans_data { + unsigned int id; + uint8_t opcode; +}; + +struct gatt_app { + int32_t id; + uint8_t uuid[16]; + + gatt_type_t type; + + /* Valid for client applications */ + struct queue *notifications; + + gatt_conn_cb_t func; +}; + +struct element_id { + bt_uuid_t uuid; + uint8_t instance; +}; + +struct descriptor { + struct element_id id; + uint16_t handle; +}; + +struct characteristic { + struct element_id id; + struct gatt_char ch; + uint16_t end_handle; + + struct queue *descriptors; +}; + +struct service { + struct element_id id; + struct gatt_primary prim; + struct gatt_included incl; + + bool primary; + + struct queue *chars; + struct queue *included; /* Valid only for primary services */ + bool incl_search_done; +}; + +struct notification_data { + struct hal_gatt_srvc_id service; + struct hal_gatt_gatt_id ch; + struct app_connection *conn; + guint notif_id; + guint ind_id; + int ref; +}; + +struct gatt_device { + bdaddr_t bdaddr; + uint8_t bdaddr_type; + + gatt_device_state_t state; + + GAttrib *attrib; + GIOChannel *att_io; + struct queue *services; + bool partial_srvc_search; + + guint watch_id; + guint server_id; + + int ref; + int conn_cnt; + + struct queue *autoconnect_apps; + + struct queue *pending_requests; +}; + +struct app_connection { + struct gatt_device *device; + struct gatt_app *app; + struct queue *transactions; + int32_t id; + + guint timeout_id; + + bool wait_execute_write; +}; + +struct service_sdp { + int32_t service_handle; + uint32_t sdp_handle; +}; + +static struct ipc *hal_ipc = NULL; +static bdaddr_t adapter_addr; +static bool scanning = false; +static unsigned int advertising_cnt = 0; + +static struct queue *gatt_apps = NULL; +static struct queue *gatt_devices = NULL; +static struct queue *app_connections = NULL; + +static struct queue *services_sdp = NULL; + +static struct queue *listen_apps = NULL; +static struct gatt_db *gatt_db = NULL; + +static uint16_t service_changed_handle = 0; + +static GIOChannel *le_io = NULL; +static GIOChannel *bredr_io = NULL; + +static uint32_t gatt_sdp_handle = 0; +static uint32_t gap_sdp_handle = 0; +static uint32_t dis_sdp_handle = 0; + +static struct bt_crypto *crypto = NULL; + +static int test_client_if = 0; +static const uint8_t TEST_UUID[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04 +}; + +static void bt_le_discovery_stop_cb(void); + +static bool is_bluetooth_uuid(const uint8_t *uuid) +{ + int i; + + for (i = 0; i < 16; i++) { + /* ignore minimal uuid (16) value */ + if (i == 12 || i == 13) + continue; + + if (uuid[i] != BLUETOOTH_UUID[i]) + return false; + } + + return true; +} + +static void android2uuid(const uint8_t *uuid, bt_uuid_t *dst) +{ + if (is_bluetooth_uuid(uuid)) { + /* copy 16 bit uuid value from full android 128bit uuid */ + dst->type = BT_UUID16; + dst->value.u16 = (uuid[13] << 8) + uuid[12]; + } else { + int i; + + dst->type = BT_UUID128; + for (i = 0; i < 16; i++) + dst->value.u128.data[i] = uuid[15 - i]; + } +} + +static void uuid2android(const bt_uuid_t *src, uint8_t *uuid) +{ + bt_uuid_t uu128; + uint8_t i; + + if (src->type != BT_UUID128) { + bt_uuid_to_uuid128(src, &uu128); + src = &uu128; + } + + for (i = 0; i < 16; i++) + uuid[15 - i] = src->value.u128.data[i]; +} + +static void hal_srvc_id_to_element_id(const struct hal_gatt_srvc_id *from, + struct element_id *to) +{ + to->instance = from->inst_id; + android2uuid(from->uuid, &to->uuid); +} + +static void element_id_to_hal_srvc_id(const struct element_id *from, + uint8_t primary, + struct hal_gatt_srvc_id *to) +{ + to->is_primary = primary; + to->inst_id = from->instance; + uuid2android(&from->uuid, to->uuid); +} + +static void hal_gatt_id_to_element_id(const struct hal_gatt_gatt_id *from, + struct element_id *to) +{ + to->instance = from->inst_id; + android2uuid(from->uuid, &to->uuid); +} + +static void element_id_to_hal_gatt_id(const struct element_id *from, + struct hal_gatt_gatt_id *to) +{ + to->inst_id = from->instance; + uuid2android(&from->uuid, to->uuid); +} + +static void destroy_characteristic(void *data) +{ + struct characteristic *chars = data; + + if (!chars) + return; + + queue_destroy(chars->descriptors, free); + free(chars); +} + +static void destroy_service(void *data) +{ + struct service *srvc = data; + + if (!srvc) + return; + + queue_destroy(srvc->chars, destroy_characteristic); + + /* + * Included services we keep on two queues. + * 1. On the same queue with primary services. + * 2. On the queue inside primary service. + * So we need to free service memory only once but we need to destroy + * two queues + */ + if (srvc->primary) + queue_destroy(srvc->included, NULL); + + free(srvc); +} + +static bool match_app_by_uuid(const void *data, const void *user_data) +{ + const uint8_t *exp_uuid = user_data; + const struct gatt_app *client = data; + + return !memcmp(exp_uuid, client->uuid, sizeof(client->uuid)); +} + +static bool match_app_by_id(const void *data, const void *user_data) +{ + int32_t exp_id = PTR_TO_INT(user_data); + const struct gatt_app *client = data; + + return client->id == exp_id; +} + +static struct gatt_app *find_app_by_id(int32_t id) +{ + return queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(id)); +} + +static bool match_by_value(const void *data, const void *user_data) +{ + return data == user_data; +} + +static bool match_device_by_bdaddr(const void *data, const void *user_data) +{ + const struct gatt_device *dev = data; + const bdaddr_t *addr = user_data; + + return !bacmp(&dev->bdaddr, addr); +} + +static bool match_device_by_state(const void *data, const void *user_data) +{ + const struct gatt_device *dev = data; + + if (dev->state != PTR_TO_UINT(user_data)) + return false; + + return true; +} + +static bool match_pending_device(const void *data, const void *user_data) +{ + const struct gatt_device *dev = data; + + if ((dev->state == DEVICE_CONNECT_INIT) || + (dev->state == DEVICE_CONNECT_READY)) + return true; + + return false; +} + +static bool match_connection_by_id(const void *data, const void *user_data) +{ + const struct app_connection *conn = data; + const int32_t id = PTR_TO_INT(user_data); + + return conn->id == id; +} + +static bool match_connection_by_device_and_app(const void *data, + const void *user_data) +{ + const struct app_connection *conn = data; + const struct app_connection *match = user_data; + + return conn->device == match->device && conn->app == match->app; +} + +static struct app_connection *find_connection_by_id(int32_t conn_id) +{ + struct app_connection *conn; + + conn = queue_find(app_connections, match_connection_by_id, + INT_TO_PTR(conn_id)); + if (conn && conn->device->state == DEVICE_CONNECTED) + return conn; + + return NULL; +} + +static bool match_connection_by_device(const void *data, const void *user_data) +{ + const struct app_connection *conn = data; + const struct gatt_device *dev = user_data; + + return conn->device == dev; +} + +static bool match_connection_by_app(const void *data, const void *user_data) +{ + const struct app_connection *conn = data; + const struct gatt_app *app = user_data; + + return conn->app == app; +} + +static struct gatt_device *find_device_by_addr(const bdaddr_t *addr) +{ + return queue_find(gatt_devices, match_device_by_bdaddr, addr); +} + +static struct gatt_device *find_pending_device(void) +{ + return queue_find(gatt_devices, match_pending_device, NULL); +} + +static struct gatt_device *find_device_by_state(uint32_t state) +{ + return queue_find(gatt_devices, match_device_by_state, + UINT_TO_PTR(state)); +} + +static bool match_srvc_by_element_id(const void *data, const void *user_data) +{ + const struct element_id *exp_id = user_data; + const struct service *service = data; + + if (service->id.instance == exp_id->instance) + return !bt_uuid_cmp(&service->id.uuid, &exp_id->uuid); + + return false; +} + +static bool match_srvc_by_higher_inst_id(const void *data, + const void *user_data) +{ + const struct service *s = data; + uint8_t inst_id = PTR_TO_INT(user_data); + + /* For now we match inst_id as it is unique */ + return inst_id < s->id.instance; +} + +static bool match_srvc_by_bt_uuid(const void *data, const void *user_data) +{ + const bt_uuid_t *exp_uuid = user_data; + const struct service *service = data; + + return !bt_uuid_cmp(exp_uuid, &service->id.uuid); +} + +static bool match_srvc_by_range(const void *data, const void *user_data) +{ + const struct service *srvc = data; + const struct att_range *range = user_data; + + return !memcmp(&srvc->prim.range, range, sizeof(srvc->prim.range)); +} + +static bool match_char_by_higher_inst_id(const void *data, + const void *user_data) +{ + const struct characteristic *ch = data; + uint8_t inst_id = PTR_TO_INT(user_data); + + /* For now we match inst_id as it is unique, we'll match uuids later */ + return inst_id < ch->id.instance; +} + +static bool match_descr_by_element_id(const void *data, const void *user_data) +{ + const struct element_id *exp_id = user_data; + const struct descriptor *descr = data; + + if (exp_id->instance == descr->id.instance) + return !bt_uuid_cmp(&descr->id.uuid, &exp_id->uuid); + + return false; +} + +static bool match_descr_by_higher_inst_id(const void *data, + const void *user_data) +{ + const struct descriptor *descr = data; + uint8_t instance = PTR_TO_INT(user_data); + + /* For now we match instance as it is unique */ + return instance < descr->id.instance; +} + +static bool match_notification(const void *a, const void *b) +{ + const struct notification_data *a1 = a; + const struct notification_data *b1 = b; + + if (a1->conn != b1->conn) + return false; + + if (memcmp(&a1->ch, &b1->ch, sizeof(a1->ch))) + return false; + + if (memcmp(&a1->service, &b1->service, sizeof(a1->service))) + return false; + + return true; +} + +static bool match_char_by_element_id(const void *data, const void *user_data) +{ + const struct element_id *exp_id = user_data; + const struct characteristic *chars = data; + + if (exp_id->instance == chars->id.instance) + return !bt_uuid_cmp(&chars->id.uuid, &exp_id->uuid); + + return false; +} + +static void destroy_notification(void *data) +{ + struct notification_data *notification = data; + struct gatt_app *app; + + if (--notification->ref) + return; + + app = notification->conn->app; + queue_remove_if(app->notifications, match_notification, notification); + free(notification); +} + +static void unregister_notification(void *data) +{ + struct notification_data *notification = data; + struct gatt_device *dev = notification->conn->device; + + /* + * No device means it was already disconnected and client cleanup was + * triggered afterwards, but once client unregisters, device stays if + * used by others. Then just unregister single handle. + */ + if (!queue_find(gatt_devices, match_by_value, dev)) + return; + + if (notification->notif_id && dev) + g_attrib_unregister(dev->attrib, notification->notif_id); + + if (notification->ind_id && dev) + g_attrib_unregister(dev->attrib, notification->ind_id); +} + +static void device_set_state(struct gatt_device *dev, uint32_t state) +{ + char bda[18]; + + ba2str(&dev->bdaddr, bda); + DBG("gatt: Device %s state changed %s -> %s", bda, + device_state_str[dev->state], device_state_str[state]); + + dev->state = state; +} + +static bool auto_connect_le(struct gatt_device *dev) +{ + /* For LE devices use auto connect feature if possible */ + if (bt_kernel_conn_control()) + return bt_auto_connect_add(&dev->bdaddr); + + /* Trigger discovery if not already started */ + if (!scanning) { + if (!bt_le_discovery_start()) { + error("gatt: Could not start scan"); + return false; + } + } + + return true; +} + +static void connection_cleanup(struct gatt_device *device) +{ + if (device->watch_id) { + g_source_remove(device->watch_id); + device->watch_id = 0; + } + + if (device->att_io) { + g_io_channel_shutdown(device->att_io, FALSE, NULL); + g_io_channel_unref(device->att_io); + device->att_io = NULL; + } + + if (device->attrib) { + GAttrib *attrib = device->attrib; + + if (device->server_id > 0) + g_attrib_unregister(device->attrib, device->server_id); + + device->attrib = NULL; + g_attrib_cancel_all(attrib); + g_attrib_unref(attrib); + } + + /* + * If device was in connection_pending or connectable state we + * search device list if we should stop the scan. + */ + if (!scanning && (device->state == DEVICE_CONNECT_INIT || + device->state == DEVICE_CONNECT_READY)) { + if (!find_pending_device()) + bt_le_discovery_stop(NULL); + } + + /* If device is not bonded service cache should be refreshed */ + if (!bt_device_is_bonded(&device->bdaddr)) + queue_remove_all(device->services, NULL, NULL, destroy_service); + + device_set_state(device, DEVICE_DISCONNECTED); + + if (!queue_isempty(device->autoconnect_apps)) { + auto_connect_le(device); + device_set_state(device, DEVICE_CONNECT_INIT); + } else { + bt_auto_connect_remove(&device->bdaddr); + } +} + +static void destroy_gatt_app(void *data) +{ + struct gatt_app *app = data; + + /* + * First we want to get all notifications and unregister them. + * We don't pass unregister_notification to queue_destroy, + * because destroy notification performs operations on queue + * too. So remove all elements and then destroy queue. + */ + + if (app->type == GATT_CLIENT) + while (queue_peek_head(app->notifications)) { + struct notification_data *notification; + + notification = queue_pop_head(app->notifications); + unregister_notification(notification); + } + + queue_destroy(app->notifications, free); + + free(app); +} + +enum pend_req_state { + REQUEST_INIT, + REQUEST_PENDING, + REQUEST_DONE, +}; + +struct pending_request { + uint16_t handle; + int length; + uint8_t *value; + uint16_t offset; + + uint8_t *filter_value; + uint16_t filter_vlen; + + enum pend_req_state state; + uint8_t error; +}; + +static void destroy_pending_request(void *data) +{ + struct pending_request *entry = data; + + free(entry->value); + free(entry->filter_value); + free(entry); +} + +static void destroy_device(void *data) +{ + struct gatt_device *dev = data; + + if (!dev) + return; + + queue_destroy(dev->services, destroy_service); + queue_destroy(dev->pending_requests, destroy_pending_request); + queue_destroy(dev->autoconnect_apps, NULL); + + bt_auto_connect_remove(&dev->bdaddr); + + free(dev); +} + +static struct gatt_device *device_ref(struct gatt_device *device) +{ + if (!device) + return NULL; + + device->ref++; + + return device; +} + +static void device_unref(struct gatt_device *device) +{ + if (!device) + return; + + if (--device->ref) + return; + + destroy_device(device); +} + +static struct gatt_device *create_device(const bdaddr_t *addr) +{ + struct gatt_device *dev; + + dev = new0(struct gatt_device, 1); + if (!dev) + return NULL; + + bacpy(&dev->bdaddr, addr); + + dev->services = queue_new(); + if (!dev->services) { + error("gatt: Failed to allocate memory for client"); + destroy_device(dev); + return NULL; + } + + dev->autoconnect_apps = queue_new(); + if (!dev->autoconnect_apps) { + error("gatt: Failed to allocate memory for client"); + destroy_device(dev); + return NULL; + } + + dev->pending_requests = queue_new(); + if (!dev->pending_requests) { + error("gatt: Failed to allocate memory for client"); + destroy_device(dev); + return NULL; + } + + if (!queue_push_head(gatt_devices, dev)) { + error("gatt: Cannot push device to queue"); + destroy_device(dev); + return NULL; + } + + return device_ref(dev); +} + +static void send_client_connection_notify(struct app_connection *connection, + int32_t status) +{ + struct hal_ev_gatt_client_connect ev; + + if (connection->app->func) { + connection->app->func(&connection->device->bdaddr, + status == GATT_SUCCESS ? 0 : -ENOTCONN, + connection->device->attrib); + return; + } + + ev.client_if = connection->app->id; + ev.conn_id = connection->id; + ev.status = status; + + bdaddr2android(&connection->device->bdaddr, &ev.bda); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT, + sizeof(ev), &ev); +} + +static void send_server_connection_notify(struct app_connection *connection, + bool connected) +{ + struct hal_ev_gatt_server_connection ev; + + if (connection->app->func) { + connection->app->func(&connection->device->bdaddr, + connected ? 0 : -ENOTCONN, + connection->device->attrib); + return; + } + + ev.server_if = connection->app->id; + ev.conn_id = connection->id; + ev.connected = connected; + + bdaddr2android(&connection->device->bdaddr, &ev.bdaddr); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev); +} + +static void send_client_disconnection_notify(struct app_connection *connection, + int32_t status) +{ + struct hal_ev_gatt_client_disconnect ev; + + if (connection->app->func) { + connection->app->func(&connection->device->bdaddr, -ENOTCONN, + connection->device->attrib); + return; + } + + ev.client_if = connection->app->id; + ev.conn_id = connection->id; + ev.status = status; + + bdaddr2android(&connection->device->bdaddr, &ev.bda); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev); + +} + +static void send_app_disconnect_notify(struct app_connection *connection, + int32_t status) +{ + if (!connection->app) + return; + + if (connection->app->type == GATT_CLIENT) + send_client_disconnection_notify(connection, status); + else + send_server_connection_notify(connection, !!status); +} + +static void send_app_connect_notify(struct app_connection *connection, + int32_t status) +{ + if (!connection->app) + return; + + if (connection->app->type == GATT_CLIENT) + send_client_connection_notify(connection, status); + else if (connection->app->type == GATT_SERVER) + send_server_connection_notify(connection, !status); +} + +static void disconnect_notify_by_device(void *data, void *user_data) +{ + struct app_connection *conn = data; + struct gatt_device *dev = user_data; + + if (dev != conn->device || !conn->app) + return; + + if (dev->state == DEVICE_CONNECTED) + send_app_disconnect_notify(conn, GATT_SUCCESS); + else if (dev->state == DEVICE_CONNECT_INIT || + dev->state == DEVICE_CONNECT_READY) + send_app_connect_notify(conn, GATT_FAILURE); +} + +static void destroy_connection(void *data) +{ + struct app_connection *conn = data; + + if (conn->timeout_id > 0) + g_source_remove(conn->timeout_id); + + if (!queue_find(gatt_devices, match_by_value, conn->device)) + goto cleanup; + + conn->device->conn_cnt--; + if (conn->device->conn_cnt == 0) + connection_cleanup(conn->device); + +cleanup: + queue_destroy(conn->transactions, free); + device_unref(conn->device); + free(conn); +} + +static void device_disconnect_clients(struct gatt_device *dev) +{ + /* Notify disconnection to all clients */ + queue_foreach(app_connections, disconnect_notify_by_device, dev); + + /* Remove all clients by given device's */ + queue_remove_all(app_connections, match_connection_by_device, dev, + destroy_connection); +} + +static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct gatt_device *dev = user_data; + int sock, err = 0; + socklen_t len; + + sock = g_io_channel_unix_get_fd(io); + len = sizeof(err); + if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len)) + DBG("%s (%d)", strerror(err), err); + + device_disconnect_clients(dev); + + return FALSE; +} + +static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data); + +static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + struct gatt_device *device = user_data; + GIOChannel *io; + GError *gerr = NULL; + uint16_t rmtu, mtu, imtu; + + if (status) { + error("gatt: MTU exchange: %s", att_ecode2str(status)); + goto failed; + } + + if (!dec_mtu_resp(pdu, plen, &rmtu)) { + error("gatt: MTU exchange: protocol error"); + goto failed; + } + + if (rmtu < ATT_DEFAULT_LE_MTU) { + error("gatt: MTU exchange: mtu error"); + goto failed; + } + + io = g_attrib_get_channel(device->attrib); + + bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID); + if (gerr) { + error("gatt: Could not get imtu: %s", gerr->message); + g_error_free(gerr); + + return; + } + + mtu = MIN(rmtu, imtu); + if (mtu != imtu && !g_attrib_set_mtu(device->attrib, mtu)) { + error("gatt: MTU exchange failed"); + goto failed; + } + + DBG("MTU exchange succeeded: rmtu:%d, old mtu:%d, new mtu:%d", rmtu, + imtu, mtu); + +failed: + device_unref(device); +} + +static void send_exchange_mtu_request(struct gatt_device *device) +{ + GIOChannel *io; + GError *gerr = NULL; + uint16_t imtu; + + io = g_attrib_get_channel(device->attrib); + + bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID); + if (gerr) { + error("gatt: Could not get imtu: %s", gerr->message); + g_error_free(gerr); + + return; + } + + if (!gatt_exchange_mtu(device->attrib, imtu, exchange_mtu_cb, + device_ref(device))) + device_unref(device); +} + +static void notify_att_range_change(struct gatt_device *dev, + struct att_range *range) +{ + uint16_t length = 0; + uint16_t ccc; + uint8_t *pdu; + size_t mtu; + + ccc = bt_get_gatt_ccc(&dev->bdaddr); + if (!ccc) + return; + + pdu = g_attrib_get_buffer(dev->attrib, &mtu); + + switch (ccc) { + case 0x0001: + length = enc_notification(service_changed_handle, + (uint8_t *) range, + sizeof(*range), pdu, mtu); + break; + case 0x0002: + length = enc_indication(service_changed_handle, + (uint8_t *) range, sizeof(*range), pdu, + mtu); + break; + default: + /* 0xfff4 reserved for future use */ + break; + } + + if (length) + g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL); +} + +static struct app_connection *create_connection(struct gatt_device *device, + struct gatt_app *app) +{ + struct app_connection *new_conn; + static int32_t last_conn_id = 1; + + /* Check if already connected */ + new_conn = new0(struct app_connection, 1); + if (!new_conn) + return NULL; + + /* Make connection id unique to connection record (app, device) pair */ + new_conn->app = app; + new_conn->id = last_conn_id++; + + new_conn->transactions = queue_new(); + if (!new_conn->transactions) { + free(new_conn); + return NULL; + } + + if (!queue_push_head(app_connections, new_conn)) { + error("gatt: Cannot push client on the client queue!?"); + queue_destroy(new_conn->transactions, free); + free(new_conn); + return NULL; + } + + new_conn->device = device_ref(device); + new_conn->device->conn_cnt++; + + return new_conn; +} + +static struct service *create_service(uint8_t id, bool primary, char *uuid, + void *data) +{ + struct service *s; + + s = new0(struct service, 1); + if (!s) { + error("gatt: Cannot allocate memory for gatt_primary"); + return NULL; + } + + s->chars = queue_new(); + if (!s->chars) { + error("gatt: Cannot allocate memory for char cache"); + free(s); + return NULL; + } + + if (bt_string_to_uuid(&s->id.uuid, uuid) < 0) { + error("gatt: Cannot convert string to uuid"); + queue_destroy(s->chars, NULL); + free(s); + return NULL; + } + + s->id.instance = id; + + /* Put primary service to our local list */ + s->primary = primary; + if (s->primary) { + memcpy(&s->prim, data, sizeof(s->prim)); + } else { + memcpy(&s->incl, data, sizeof(s->incl)); + return s; + } + + /* For primary service allocate queue for included services */ + s->included = queue_new(); + if (!s->included) { + queue_destroy(s->chars, NULL); + free(s); + return NULL; + } + + return s; +} + +static void send_client_primary_notify(void *data, void *user_data) +{ + struct hal_ev_gatt_client_search_result ev; + struct service *p = data; + int32_t conn_id = PTR_TO_INT(user_data); + + /* In service queue we will have also included services */ + if (!p->primary) + return; + + ev.conn_id = conn_id; + element_id_to_hal_srvc_id(&p->id, 1, &ev.srvc_id); + + uuid2android(&p->id.uuid, ev.srvc_id.uuid); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_SEARCH_RESULT, sizeof(ev), &ev); +} + +static void send_client_search_complete_notify(int32_t status, int32_t conn_id) +{ + struct hal_ev_gatt_client_search_complete ev; + + ev.status = status; + ev.conn_id = conn_id; + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_SEARCH_COMPLETE, sizeof(ev), &ev); +} + +struct discover_srvc_data { + bt_uuid_t uuid; + struct app_connection *conn; +}; + +static void discover_srvc_by_uuid_cb(uint8_t status, GSList *ranges, + void *user_data) +{ + struct discover_srvc_data *cb_data = user_data; + struct gatt_primary prim; + struct service *s; + int32_t gatt_status; + struct gatt_device *dev = cb_data->conn->device; + uint8_t instance_id = queue_length(dev->services); + + DBG("Status %d", status); + + if (status) { + error("gatt: Discover pri srvc filtered by uuid failed: %s", + att_ecode2str(status)); + gatt_status = GATT_FAILURE; + goto reply; + } + + if (!ranges) { + info("gatt: No primary services searched by uuid found"); + gatt_status = GATT_SUCCESS; + goto reply; + } + + bt_uuid_to_string(&cb_data->uuid, prim.uuid, sizeof(prim.uuid)); + + for (; ranges; ranges = ranges->next) { + memcpy(&prim.range, ranges->data, sizeof(prim.range)); + + s = create_service(instance_id++, true, prim.uuid, &prim); + if (!s) { + gatt_status = GATT_FAILURE; + goto reply; + } + + if (!queue_push_tail(dev->services, s)) { + error("gatt: Cannot push primary service to the list"); + destroy_service(s); + gatt_status = GATT_FAILURE; + goto reply; + } + + send_client_primary_notify(s, INT_TO_PTR(cb_data->conn->id)); + + DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s", + prim.range.start, prim.range.end, prim.uuid); + } + + /* Partial search service scanning was performed */ + dev->partial_srvc_search = true; + gatt_status = GATT_SUCCESS; + +reply: + send_client_search_complete_notify(gatt_status, cb_data->conn->id); + free(cb_data); +} + +static void discover_srvc_all_cb(uint8_t status, GSList *services, + void *user_data) +{ + struct discover_srvc_data *cb_data = user_data; + struct gatt_device *dev = cb_data->conn->device; + int32_t gatt_status; + GSList *l; + /* + * There might be multiply services with same uuid. Therefore make sure + * each primary service one has unique instance_id + */ + uint8_t instance_id = queue_length(dev->services); + + DBG("Status %d", status); + + if (status) { + error("gatt: Discover all primary services failed: %s", + att_ecode2str(status)); + gatt_status = GATT_FAILURE; + goto reply; + } + + if (!services) { + info("gatt: No primary services found"); + gatt_status = GATT_SUCCESS; + goto reply; + } + + for (l = services; l; l = l->next) { + struct gatt_primary *prim = l->data; + struct service *p; + + if (queue_find(dev->services, match_srvc_by_range, + &prim->range)) + continue; + + p = create_service(instance_id++, true, prim->uuid, prim); + if (!p) + continue; + + if (!queue_push_tail(dev->services, p)) { + error("gatt: Cannot push primary service to the list"); + free(p); + continue; + } + + DBG("attr handle = 0x%04x, end grp handle = 0x%04x uuid: %s", + prim->range.start, prim->range.end, prim->uuid); + } + + /* + * Send all found services notifications - first cache, + * then send notifies + */ + queue_foreach(dev->services, send_client_primary_notify, + INT_TO_PTR(cb_data->conn->id)); + + /* Full search service scanning was performed */ + dev->partial_srvc_search = false; + gatt_status = GATT_SUCCESS; + +reply: + send_client_search_complete_notify(gatt_status, cb_data->conn->id); + free(cb_data); +} +static gboolean connection_timeout(void *user_data) +{ + struct app_connection *conn = user_data; + + conn->timeout_id = 0; + + queue_remove(app_connections, conn); + destroy_connection(conn); + + return FALSE; +} + +static void discover_primary_cb(uint8_t status, GSList *services, + void *user_data) +{ + struct discover_srvc_data *cb_data = user_data; + struct app_connection *conn = cb_data->conn; + struct gatt_device *dev = conn->device; + GSList *l, *uuids = NULL; + + DBG("Status %d", status); + + if (status) { + error("gatt: Discover all primary services failed: %s", + att_ecode2str(status)); + free(cb_data); + + return; + } + + if (!services) { + info("gatt: No primary services found"); + free(cb_data); + + return; + } + + for (l = services; l; l = l->next) { + struct gatt_primary *prim = l->data; + uint8_t *new_uuid; + bt_uuid_t uuid; + + DBG("uuid: %s", prim->uuid); + + if (bt_string_to_uuid(&uuid, prim->uuid) < 0) { + error("gatt: Cannot convert string to uuid"); + continue; + } + + new_uuid = g_memdup(&uuid.value.u128, sizeof(uuid.value.u128)); + + uuids = g_slist_prepend(uuids, new_uuid); + } + + bt_device_set_uuids(&dev->bdaddr, uuids); + + free(cb_data); + + conn->timeout_id = g_timeout_add_seconds(GATT_CONN_TIMEOUT, + connection_timeout, conn); +} + +static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid) +{ + struct discover_srvc_data *cb_data = + new0(struct discover_srvc_data, 1); + + if (!cb_data) { + error("gatt: Cannot allocate cb data"); + return 0; + } + + cb_data->conn = conn; + + if (uuid) { + memcpy(&cb_data->uuid, uuid, sizeof(cb_data->uuid)); + return gatt_discover_primary(conn->device->attrib, uuid, + discover_srvc_by_uuid_cb, cb_data); + } + + + if (conn->app) + return gatt_discover_primary(conn->device->attrib, NULL, + discover_srvc_all_cb, cb_data); + + return gatt_discover_primary(conn->device->attrib, NULL, + discover_primary_cb, cb_data); +} + +struct connect_data { + struct gatt_device *dev; + int32_t status; +}; + +static void send_app_connect_notifications(void *data, void *user_data) +{ + struct app_connection *conn = data; + struct connect_data *con_data = user_data; + + if (conn->device == con_data->dev) + send_app_connect_notify(conn, con_data->status); +} + +static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id) +{ + struct app_connection conn_match; + struct gatt_device *dev = NULL; + struct gatt_app *app; + + /* Check if app is registered */ + app = find_app_by_id(app_id); + if (!app) { + error("gatt: Client id %d not found", app_id); + return NULL; + } + + /* Check if device is known */ + dev = find_device_by_addr(addr); + if (!dev) { + error("gatt: Client id %d not found", app_id); + return NULL; + } + + conn_match.device = dev; + conn_match.app = app; + + return queue_find(app_connections, match_connection_by_device_and_app, + &conn_match); +} + +static void create_app_connection(void *data, void *user_data) +{ + struct gatt_device *dev = user_data; + struct gatt_app *app; + + app = find_app_by_id(PTR_TO_INT(data)); + if (!app) + return; + + DBG("Autoconnect application id=%d", app->id); + + if (!find_conn(&dev->bdaddr, PTR_TO_INT(data))) + create_connection(dev, app); +} + +static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) +{ + struct gatt_device *dev = user_data; + struct connect_data data; + struct att_range range; + uint32_t status; + GAttrib *attrib; + + if (dev->state != DEVICE_CONNECT_READY) { + error("gatt: Device not in a connecting state!?"); + g_io_channel_shutdown(io, TRUE, NULL); + return; + } + + if (dev->att_io) { + g_io_channel_unref(dev->att_io); + dev->att_io = NULL; + } + + if (gerr) { + error("gatt: connection failed %s", gerr->message); + device_set_state(dev, DEVICE_DISCONNECTED); + status = GATT_FAILURE; + goto reply; + } + + attrib = g_attrib_new(io); + if (!attrib) { + error("gatt: unable to create new GAttrib instance"); + device_set_state(dev, DEVICE_DISCONNECTED); + status = GATT_FAILURE; + goto reply; + } + + dev->attrib = attrib; + dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + disconnected_cb, dev); + + dev->server_id = g_attrib_register(attrib, GATTRIB_ALL_REQS, + GATTRIB_ALL_HANDLES, + att_handler, dev, NULL); + if (dev->server_id == 0) + error("gatt: Could not attach to server"); + + device_set_state(dev, DEVICE_CONNECTED); + + /* Send exchange mtu request as we assume being client and server */ + /* TODO: Dont exchange mtu if no client apps */ + send_exchange_mtu_request(dev); + + /* + * Service Changed Characteristic and CCC Descriptor handles + * should not change if there are bonded devices. We have them + * constant all the time, thus they should be excluded from + * range indicating changes. + */ + range.start = service_changed_handle + 2; + range.end = 0xffff; + + /* + * If there is ccc stored for that device we were acting as server for + * it, and as we dont have last connect and last services (de)activation + * timestamps we should always assume something has changed. + */ + notify_att_range_change(dev, &range); + + status = GATT_SUCCESS; + +reply: + /* + * Make sure there are app_connections for all apps interested in auto + * connect to that device + */ + queue_foreach(dev->autoconnect_apps, create_app_connection, dev); + + if (!dev->conn_cnt) { + struct app_connection *conn; + + if (!dev->attrib) + return; + + conn = create_connection(dev, NULL); + if (!conn) + return; + + search_dev_for_srvc(conn, NULL); + } + + data.dev = dev; + data.status = status; + queue_foreach(app_connections, send_app_connect_notifications, &data); + device_unref(dev); + + /* Check if we should restart scan */ + if (scanning) + bt_le_discovery_start(); + + /* FIXME: What to do if discovery won't start here. */ +} + +static int connect_le(struct gatt_device *dev) +{ + GIOChannel *io; + GError *gerr = NULL; + char addr[18]; + const bdaddr_t *bdaddr; + uint8_t bdaddr_type; + + ba2str(&dev->bdaddr, addr); + + /* There is one connection attempt going on */ + if (dev->att_io) { + info("gatt: connection to dev %s is ongoing", addr); + return -EALREADY; + } + + DBG("Connection attempt to: %s", addr); + + /* + * If address type is random it might be that IRK was received and + * random is just for faking Android Framework. ID address should be + * used for connection if present. + */ + if (dev->bdaddr_type == BDADDR_LE_RANDOM) { + bdaddr = bt_get_id_addr(&dev->bdaddr, &bdaddr_type); + if (!bdaddr) + return -EINVAL; + } else { + bdaddr = &dev->bdaddr; + bdaddr_type = dev->bdaddr_type; + } + + /* + * This connection will help us catch any PDUs that comes before + * pairing finishes + */ + io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, + &adapter_addr, + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, + BT_IO_OPT_DEST_BDADDR, bdaddr, + BT_IO_OPT_DEST_TYPE, bdaddr_type, + BT_IO_OPT_CID, ATT_CID, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!io) { + error("gatt: Failed bt_io_connect(%s): %s", addr, + gerr->message); + g_error_free(gerr); + return -EIO; + } + + /* Keep this, so we can cancel the connection */ + dev->att_io = io; + + return 0; +} + +static int connect_next_dev(void) +{ + struct gatt_device *dev; + + DBG(""); + + dev = find_device_by_state(DEVICE_CONNECT_READY); + if (!dev) + return -ENODEV; + + return connect_le(dev); +} + +static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type, + int rssi, uint16_t eir_len, + const void *eir, + bool discoverable, bool bonded) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_scan_result *ev = (void *) buf; + struct gatt_device *dev; + char bda[18]; + + if (!scanning || (!discoverable && !bonded)) + goto connect; + + ba2str(addr, bda); + DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir); + + bdaddr2android(addr, ev->bda); + ev->rssi = rssi; + ev->len = eir_len; + + memcpy(ev->adv_data, eir, ev->len); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_SCAN_RESULT, + sizeof(*ev) + ev->len, ev); + +connect: + /* We use auto connect feature from kernel if possible */ + if (bt_kernel_conn_control()) + return; + + dev = find_device_by_addr(addr); + if (!dev) { + if (!bonded) + return; + + dev = create_device(addr); + } + + if (!dev || dev->state != DEVICE_CONNECT_INIT) + return; + + device_set_state(dev, DEVICE_CONNECT_READY); + dev->bdaddr_type = addr_type; + + /* + * We are ok to perform connect now. Stop discovery + * and once it is stopped continue with creating ACL + */ + bt_le_discovery_stop(bt_le_discovery_stop_cb); +} + +static struct gatt_app *register_app(const uint8_t *uuid, gatt_type_t type) +{ + static int32_t application_id = 1; + struct gatt_app *app; + + if (queue_find(gatt_apps, match_app_by_uuid, uuid)) { + error("gatt: app uuid is already on list"); + return NULL; + } + + app = new0(struct gatt_app, 1); + if (!app) { + error("gatt: Cannot allocate memory for registering app"); + return NULL; + } + + app->type = type; + + if (app->type == GATT_CLIENT) { + app->notifications = queue_new(); + if (!app->notifications) { + error("gatt: couldn't allocate notifications queue"); + destroy_gatt_app(app); + return NULL; + } + } + + memcpy(app->uuid, uuid, sizeof(app->uuid)); + + app->id = application_id++; + + if (!queue_push_head(gatt_apps, app)) { + error("gatt: Cannot push app on the list"); + destroy_gatt_app(app); + return NULL; + } + + if ((app->type == GATT_SERVER) && + !queue_push_tail(listen_apps, INT_TO_PTR(app->id))) { + error("gatt: Cannot push server on the list"); + destroy_gatt_app(app); + return NULL; + } + + return app; +} + +static void handle_client_register(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_register *cmd = buf; + struct hal_ev_gatt_client_register_client ev; + struct gatt_app *app; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + app = register_app(cmd->uuid, GATT_CLIENT); + + if (app) { + ev.client_if = app->id; + ev.status = GATT_SUCCESS; + } else + ev.status = GATT_FAILURE; + + /* We should send notification with given in cmd UUID */ + memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_REGISTER_CLIENT, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER, + HAL_STATUS_SUCCESS); +} + +static void handle_client_scan(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_scan *cmd = buf; + uint8_t status; + void *registered; + + DBG("new state %d", cmd->start); + + registered = find_app_by_id(cmd->client_if); + if (!registered) { + error("gatt: Client not registered"); + status = HAL_STATUS_FAILED; + goto reply; + } + + /* Turn off scan */ + if (!cmd->start) { + DBG("Stopping LE SCAN"); + + if (scanning) { + bt_le_discovery_stop(NULL); + scanning = false; + } + + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* Reply success if we already do scan */ + if (scanning) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* Turn on scan */ + if (!bt_le_discovery_start()) { + error("gatt: LE scan switch failed"); + status = HAL_STATUS_FAILED; + goto reply; + } + scanning = true; + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN, + status); +} + +static void bt_le_discovery_stop_cb(void) +{ + DBG(""); + + /* Check now if there is any device ready to connect */ + if (connect_next_dev() < 0) + bt_le_discovery_start(); +} + +static void trigger_disconnection(struct app_connection *connection) +{ + /* Notify client */ + if (queue_remove(app_connections, connection)) + send_app_disconnect_notify(connection, GATT_SUCCESS); + + destroy_connection(connection); +} + +static void app_disconnect_devices(struct gatt_app *client) +{ + struct app_connection *conn; + + /* find every connection for client record and trigger disconnect */ + conn = queue_remove_if(app_connections, match_connection_by_app, + client); + while (conn) { + trigger_disconnection(conn); + + conn = queue_remove_if(app_connections, + match_connection_by_app, client); + } +} + +static int connect_bredr(struct gatt_device *dev) +{ + BtIOSecLevel sec_level; + GIOChannel *io; + GError *gerr = NULL; + char addr[18]; + + ba2str(&dev->bdaddr, addr); + + /* There is one connection attempt going on */ + if (dev->att_io) { + info("gatt: connection to dev %s is ongoing", addr); + return -EALREADY; + } + + DBG("Connection attempt to: %s", addr); + + sec_level = bt_device_is_bonded(&dev->bdaddr) ? BT_IO_SEC_MEDIUM : + BT_IO_SEC_LOW; + + io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR, + BT_IO_OPT_DEST_BDADDR, &dev->bdaddr, + BT_IO_OPT_DEST_TYPE, BDADDR_BREDR, + BT_IO_OPT_PSM, ATT_PSM, + BT_IO_OPT_SEC_LEVEL, sec_level, + BT_IO_OPT_INVALID); + if (!io) { + error("gatt: Failed bt_io_connect(%s): %s", addr, + gerr->message); + g_error_free(gerr); + return -EIO; + } + + device_set_state(dev, DEVICE_CONNECT_READY); + + /* Keep this, so we can cancel the connection */ + dev->att_io = io; + + return 0; +} + +static bool trigger_connection(struct app_connection *connection) +{ + bool ret; + + switch (connection->device->state) { + case DEVICE_DISCONNECTED: + /* + * If device was last seen over BR/EDR connect over it. + * Note: Connection state is handled in connect_bredr() func + */ + if (bt_device_last_seen_bearer(&connection->device->bdaddr) == + BDADDR_BREDR) + return connect_bredr(connection->device) == 0; + + /* For LE use auto connect feature */ + ret = auto_connect_le(connection->device); + if (ret) + device_set_state(connection->device, + DEVICE_CONNECT_INIT); + break; + case DEVICE_CONNECTED: + send_app_connect_notify(connection, GATT_SUCCESS); + ret = true; + break; + case DEVICE_CONNECT_READY: + case DEVICE_CONNECT_INIT: + default: + /* In those cases connection is already triggered. */ + ret = true; + break; + } + + return ret; +} + +static void remove_autoconnect_device(struct gatt_device *dev) +{ + bt_auto_connect_remove(&dev->bdaddr); + + if (dev->state == DEVICE_CONNECT_INIT) + device_set_state(dev, DEVICE_DISCONNECTED); + + device_unref(dev); +} + +static void clear_autoconnect_devices(void *data, void *user_data) +{ + struct gatt_device *dev = data; + + if (queue_remove(dev->autoconnect_apps, user_data)) + if (queue_isempty(dev->autoconnect_apps)) + remove_autoconnect_device(dev); + + +} + +static uint8_t unregister_app(int client_if) +{ + struct gatt_app *cl; + + /* + * Make sure that there is no devices in auto connect list for this + * application + */ + queue_foreach(gatt_devices, clear_autoconnect_devices, INT_TO_PTR(client_if)); + + cl = queue_remove_if(gatt_apps, match_app_by_id, INT_TO_PTR(client_if)); + if (!cl) { + error("gatt: client_if=%d not found", client_if); + + return HAL_STATUS_FAILED; + } + + /* Destroy app connections with proper notifications for this app. */ + app_disconnect_devices(cl); + destroy_gatt_app(cl); + + return HAL_STATUS_SUCCESS; +} + +static void send_client_listen_notify(int32_t id, int32_t status) +{ + struct hal_ev_gatt_client_listen ev; + + /* Server if because of typo in android headers */ + ev.server_if = id; + ev.status = status; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_LISTEN, + sizeof(ev), &ev); +} + +struct listen_data { + int32_t client_id; + bool start; +}; + +static struct listen_data *create_listen_data(int32_t client_id, bool start) +{ + struct listen_data *d; + + d = new0(struct listen_data, 1); + if (!d) + return NULL; + + d->client_id = client_id; + d->start = start; + + return d; +} + +static void set_advertising_cb(uint8_t status, void *user_data) +{ + struct listen_data *l = user_data; + + send_client_listen_notify(l->client_id, status); + + /* In case of success update advertising state*/ + if (!status) + advertising_cnt = l->start ? 1 : 0; + + /* + * Let's remove client from the list in two cases + * 1. Start failed + * 2. Stop succeed + */ + if ((l->start && status) || (!l->start && !status)) + queue_remove(listen_apps, INT_TO_PTR(l->client_id)); + + free(l); +} + +static void handle_client_unregister(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_unregister *cmd = buf; + uint8_t status; + void *listening_client; + struct listen_data *data; + + DBG(""); + + listening_client = queue_find(listen_apps, match_by_value, + INT_TO_PTR(cmd->client_if)); + + if (listening_client) { + advertising_cnt--; + queue_remove(listen_apps, INT_TO_PTR(cmd->client_if)); + } else { + status = unregister_app(cmd->client_if); + goto reply; + } + + if (!advertising_cnt) { + data = create_listen_data(cmd->client_if, false); + if (!data) { + error("gatt: Could not allocate listen data"); + status = HAL_STATUS_NOMEM; + goto reply; + } + + if (!bt_le_set_advertising(data->start, set_advertising_cb, + data)) { + error("gatt: Could not set advertising"); + status = HAL_STATUS_FAILED; + free(data); + goto reply; + } + } + + status = unregister_app(cmd->client_if); + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_UNREGISTER, status); +} + +static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr) +{ + struct app_connection conn_match; + struct app_connection *conn; + struct gatt_device *device; + struct gatt_app *app; + + DBG(""); + + app = find_app_by_id(app_id); + if (!app) + return HAL_STATUS_FAILED; + + device = find_device_by_addr(addr); + if (!device) { + device = create_device(addr); + if (!device) + return HAL_STATUS_FAILED; + } + + conn_match.device = device; + conn_match.app = app; + + conn = queue_find(app_connections, match_connection_by_device_and_app, + &conn_match); + if (!conn) { + conn = create_connection(device, app); + if (!conn) + return HAL_STATUS_NOMEM; + } + + if (!trigger_connection(conn)) + return HAL_STATUS_FAILED; + + return HAL_STATUS_SUCCESS; +} + +static void handle_client_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_connect *cmd = buf; + uint8_t status; + bdaddr_t addr; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &addr); + + /* TODO handle is_direct flag */ + + status = handle_connect(cmd->client_if, &addr); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT, + status); +} + +static void handle_client_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_disconnect *cmd = buf; + struct app_connection *conn; + uint8_t status; + + DBG(""); + + /* TODO: should we care to match also bdaddr when conn_id is unique? */ + conn = find_connection_by_id(cmd->conn_id); + if (conn) + trigger_disconnection(conn); + + status = HAL_STATUS_SUCCESS; + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_DISCONNECT, status); +} + +static void handle_client_listen(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_listen *cmd = buf; + uint8_t status; + struct listen_data *data; + bool req_sent = false; + void *listening_client; + + DBG(""); + + if (!find_app_by_id(cmd->client_if)) { + error("gatt: Client not registered"); + status = HAL_STATUS_FAILED; + goto reply; + } + + listening_client = queue_find(listen_apps, match_by_value, + INT_TO_PTR(cmd->client_if)); + /* Start listening */ + if (cmd->start) { + if (listening_client) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + if (!queue_push_tail(listen_apps, + INT_TO_PTR(cmd->client_if))) { + error("gatt: Could not put client on listen queue"); + status = HAL_STATUS_FAILED; + goto reply; + } + + /* If listen is already on just return success*/ + if (advertising_cnt > 0) { + advertising_cnt++; + status = HAL_STATUS_SUCCESS; + goto reply; + } + } else { + /* Stop listening. Check if client was listening */ + if (!listening_client) { + error("gatt: This client %d does not listen", + cmd->client_if); + status = HAL_STATUS_FAILED; + goto reply; + } + + /* + * In case there is more listening clients don't stop + * advertising + */ + if (advertising_cnt > 1) { + advertising_cnt--; + queue_remove(listen_apps, + INT_TO_PTR(cmd->client_if)); + status = HAL_STATUS_SUCCESS; + goto reply; + } + } + + data = create_listen_data(cmd->client_if, cmd->start); + if (!data) { + error("gatt: Could not allocate listen data"); + status = HAL_STATUS_NOMEM; + goto reply; + } + + if (!bt_le_set_advertising(cmd->start, set_advertising_cb, data)) { + error("gatt: Could not set advertising"); + status = HAL_STATUS_FAILED; + free(data); + goto reply; + } + + /* + * Use this flag to keep in mind that we are waiting for callback with + * result + */ + req_sent = true; + + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN, + status); + + /* In case of early success or error, just send notification up */ + if (!req_sent) { + int32_t gatt_status = status == HAL_STATUS_SUCCESS ? + GATT_SUCCESS : GATT_FAILURE; + send_client_listen_notify(cmd->client_if, gatt_status); + } +} + +static void handle_client_refresh(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_refresh *cmd = buf; + struct gatt_device *dev; + uint8_t status; + bdaddr_t bda; + + /* + * This is Android's framework hidden API call. It seams that no + * notification is expected and Bluedroid silently updates device's + * cache under the hood. As we use lazy caching ,we can just clear the + * cache and we're done. + */ + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &bda); + dev = find_device_by_addr(&bda); + if (!dev) { + status = HAL_STATUS_FAILED; + goto done; + } + + queue_remove_all(dev->services, NULL, NULL, destroy_service); + + status = HAL_STATUS_SUCCESS; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH, + status); +} + +static void handle_client_search_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_search_service *cmd = buf; + struct app_connection *conn; + uint8_t status; + struct service *s; + bt_uuid_t uuid; + guint srvc_search_success; + + DBG(""); + + if (len != sizeof(*cmd) + (cmd->filtered ? 16 : 0)) { + error("Invalid search service size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + conn = find_connection_by_id(cmd->conn_id); + if (!conn) { + error("gatt: dev with conn_id=%d not found", cmd->conn_id); + + status = HAL_STATUS_FAILED; + goto reply; + } + + if (conn->device->state != DEVICE_CONNECTED) { + char bda[18]; + + ba2str(&conn->device->bdaddr, bda); + error("gatt: device %s not connected", bda); + + status = HAL_STATUS_FAILED; + goto reply; + } + + if (cmd->filtered) + android2uuid(cmd->filter_uuid, &uuid); + + /* Services not cached yet */ + if (queue_isempty(conn->device->services)) { + if (cmd->filtered) + srvc_search_success = search_dev_for_srvc(conn, &uuid); + else + srvc_search_success = search_dev_for_srvc(conn, NULL); + + if (!srvc_search_success) { + status = HAL_STATUS_FAILED; + goto reply; + } + + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* Search in cached services for given service */ + if (cmd->filtered) { + /* Search in cache for service by uuid */ + s = queue_find(conn->device->services, match_srvc_by_bt_uuid, + &uuid); + + if (s) { + send_client_primary_notify(s, INT_TO_PTR(conn->id)); + } else { + if (!search_dev_for_srvc(conn, &uuid)) { + status = HAL_STATUS_FAILED; + goto reply; + } + + status = HAL_STATUS_SUCCESS; + goto reply; + } + } else { + /* Refresh service cache if only partial search was performed */ + if (conn->device->partial_srvc_search) { + srvc_search_success = search_dev_for_srvc(conn, NULL); + if (!srvc_search_success) { + status = HAL_STATUS_FAILED; + goto reply; + } + } else + queue_foreach(conn->device->services, + send_client_primary_notify, + INT_TO_PTR(cmd->conn_id)); + } + + send_client_search_complete_notify(GATT_SUCCESS, conn->id); + + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status); +} + +static void send_client_incl_service_notify(const struct element_id *srvc_id, + const struct service *incl, + int32_t conn_id) +{ + struct hal_ev_gatt_client_get_inc_service ev; + + memset(&ev, 0, sizeof(ev)); + + ev.conn_id = conn_id; + + element_id_to_hal_srvc_id(srvc_id, 1, &ev.srvc_id); + + if (incl) { + element_id_to_hal_srvc_id(&incl->id, 0, &ev.incl_srvc_id); + ev.status = GATT_SUCCESS; + } else { + ev.status = GATT_FAILURE; + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT , + HAL_EV_GATT_CLIENT_GET_INC_SERVICE, + sizeof(ev), &ev); +} + +struct get_included_data { + struct service *prim; + struct app_connection *conn; +}; + +static int get_inst_id_of_prim_services(const struct gatt_device *dev) +{ + struct service *s = queue_peek_tail(dev->services); + + if (s) + return s->id.instance; + + return -1; +} + +static void get_included_cb(uint8_t status, GSList *included, void *user_data) +{ + struct get_included_data *data = user_data; + struct app_connection *conn = data->conn; + struct service *service = data->prim; + struct service *incl = NULL; + int instance_id; + + DBG(""); + + free(data); + + if (status) { + error("gatt: no included services found"); + goto failed; + } + + /* Remember that we already search included services.*/ + service->incl_search_done = true; + + /* + * There might be multiply services with same uuid. Therefore make sure + * each service has unique instance id. Let's take the latest instance + * id of primary service and start iterate included services from this + * point. + */ + instance_id = get_inst_id_of_prim_services(conn->device); + if (instance_id < 0) + goto failed; + + for (; included; included = included->next) { + struct gatt_included *included_service = included->data; + + incl = create_service(++instance_id, false, + included_service->uuid, + included_service); + if (!incl) + continue; + + /* + * Lets keep included service on two queues. + * 1. on services queue together with primary service + * 2. on special queue inside primary service + */ + if (!queue_push_tail(service->included, incl) || + !queue_push_tail(conn->device->services, incl)) { + error("gatt: Cannot push incl service to the list"); + destroy_service(incl); + continue; + } + } + + /* + * Notify upper layer about first included service. + * Android framework will iterate for next one. + */ + incl = queue_peek_head(service->included); + +failed: + send_client_incl_service_notify(&service->id, incl, conn->id); +} + +static bool search_included_services(struct app_connection *connection, + struct service *service) +{ + struct get_included_data *data; + + data = new0(struct get_included_data, 1); + if (!data) { + error("gatt: failed to allocate memory for included_data"); + return false; + } + + data->prim = service; + data->conn = connection; + + gatt_find_included(connection->device->attrib, + service->prim.range.start, + service->prim.range.end, get_included_cb, data); + return true; +} + +static bool find_service(int32_t conn_id, struct element_id *service_id, + struct app_connection **connection, + struct service **service) +{ + struct service *srvc; + struct app_connection *conn; + + conn = find_connection_by_id(conn_id); + if (!conn) { + error("gatt: conn_id=%d not found", conn_id); + return false; + } + + srvc = queue_find(conn->device->services, match_srvc_by_element_id, + service_id); + if (!srvc) { + error("gatt: Service with inst_id: %d not found", + service_id->instance); + return false; + } + + *connection = conn; + *service = srvc; + + return true; +} + +static void handle_client_get_included_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_get_included_service *cmd = buf; + struct app_connection *conn; + struct service *prim_service; + struct service *incl_service = NULL; + struct element_id match_id; + struct element_id srvc_id; + uint8_t status; + + DBG(""); + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + + if (len != sizeof(*cmd) + + (cmd->continuation ? sizeof(cmd->incl_srvc_id[0]) : 0)) { + error("Invalid get incl services size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); + if (!find_service(cmd->conn_id, &match_id, &conn, &prim_service)) { + status = HAL_STATUS_FAILED; + goto notify; + } + + if (!prim_service->incl_search_done) { + if (search_included_services(conn, prim_service)) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + status = HAL_STATUS_FAILED; + goto notify; + } + + /* Try to use cache here */ + if (!cmd->continuation) { + incl_service = queue_peek_head(prim_service->included); + } else { + uint8_t inst_id = cmd->incl_srvc_id[0].inst_id; + incl_service = queue_find(prim_service->included, + match_srvc_by_higher_inst_id, + INT_TO_PTR(inst_id)); + } + + status = HAL_STATUS_SUCCESS; + +notify: + /* + * In case of error in handling request we need to send event with + * service id of cmd and gatt failure status. + */ + send_client_incl_service_notify(&srvc_id, incl_service, cmd->conn_id); + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, status); +} + +static void send_client_char_notify(const struct characteristic *ch, + int32_t conn_id, + const struct service *service) +{ + struct hal_ev_gatt_client_get_characteristic ev; + + memset(&ev, 0, sizeof(ev)); + ev.status = ch ? GATT_SUCCESS : GATT_FAILURE; + + if (ch) { + ev.char_prop = ch->ch.properties; + element_id_to_hal_gatt_id(&ch->id, &ev.char_id); + } + + ev.conn_id = conn_id; + element_id_to_hal_srvc_id(&service->id, service->primary, &ev.srvc_id); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC, + sizeof(ev), &ev); +} + +static void cache_all_srvc_chars(struct service *srvc, GSList *characteristics) +{ + uint16_t inst_id = 0; + bt_uuid_t uuid; + + for (; characteristics; characteristics = characteristics->next) { + struct characteristic *ch; + + ch = new0(struct characteristic, 1); + if (!ch) { + error("gatt: Error while caching characteristic"); + continue; + } + + ch->descriptors = queue_new(); + if (!ch->descriptors) { + error("gatt: Error while caching characteristic"); + free(ch); + continue; + } + + memcpy(&ch->ch, characteristics->data, sizeof(ch->ch)); + + bt_string_to_uuid(&uuid, ch->ch.uuid); + bt_uuid_to_uuid128(&uuid, &ch->id.uuid); + + /* + * For now we increment inst_id and use it as characteristic + * handle + */ + ch->id.instance = ++inst_id; + + /* Store end handle to use later for descriptors discovery */ + if (characteristics->next) { + struct gatt_char *next = characteristics->next->data; + ch->end_handle = next->handle - 1; + } else { + ch->end_handle = srvc->primary ? srvc->prim.range.end : + srvc->incl.range.end; + } + + DBG("attr handle = 0x%04x, end handle = 0x%04x uuid: %s", + ch->ch.handle, ch->end_handle, ch->ch.uuid); + + if (!queue_push_tail(srvc->chars, ch)) { + error("gatt: Error while caching characteristic"); + destroy_characteristic(ch); + } + } +} + +struct discover_char_data { + int32_t conn_id; + struct service *service; +}; + +static void discover_char_cb(uint8_t status, GSList *characteristics, + void *user_data) +{ + struct discover_char_data *data = user_data; + struct service *srvc = data->service; + + if (queue_isempty(srvc->chars)) + cache_all_srvc_chars(srvc, characteristics); + + send_client_char_notify(queue_peek_head(srvc->chars), data->conn_id, + srvc); + + free(data); +} + +static void handle_client_get_characteristic(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_get_characteristic *cmd = buf; + struct characteristic *ch; + struct element_id match_id; + struct app_connection *conn; + struct service *srvc; + uint8_t status; + + DBG(""); + + if (len != sizeof(*cmd) + (cmd->continuation ? sizeof(cmd->char_id[0]) : 0)) { + error("Invalid get characteristic size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); + if (!find_service(cmd->conn_id, &match_id, &conn, &srvc)) { + status = HAL_STATUS_FAILED; + goto done; + } + + /* Discover all characteristics for services if not cached yet */ + if (queue_isempty(srvc->chars)) { + struct att_range range; + + struct discover_char_data *cb_data = + new0(struct discover_char_data, 1); + + if (!cb_data) { + error("gatt: Cannot allocate cb data"); + status = HAL_STATUS_FAILED; + goto done; + } + + cb_data->service = srvc; + cb_data->conn_id = conn->id; + + range = srvc->primary ? srvc->prim.range : srvc->incl.range; + + if (!gatt_discover_char(conn->device->attrib, range.start, + range.end, NULL, + discover_char_cb, cb_data)) { + free(cb_data); + + status = HAL_STATUS_FAILED; + goto done; + } + + status = HAL_STATUS_SUCCESS; + goto done; + } + + if (cmd->continuation) + ch = queue_find(srvc->chars, match_char_by_higher_inst_id, + INT_TO_PTR(cmd->char_id[0].inst_id)); + else + ch = queue_peek_head(srvc->chars); + + send_client_char_notify(ch, conn->id, srvc); + + status = HAL_STATUS_SUCCESS; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, status); +} + +static void send_client_descr_notify(int32_t status, int32_t conn_id, + bool primary, + const struct element_id *srvc, + const struct element_id *ch, + const struct element_id *opt_descr) +{ + struct hal_ev_gatt_client_get_descriptor ev; + + memset(&ev, 0, sizeof(ev)); + + ev.status = status; + ev.conn_id = conn_id; + + element_id_to_hal_srvc_id(srvc, primary, &ev.srvc_id); + element_id_to_hal_gatt_id(ch, &ev.char_id); + + if (opt_descr) + element_id_to_hal_gatt_id(opt_descr, &ev.descr_id); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_GET_DESCRIPTOR, sizeof(ev), &ev); +} + +struct discover_desc_data { + struct app_connection *conn; + struct service *srvc; + struct characteristic *ch; +}; + +static void gatt_discover_desc_cb(guint8 status, GSList *descs, + gpointer user_data) +{ + struct discover_desc_data *data = user_data; + struct app_connection *conn = data->conn; + struct service *srvc = data->srvc; + struct characteristic *ch = data->ch; + struct descriptor *descr; + int i = 0; + + if (status != 0) { + error("Discover all characteristic descriptors failed [%s]: %s", + ch->ch.uuid, att_ecode2str(status)); + goto reply; + } + + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; + bt_uuid_t uuid; + + descr = new0(struct descriptor, 1); + if (!descr) + continue; + + bt_string_to_uuid(&uuid, desc->uuid); + bt_uuid_to_uuid128(&uuid, &descr->id.uuid); + + descr->id.instance = i++; + descr->handle = desc->handle; + + DBG("attr handle = 0x%04x, uuid: %s", desc->handle, desc->uuid); + + if (!queue_push_tail(ch->descriptors, descr)) + free(descr); + } + +reply: + descr = queue_peek_head(ch->descriptors); + + send_client_descr_notify(status, conn->id, srvc->primary, &srvc->id, + &ch->id, + descr ? &descr->id : NULL); + + free(data); +} + +static bool build_descr_cache(struct app_connection *connection, + struct service *srvc, + struct characteristic *ch) +{ + struct discover_desc_data *cb_data; + uint16_t start, end; + + /* Clip range to given characteristic */ + start = ch->ch.value_handle + 1; + end = ch->end_handle; + + /* If there are no descriptors, notify with fail status. */ + if (start > end) + return false; + + cb_data = new0(struct discover_desc_data, 1); + if (!cb_data) + return false; + + cb_data->conn = connection; + cb_data->srvc = srvc; + cb_data->ch = ch; + + if (!gatt_discover_desc(connection->device->attrib, start, end, NULL, + gatt_discover_desc_cb, cb_data)) { + free(cb_data); + return false; + } + + return true; +} + +static void handle_client_get_descriptor(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_get_descriptor *cmd = buf; + struct descriptor *descr = NULL; + struct characteristic *ch; + struct service *srvc; + struct element_id srvc_id; + struct element_id char_id; + struct app_connection *conn; + int32_t conn_id; + uint8_t primary; + uint8_t status; + + DBG(""); + + if (len != sizeof(*cmd) + + (cmd->continuation ? sizeof(cmd->descr_id[0]) : 0)) { + error("gatt: Invalid get descr command (%u bytes), terminating", + len); + + raise(SIGTERM); + return; + } + + conn_id = cmd->conn_id; + primary = cmd->srvc_id.is_primary; + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + + if (!find_service(conn_id, &srvc_id, &conn, &srvc)) { + error("gatt: Get descr. could not find service"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Get descr. could not find characteristic"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + if (queue_isempty(ch->descriptors)) { + if (build_descr_cache(conn, srvc, ch)) { + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, + HAL_STATUS_SUCCESS); + return; + } + } + + status = HAL_STATUS_SUCCESS; + + /* Send from cache */ + if (cmd->continuation) + descr = queue_find(ch->descriptors, + match_descr_by_higher_inst_id, + INT_TO_PTR(cmd->descr_id[0].inst_id)); + else + descr = queue_peek_head(ch->descriptors); + +failed: + send_client_descr_notify(descr ? GATT_SUCCESS : GATT_FAILURE, conn_id, + primary, &srvc_id, &char_id, + &descr->id); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, status); +} + +struct char_op_data { + int32_t conn_id; + const struct element_id *srvc_id; + const struct element_id *char_id; + uint8_t primary; +}; + +static struct char_op_data *create_char_op_data(int32_t conn_id, + const struct element_id *s_id, + const struct element_id *ch_id, + bool primary) +{ + struct char_op_data *d; + + d = new0(struct char_op_data, 1); + if (!d) + return NULL; + + d->conn_id = conn_id; + d->srvc_id = s_id; + d->char_id = ch_id; + d->primary = primary; + + return d; +} + +static void send_client_read_char_notify(int32_t status, const uint8_t *pdu, + uint16_t len, int32_t conn_id, + const struct element_id *s_id, + const struct element_id *ch_id, + uint8_t primary) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_read_characteristic *ev = (void *) buf; + ssize_t vlen; + + memset(buf, 0, sizeof(buf)); + + ev->conn_id = conn_id; + ev->status = status; + ev->data.status = status; + + element_id_to_hal_srvc_id(s_id, primary, &ev->data.srvc_id); + element_id_to_hal_gatt_id(ch_id, &ev->data.char_id); + + if (status == 0 && pdu) { + vlen = dec_read_resp(pdu, len, ev->data.value, sizeof(buf)); + if (vlen < 0) { + error("gatt: Protocol error"); + ev->status = GATT_FAILURE; + } else { + ev->data.len = vlen; + } + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC, + sizeof(*ev) + ev->data.len, ev); +} + +static void read_char_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct char_op_data *data = user_data; + + send_client_read_char_notify(status, pdu, len, data->conn_id, + data->srvc_id, data->char_id, + data->primary); + + free(data); +} + +static int get_cid(struct gatt_device *dev) +{ + GIOChannel *io; + int cid; + + io = g_attrib_get_channel(dev->attrib); + + if (!bt_io_get(io, NULL, BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID)) { + error("gatt: Failed to get CID"); + return -1; + } + + return cid; +} + +static int get_sec_level(struct gatt_device *dev) +{ + GIOChannel *io; + int sec_level; + + io = g_attrib_get_channel(dev->attrib); + + if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level, + BT_IO_OPT_INVALID)) { + error("gatt: Failed to get sec_level"); + return -1; + } + + return sec_level; +} + +static bool set_security(struct gatt_device *device, int req_sec_level) +{ + int sec_level; + GError *gerr = NULL; + GIOChannel *io; + + sec_level = get_sec_level(device); + if (sec_level < 0) + return false; + + if (req_sec_level <= sec_level) + return true; + + io = g_attrib_get_channel(device->attrib); + if (!io) + return false; + + bt_io_set(io, &gerr, BT_IO_OPT_SEC_LEVEL, req_sec_level, + BT_IO_OPT_INVALID); + if (gerr) { + error("gatt: Failed to set security level: %s", gerr->message); + g_error_free(gerr); + return false; + } + + return true; +} + +bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level) +{ + struct gatt_device *device; + + device = find_device_by_addr(bdaddr); + if (!device) + return false; + + return set_security(device, sec_level); +} + +static bool set_auth_type(struct gatt_device *device, int auth_type) +{ + int sec_level; + + switch (auth_type) { + case HAL_GATT_AUTHENTICATION_MITM: + sec_level = BT_SECURITY_HIGH; + break; + case HAL_GATT_AUTHENTICATION_NO_MITM: + sec_level = BT_SECURITY_MEDIUM; + break; + case HAL_GATT_AUTHENTICATION_NONE: + sec_level = BT_SECURITY_LOW; + break; + default: + error("gatt: Invalid auth_type value: %d", auth_type); + return false; + } + + return set_security(device, sec_level); +} + +static void handle_client_read_characteristic(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_read_characteristic *cmd = buf; + struct char_op_data *cb_data; + struct characteristic *ch; + struct app_connection *conn; + struct service *srvc; + struct element_id srvc_id; + struct element_id char_id; + uint8_t status; + + DBG(""); + + /* TODO authorization needs to be handled */ + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + + if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + /* search characteristics by element id */ + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Characteristic with inst_id: %d not found", + cmd->char_id.inst_id); + status = HAL_STATUS_FAILED; + goto failed; + } + + cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id, + cmd->srvc_id.is_primary); + if (!cb_data) { + error("gatt: Cannot allocate cb data"); + status = HAL_STATUS_NOMEM; + goto failed; + } + + if (!set_auth_type(conn->device, cmd->auth_req)) { + error("gatt: Failed to set security %d", cmd->auth_req); + status = HAL_STATUS_FAILED; + free(cb_data); + goto failed; + } + + if (!gatt_read_char(conn->device->attrib, ch->ch.value_handle, + read_char_cb, cb_data)) { + error("gatt: Cannot read characteristic with inst_id: %d", + cmd->char_id.inst_id); + status = HAL_STATUS_FAILED; + free(cb_data); + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, status); + + /* + * We should send notification with service, characteristic id in case + * of errors. + */ + if (status != HAL_STATUS_SUCCESS) + send_client_read_char_notify(GATT_FAILURE, NULL, 0, + cmd->conn_id, &srvc_id, &char_id, + cmd->srvc_id.is_primary); +} + +static void send_client_write_char_notify(int32_t status, int32_t conn_id, + const struct element_id *srvc_id, + const struct element_id *char_id, + uint8_t primary) +{ + struct hal_ev_gatt_client_write_characteristic ev; + + memset(&ev, 0, sizeof(ev)); + + ev.conn_id = conn_id; + ev.status = status; + + element_id_to_hal_srvc_id(srvc_id, primary, &ev.data.srvc_id); + element_id_to_hal_gatt_id(char_id, &ev.data.char_id); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC, + sizeof(ev), &ev); +} + +static void write_char_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct char_op_data *data = user_data; + + send_client_write_char_notify(status, data->conn_id, data->srvc_id, + data->char_id, data->primary); + + free(data); +} + +static guint signed_write_cmd(struct gatt_device *dev, uint16_t handle, + const uint8_t *value, uint16_t vlen) +{ + uint8_t csrk[16]; + uint32_t sign_cnt; + guint res; + + memset(csrk, 0, 16); + + if (!bt_get_csrk(&dev->bdaddr, LOCAL_CSRK, csrk, &sign_cnt)) { + error("gatt: Could not get csrk key"); + return 0; + } + + res = gatt_signed_write_cmd(dev->attrib, handle, value, vlen, crypto, + csrk, sign_cnt, NULL, NULL); + if (!res) { + error("gatt: Could write signed cmd"); + return 0; + } + + bt_update_sign_counter(&dev->bdaddr, LOCAL_CSRK); + + return res; +} + +static void handle_client_write_characteristic(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_write_characteristic *cmd = buf; + struct char_op_data *cb_data = NULL; + struct characteristic *ch; + struct app_connection *conn; + struct service *srvc; + struct element_id srvc_id; + struct element_id char_id; + uint8_t status; + guint res; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid write char size (%u bytes), terminating", len); + raise(SIGTERM); + return; + } + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + + if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + /* search characteristics by instance id */ + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Characteristic with inst_id: %d not found", + cmd->char_id.inst_id); + status = HAL_STATUS_FAILED; + goto failed; + } + + if (cmd->write_type == GATT_WRITE_TYPE_PREPARE || + cmd->write_type == GATT_WRITE_TYPE_DEFAULT) { + cb_data = create_char_op_data(cmd->conn_id, &srvc->id, &ch->id, + cmd->srvc_id.is_primary); + if (!cb_data) { + error("gatt: Cannot allocate call data"); + status = HAL_STATUS_NOMEM; + goto failed; + } + } + + if (!set_auth_type(conn->device, cmd->auth_req)) { + error("gatt: Failed to set security %d", cmd->auth_req); + status = HAL_STATUS_FAILED; + goto failed; + } + + switch (cmd->write_type) { + case GATT_WRITE_TYPE_NO_RESPONSE: + res = gatt_write_cmd(conn->device->attrib, ch->ch.value_handle, + cmd->value, cmd->len, + NULL, NULL); + break; + case GATT_WRITE_TYPE_PREPARE: + res = gatt_reliable_write_char(conn->device->attrib, + ch->ch.value_handle, + cmd->value, cmd->len, + write_char_cb, cb_data); + break; + case GATT_WRITE_TYPE_DEFAULT: + res = gatt_write_char(conn->device->attrib, ch->ch.value_handle, + cmd->value, cmd->len, + write_char_cb, cb_data); + break; + case GATT_WRITE_TYPE_SIGNED: + if (get_cid(conn->device) != ATT_CID) { + error("gatt: Cannot write signed on BR/EDR bearer"); + status = HAL_STATUS_FAILED; + goto failed; + } + + if (get_sec_level(conn->device) != BT_SECURITY_LOW) { + error("gatt: Cannot write signed on encrypted link"); + status = HAL_STATUS_FAILED; + goto failed; + } + + res = signed_write_cmd(conn->device, ch->ch.value_handle, + cmd->value, cmd->len); + break; + default: + error("gatt: Write type %d unsupported", cmd->write_type); + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } + + if (!res) { + error("gatt: Cannot write char. with inst_id: %d", + cmd->char_id.inst_id); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, status); + + /* + * We should send notification with service, characteristic id in case + * of error and write with no response + */ + if (status != HAL_STATUS_SUCCESS || + cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE || + cmd->write_type == GATT_WRITE_TYPE_SIGNED) { + int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ? + GATT_SUCCESS : GATT_FAILURE; + + send_client_write_char_notify(gatt_status, cmd->conn_id, + &srvc_id, &char_id, + cmd->srvc_id.is_primary); + free(cb_data); + } +} + +static void send_client_descr_read_notify(int32_t status, const uint8_t *pdu, + guint16 len, int32_t conn_id, + const struct element_id *srvc, + const struct element_id *ch, + const struct element_id *descr, + uint8_t primary) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_read_descriptor *ev = (void *) buf; + + memset(buf, 0, sizeof(buf)); + + ev->status = status; + ev->conn_id = conn_id; + ev->data.status = ev->status; + + element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id); + element_id_to_hal_gatt_id(ch, &ev->data.char_id); + element_id_to_hal_gatt_id(descr, &ev->data.descr_id); + + if (status == 0 && pdu) { + ssize_t ret; + + ret = dec_read_resp(pdu, len, ev->data.value, + GATT_MAX_ATTR_LEN); + if (ret < 0) { + error("gatt: Protocol error"); + ev->status = GATT_FAILURE; + } else { + ev->data.len = ret; + } + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_READ_DESCRIPTOR, + sizeof(*ev) + ev->data.len, ev); +} + +struct desc_data { + int32_t conn_id; + const struct element_id *srvc_id; + const struct element_id *char_id; + const struct element_id *descr_id; + uint8_t primary; +}; + +static void read_desc_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct desc_data *cb_data = user_data; + + if (status != 0) + error("gatt: Discover all char descriptors failed: %s", + att_ecode2str(status)); + + send_client_descr_read_notify(status, pdu, len, cb_data->conn_id, + cb_data->srvc_id, cb_data->char_id, + cb_data->descr_id, cb_data->primary); + + free(cb_data); +} + +static struct desc_data *create_desc_data(int32_t conn_id, + const struct element_id *s_id, + const struct element_id *ch_id, + const struct element_id *d_id, + uint8_t primary) +{ + struct desc_data *d; + + d = new0(struct desc_data, 1); + if (!d) + return NULL; + + d->conn_id = conn_id; + d->srvc_id = s_id; + d->char_id = ch_id; + d->descr_id = d_id; + d->primary = primary; + + return d; +} + +static void handle_client_read_descriptor(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_read_descriptor *cmd = buf; + struct desc_data *cb_data; + struct characteristic *ch; + struct descriptor *descr; + struct service *srvc; + struct element_id char_id; + struct element_id descr_id; + struct element_id srvc_id; + struct app_connection *conn; + int32_t conn_id = 0; + uint8_t primary; + uint8_t status; + + DBG(""); + + conn_id = cmd->conn_id; + primary = cmd->srvc_id.is_primary; + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id); + + if (!find_service(conn_id, &srvc_id, &conn, &srvc)) { + error("gatt: Read descr. could not find service"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Read descr. could not find characteristic"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + descr = queue_find(ch->descriptors, match_descr_by_element_id, + &descr_id); + if (!descr) { + error("gatt: Read descr. could not find descriptor"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, &descr->id, + primary); + if (!cb_data) { + error("gatt: Read descr. could not allocate callback data"); + + status = HAL_STATUS_NOMEM; + goto failed; + } + + if (!set_auth_type(conn->device, cmd->auth_req)) { + error("gatt: Failed to set security %d", cmd->auth_req); + status = HAL_STATUS_FAILED; + free(cb_data); + goto failed; + } + + if (!gatt_read_char(conn->device->attrib, descr->handle, read_desc_cb, + cb_data)) { + free(cb_data); + + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + if (status != HAL_STATUS_SUCCESS) + send_client_descr_read_notify(GATT_FAILURE, NULL, 0, conn_id, + &srvc_id, &char_id, &descr_id, + primary); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, status); +} + +static void send_client_descr_write_notify(int32_t status, int32_t conn_id, + const struct element_id *srvc, + const struct element_id *ch, + const struct element_id *descr, + uint8_t primary) { + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_write_descriptor *ev = (void *) buf; + + memset(buf, 0, sizeof(buf)); + + ev->status = status; + ev->conn_id = conn_id; + + element_id_to_hal_srvc_id(srvc, primary, &ev->data.srvc_id); + element_id_to_hal_gatt_id(ch, &ev->data.char_id); + element_id_to_hal_gatt_id(descr, &ev->data.descr_id); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR, + sizeof(*ev), ev); +} + +static void write_descr_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct desc_data *cb_data = user_data; + + if (status) + error("gatt: Write descriptors failed: %s", + att_ecode2str(status)); + + send_client_descr_write_notify(status, cb_data->conn_id, + cb_data->srvc_id, cb_data->char_id, + cb_data->descr_id, cb_data->primary); + + free(cb_data); +} + +static void handle_client_write_descriptor(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_write_descriptor *cmd = buf; + struct desc_data *cb_data = NULL; + struct characteristic *ch; + struct descriptor *descr; + struct service *srvc; + struct element_id srvc_id; + struct element_id char_id; + struct element_id descr_id; + struct app_connection *conn; + int32_t conn_id; + uint8_t primary; + uint8_t status; + guint res; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid write desriptor command (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + primary = cmd->srvc_id.is_primary; + conn_id = cmd->conn_id; + + hal_srvc_id_to_element_id(&cmd->srvc_id, &srvc_id); + hal_gatt_id_to_element_id(&cmd->char_id, &char_id); + hal_gatt_id_to_element_id(&cmd->descr_id, &descr_id); + + if (!find_service(cmd->conn_id, &srvc_id, &conn, &srvc)) { + error("gatt: Write descr. could not find service"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + ch = queue_find(srvc->chars, match_char_by_element_id, &char_id); + if (!ch) { + error("gatt: Write descr. could not find characteristic"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + descr = queue_find(ch->descriptors, match_descr_by_element_id, + &descr_id); + if (!descr) { + error("gatt: Write descr. could not find descriptor"); + + status = HAL_STATUS_FAILED; + goto failed; + } + + if (cmd->write_type != GATT_WRITE_TYPE_NO_RESPONSE) { + cb_data = create_desc_data(conn_id, &srvc->id, &ch->id, + &descr->id, primary); + if (!cb_data) { + error("gatt: Write descr. could not allocate cb_data"); + + status = HAL_STATUS_NOMEM; + goto failed; + } + } + + if (!set_auth_type(conn->device, cmd->auth_req)) { + error("gatt: Failed to set security %d", cmd->auth_req); + status = HAL_STATUS_FAILED; + goto failed; + } + + switch (cmd->write_type) { + case GATT_WRITE_TYPE_NO_RESPONSE: + res = gatt_write_cmd(conn->device->attrib, descr->handle, + cmd->value, cmd->len, NULL , NULL); + break; + case GATT_WRITE_TYPE_PREPARE: + res = gatt_reliable_write_char(conn->device->attrib, + descr->handle, cmd->value, + cmd->len, write_descr_cb, + cb_data); + break; + case GATT_WRITE_TYPE_DEFAULT: + res = gatt_write_char(conn->device->attrib, descr->handle, + cmd->value, cmd->len, + write_descr_cb, cb_data); + break; + default: + error("gatt: Write type %d unsupported", cmd->write_type); + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } + + if (!res) { + error("gatt: Write desc, could not write desc"); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + if (status != HAL_STATUS_SUCCESS || + cmd->write_type == GATT_WRITE_TYPE_NO_RESPONSE) { + int32_t gatt_status = (status == HAL_STATUS_SUCCESS) ? + GATT_SUCCESS : GATT_FAILURE; + + send_client_descr_write_notify(gatt_status, conn_id, &srvc_id, + &char_id, &descr_id, primary); + free(cb_data); + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, status); +} + +static void send_client_write_execute_notify(int32_t id, int32_t status) +{ + struct hal_ev_gatt_client_exec_write ev; + + ev.conn_id = id; + ev.status = status; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_EXEC_WRITE, + sizeof(ev), &ev); +} + +static void write_execute_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + send_client_write_execute_notify(PTR_TO_INT(user_data), status); +} + +static void handle_client_execute_write(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_execute_write *cmd = buf; + struct app_connection *conn; + uint8_t status; + uint8_t flags; + + DBG(""); + + conn = find_connection_by_id(cmd->conn_id); + if (!conn) { + status = HAL_STATUS_FAILED; + goto reply; + } + + flags = cmd->execute ? ATT_WRITE_ALL_PREP_WRITES : + ATT_CANCEL_ALL_PREP_WRITES; + + if (!gatt_execute_write(conn->device->attrib, flags, write_execute_cb, + INT_TO_PTR(cmd->conn_id))) { + error("gatt: Could not send execute write"); + status = HAL_STATUS_FAILED; + goto reply; + } + + status = HAL_STATUS_SUCCESS; +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_EXECUTE_WRITE, status); + + /* In case of early error send also notification.*/ + if (status != HAL_STATUS_SUCCESS) + send_client_write_execute_notify(cmd->conn_id, GATT_FAILURE); +} + +static void handle_notification(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_client_notify *ev = (void *) buf; + struct notification_data *notification = user_data; + uint8_t data_offset = sizeof(uint8_t) + sizeof(uint16_t); + + if (len < data_offset) + return; + + memcpy(&ev->char_id, ¬ification->ch, sizeof(ev->char_id)); + memcpy(&ev->srvc_id, ¬ification->service, sizeof(ev->srvc_id)); + bdaddr2android(¬ification->conn->device->bdaddr, &ev->bda); + ev->conn_id = notification->conn->id; + ev->is_notify = pdu[0] == ATT_OP_HANDLE_NOTIFY; + + /* We have to cut opcode and handle from data */ + ev->len = len - data_offset; + memcpy(ev->value, pdu + data_offset, len - data_offset); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_NOTIFY, + sizeof(*ev) + ev->len, ev); +} + +static void send_register_for_notification_ev(int32_t id, int32_t registered, + int32_t status, + const struct hal_gatt_srvc_id *srvc, + const struct hal_gatt_gatt_id *ch) +{ + struct hal_ev_gatt_client_reg_for_notif ev; + + ev.conn_id = id; + ev.status = status; + ev.registered = registered; + memcpy(&ev.srvc_id, srvc, sizeof(ev.srvc_id)); + memcpy(&ev.char_id, ch, sizeof(ev.char_id)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF, sizeof(ev), &ev); +} + +static void handle_client_register_for_notification(const void *buf, + uint16_t len) +{ + const struct hal_cmd_gatt_client_register_for_notification *cmd = buf; + struct notification_data *notification; + struct characteristic *c; + struct element_id match_id; + struct app_connection *conn; + int32_t conn_id = 0; + struct service *service; + uint8_t status; + int32_t gatt_status; + bdaddr_t addr; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &addr); + + conn = find_conn(&addr, cmd->client_if); + if (!conn) { + status = HAL_STATUS_FAILED; + goto failed; + } + + conn_id = conn->id; + + hal_srvc_id_to_element_id(&cmd->srvc_id, &match_id); + service = queue_find(conn->device->services, match_srvc_by_element_id, + &match_id); + if (!service) { + status = HAL_STATUS_FAILED; + goto failed; + } + + hal_gatt_id_to_element_id(&cmd->char_id, &match_id); + c = queue_find(service->chars, match_char_by_element_id, &match_id); + if (!c) { + status = HAL_STATUS_FAILED; + goto failed; + } + + notification = new0(struct notification_data, 1); + if (!notification) { + status = HAL_STATUS_FAILED; + goto failed; + } + + memcpy(¬ification->ch, &cmd->char_id, sizeof(notification->ch)); + memcpy(¬ification->service, &cmd->srvc_id, + sizeof(notification->service)); + notification->conn = conn; + + if (queue_find(conn->app->notifications, match_notification, + notification)) { + free(notification); + status = HAL_STATUS_SUCCESS; + goto failed; + } + + notification->notif_id = g_attrib_register(conn->device->attrib, + ATT_OP_HANDLE_NOTIFY, + c->ch.value_handle, + handle_notification, + notification, + destroy_notification); + if (!notification->notif_id) { + free(notification); + status = HAL_STATUS_FAILED; + goto failed; + } + + notification->ind_id = g_attrib_register(conn->device->attrib, + ATT_OP_HANDLE_IND, + c->ch.value_handle, + handle_notification, + notification, + destroy_notification); + if (!notification->ind_id) { + g_attrib_unregister(conn->device->attrib, + notification->notif_id); + free(notification); + status = HAL_STATUS_FAILED; + goto failed; + } + + /* + * Because same data - notification - is shared by two handlers, we + * introduce ref counter to be sure that data can be freed with no risk. + * Counter is decremented in destroy_notification. + */ + notification->ref = 2; + + if (!queue_push_tail(conn->app->notifications, notification)) { + unregister_notification(notification); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; + send_register_for_notification_ev(conn_id, 1, gatt_status, + &cmd->srvc_id, &cmd->char_id); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, status); +} + +static void handle_client_deregister_for_notification(const void *buf, + uint16_t len) +{ + const struct hal_cmd_gatt_client_deregister_for_notification *cmd = buf; + struct notification_data *notification, notif; + struct app_connection *conn; + int32_t conn_id = 0; + uint8_t status; + int32_t gatt_status; + bdaddr_t addr; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &addr); + + conn = find_conn(&addr, cmd->client_if); + if (!conn) { + status = HAL_STATUS_FAILED; + goto failed; + } + + conn_id = conn->id; + + memcpy(¬if.ch, &cmd->char_id, sizeof(notif.ch)); + memcpy(¬if.service, &cmd->srvc_id, sizeof(notif.service)); + notif.conn = conn; + + notification = queue_find(conn->app->notifications, + match_notification, ¬if); + if (!notification) { + status = HAL_STATUS_FAILED; + goto failed; + } + + unregister_notification(notification); + + status = HAL_STATUS_SUCCESS; + +failed: + gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; + send_register_for_notification_ev(conn_id, 0, gatt_status, + &cmd->srvc_id, &cmd->char_id); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, status); +} + +static void send_client_remote_rssi_notify(int32_t client_if, + const bdaddr_t *addr, + int32_t rssi, int32_t status) +{ + struct hal_ev_gatt_client_read_remote_rssi ev; + + ev.client_if = client_if; + bdaddr2android(addr, &ev.address); + ev.rssi = rssi; + ev.status = status; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI, sizeof(ev), &ev); +} + +static void read_remote_rssi_cb(uint8_t status, const bdaddr_t *addr, + int8_t rssi, void *user_data) +{ + int32_t client_if = PTR_TO_INT(user_data); + int32_t gatt_status = status ? GATT_FAILURE : GATT_SUCCESS; + + send_client_remote_rssi_notify(client_if, addr, rssi, gatt_status); +} + +static void handle_client_read_remote_rssi(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_read_remote_rssi *cmd = buf; + uint8_t status; + bdaddr_t bdaddr; + + DBG(""); + + if (!find_app_by_id(cmd->client_if)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2bdaddr(cmd->bdaddr, &bdaddr); + if (!bt_read_device_rssi(&bdaddr, read_remote_rssi_cb, + INT_TO_PTR(cmd->client_if))) { + error("gatt: Could not read RSSI"); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, status); + + if (status != HAL_STATUS_SUCCESS) + send_client_remote_rssi_notify(cmd->client_if, &bdaddr, 0, + GATT_FAILURE); +} + +static void handle_client_get_device_type(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_get_device_type *cmd = buf; + struct hal_rsp_gatt_client_get_device_type rsp; + bdaddr_t bdaddr; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + rsp.type = bt_get_device_android_type(&bdaddr); + + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, + sizeof(rsp), &rsp, -1); +} + +static void handle_client_set_adv_data(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_set_adv_data *cmd = buf; + uint8_t status; + + if (len != sizeof(*cmd) + cmd->manufacturer_len) { + error("Invalid set adv data command (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + DBG("scan_rsp=%u name=%u tx=%u min=%d max=%d app=%d", + cmd->set_scan_rsp, cmd->include_name, cmd->include_txpower, + cmd->min_interval, cmd->max_interval, cmd->appearance); + + DBG("manufacturer=%u service_data=%u service_uuid=%u", + cmd->manufacturer_len, cmd->service_data_len, + cmd->service_uuid_len); + + /* TODO This should be implemented when kernel supports it */ + if (cmd->manufacturer_len || cmd->service_data_len || + cmd->service_uuid_len) { + error("gatt: Extra advertising data not supported"); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_SET_ADV_DATA, status); +} + +static uint8_t test_read_write(bdaddr_t *bdaddr, bt_uuid_t *uuid, uint16_t op, + uint16_t u2,uint16_t u3, + uint16_t u4, uint16_t u5) +{ + guint16 length = 0; + struct gatt_device *dev; + uint8_t *pdu; + size_t mtu; + + dev = find_device_by_addr(bdaddr); + if (!dev || dev->state != DEVICE_CONNECTED) + return HAL_STATUS_FAILED; + + pdu = g_attrib_get_buffer(dev->attrib, &mtu); + if (!pdu) + return HAL_STATUS_FAILED; + + switch (op) { + case ATT_OP_READ_REQ: + length = enc_read_req(u2, pdu, mtu); + break; + case ATT_OP_READ_BY_TYPE_REQ: + length = enc_read_by_type_req(u2, u3, uuid, pdu, mtu); + break; + case ATT_OP_READ_BLOB_REQ: + length = enc_read_blob_req(u2, u3, pdu, mtu); + break; + case ATT_OP_READ_BY_GROUP_REQ: + length = enc_read_by_grp_req(u2, u3, uuid, pdu, mtu); + break; + case ATT_OP_READ_MULTI_REQ: + return HAL_STATUS_UNSUPPORTED; + case ATT_OP_WRITE_REQ: + length = enc_write_req(u2, (uint8_t *) &u3, sizeof(u3), pdu, + mtu); + break; + case ATT_OP_WRITE_CMD: + length = enc_write_cmd(u2, (uint8_t *) &u3, sizeof(u3), pdu, + mtu); + break; + case ATT_OP_PREP_WRITE_REQ: + length = enc_prep_write_req(u2, u3, (uint8_t *) &u4, sizeof(u4), + pdu, mtu); + break; + case ATT_OP_EXEC_WRITE_REQ: + length = enc_exec_write_req(u2, pdu, mtu); + break; + case ATT_OP_SIGNED_WRITE_CMD: + if (signed_write_cmd(dev, u2, (uint8_t *) &u3, sizeof(u3))) + return HAL_STATUS_SUCCESS; + else + return HAL_STATUS_FAILED; + default: + error("gatt: Unknown operation type"); + + return HAL_STATUS_UNSUPPORTED; + } + + if (!length) + return HAL_STATUS_FAILED; + + g_attrib_send(dev->attrib, 0, pdu, length, NULL, NULL, NULL); + + return HAL_STATUS_SUCCESS; +} + +static uint8_t test_increase_security(bdaddr_t *bdaddr, uint16_t u1) +{ + struct gatt_device *device; + + device = find_device_by_addr(bdaddr); + if (!device) + return HAL_STATUS_FAILED; + + if (!set_auth_type(device, u1)) + return HAL_STATUS_FAILED; + + return HAL_STATUS_SUCCESS; +} + +static void handle_client_test_command(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_client_test_command *cmd = buf; + struct gatt_app *app; + bdaddr_t bdaddr; + bt_uuid_t uuid; + uint8_t status; + + DBG(""); + + android2bdaddr(cmd->bda1, &bdaddr); + android2uuid(cmd->uuid1, &uuid); + + switch (cmd->command) { + case GATT_CLIENT_TEST_CMD_ENABLE: + if (cmd->u1) { + if (!test_client_if) { + app = register_app(TEST_UUID, GATT_CLIENT); + if (app) + test_client_if = app->id; + } + + if (test_client_if) + status = HAL_STATUS_SUCCESS; + else + status = HAL_STATUS_FAILED; + } else { + status = unregister_app(test_client_if); + test_client_if = 0; + } + break; + case GATT_CLIENT_TEST_CMD_CONNECT: + /* TODO u1 holds device type, for now assume BLE */ + status = handle_connect(test_client_if, &bdaddr); + break; + case GATT_CLIENT_TEST_CMD_DISCONNECT: + app = queue_find(gatt_apps, match_app_by_id, + INT_TO_PTR(test_client_if)); + if (app) + app_disconnect_devices(app); + + status = HAL_STATUS_SUCCESS; + break; + case GATT_CLIENT_TEST_CMD_DISCOVER: + status = HAL_STATUS_FAILED; + break; + case GATT_CLIENT_TEST_CMD_READ: + case GATT_CLIENT_TEST_CMD_WRITE: + status = test_read_write(&bdaddr, &uuid, cmd->u1, cmd->u2, + cmd->u3, cmd->u4, cmd->u5); + break; + case GATT_CLIENT_TEST_CMD_INCREASE_SECURITY: + status = test_increase_security(&bdaddr, cmd->u1); + break; + case GATT_CLIENT_TEST_CMD_PAIRING_CONFIG: + default: + status = HAL_STATUS_FAILED; + break; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_TEST_COMMAND, status); +} + +static void handle_server_register(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_register *cmd = buf; + struct hal_ev_gatt_server_register ev; + struct gatt_app *app; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + app = register_app(cmd->uuid, GATT_SERVER); + + if (app) { + ev.server_if = app->id; + ev.status = GATT_SUCCESS; + } else { + ev.status = GATT_FAILURE; + } + + memcpy(ev.uuid, cmd->uuid, sizeof(ev.uuid)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_REGISTER, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER, + HAL_STATUS_SUCCESS); +} + +static void handle_server_unregister(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_unregister *cmd = buf; + uint8_t status; + + DBG(""); + + status = unregister_app(cmd->server_if); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_UNREGISTER, status); +} + +static void handle_server_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_connect *cmd = buf; + uint8_t status; + bdaddr_t addr; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &addr); + + status = handle_connect(cmd->server_if, &addr); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT, + status); +} + +static void handle_server_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_disconnect *cmd = buf; + struct app_connection *conn; + uint8_t status; + + DBG(""); + + /* TODO: should we care to match also bdaddr when conn_id is unique? */ + conn = find_connection_by_id(cmd->conn_id); + if (conn) + trigger_disconnection(conn); + + status = HAL_STATUS_SUCCESS; + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_DISCONNECT, status); +} + +static void handle_server_add_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_add_service *cmd = buf; + struct hal_ev_gatt_server_service_added ev; + struct gatt_app *server; + uint8_t status; + bt_uuid_t uuid; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2uuid(cmd->srvc_id.uuid, &uuid); + + ev.srvc_handle = gatt_db_add_service(gatt_db, &uuid, + cmd->srvc_id.is_primary, + cmd->num_handles); + if (!ev.srvc_handle) { + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + ev.srvc_id = cmd->srvc_id; + ev.server_if = cmd->server_if; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_SERVICE_ADDED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_SERVICE, status); +} + +static void handle_server_add_included_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_add_inc_service *cmd = buf; + struct hal_ev_gatt_server_inc_srvc_added ev; + struct gatt_app *server; + uint8_t status; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + ev.incl_srvc_handle = gatt_db_add_included_service(gatt_db, + cmd->service_handle, + cmd->included_handle); + if (!ev.incl_srvc_handle) { + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; +failed: + ev.srvc_handle = cmd->service_handle; + ev.status = status; + ev.server_if = cmd->server_if; + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_INC_SRVC_ADDED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status); +} + +static bool is_service(const bt_uuid_t *type) +{ + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); + if (!bt_uuid_cmp(&uuid, type)) + return true; + + bt_uuid16_create(&uuid, GATT_SND_SVC_UUID); + if (!bt_uuid_cmp(&uuid, type)) + return true; + + return false; +} + +static bool match_pending_dev_request(const void *data, const void *user_data) +{ + const struct pending_request *pending_request = data; + + return pending_request->state == REQUEST_PENDING; +} + +static void send_dev_complete_response(struct gatt_device *device, + uint8_t opcode) +{ + size_t mtu; + uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu); + struct pending_request *val; + uint16_t len = 0; + uint8_t error = 0; + + if (queue_find(device->pending_requests, match_pending_dev_request, + NULL)) { + DBG("Still pending requests"); + return; + } + + switch (opcode) { + case ATT_OP_READ_BY_TYPE_REQ: { + struct att_data_list *adl; + int iterator = 0; + int length; + struct queue *temp; + + temp = queue_new(); + if (!temp) + goto done; + + val = queue_pop_head(device->pending_requests); + if (!val) { + queue_destroy(temp, NULL); + error = ATT_ECODE_ATTR_NOT_FOUND; + goto done; + } + + if (val->error) { + queue_destroy(temp, NULL); + error = val->error; + goto done; + } + + length = val->length; + + while (val && val->length == length && val->error == 0) { + queue_push_tail(temp, val); + val = queue_pop_head(device->pending_requests); + } + + adl = att_data_list_alloc(queue_length(temp), sizeof(uint16_t) + + length); + + val = queue_pop_head(temp); + while (val) { + uint8_t *value = adl->data[iterator++]; + + put_le16(val->handle, value); + memcpy(&value[2], val->value, val->length); + + destroy_pending_request(val); + val = queue_pop_head(temp); + } + + len = enc_read_by_type_resp(adl, rsp, mtu); + + att_data_list_free(adl); + queue_destroy(temp, destroy_pending_request); + + break; + } + case ATT_OP_READ_BLOB_REQ: + val = queue_pop_head(device->pending_requests); + if (val->error) { + error = val->error; + goto done; + } + + len = enc_read_blob_resp(val->value, val->length, val->offset, + rsp, mtu); + destroy_pending_request(val); + break; + case ATT_OP_READ_REQ: + val = queue_pop_head(device->pending_requests); + if (val->error) { + error = val->error; + goto done; + } + + len = enc_read_resp(val->value, val->length, rsp, mtu); + destroy_pending_request(val); + break; + case ATT_OP_READ_BY_GROUP_REQ: { + struct att_data_list *adl; + int iterator = 0; + int length; + struct queue *temp; + + temp = queue_new(); + if (!temp) + goto done; + + val = queue_pop_head(device->pending_requests); + if (!val) { + queue_destroy(temp, NULL); + error = ATT_ECODE_ATTR_NOT_FOUND; + goto done; + } + + length = val->length; + + while (val && val->length == length) { + queue_push_tail(temp, val); + val = queue_pop_head(device->pending_requests); + } + + adl = att_data_list_alloc(queue_length(temp), + 2 * sizeof(uint16_t) + length); + + val = queue_pop_head(temp); + while (val) { + uint8_t *value = adl->data[iterator++]; + uint16_t end_handle; + + end_handle = gatt_db_get_end_handle(gatt_db, + val->handle); + + put_le16(val->handle, value); + put_le16(end_handle, &value[2]); + memcpy(&value[4], val->value, val->length); + + destroy_pending_request(val); + val = queue_pop_head(temp); + } + + len = enc_read_by_grp_resp(adl, rsp, mtu); + + att_data_list_free(adl); + queue_destroy(temp, destroy_pending_request); + + break; + } + case ATT_OP_FIND_BY_TYPE_REQ: { + GSList *list = NULL; + + val = queue_pop_head(device->pending_requests); + while (val) { + struct att_range *range; + const bt_uuid_t *type; + + /* Its find by type and value - filter by value here */ + if ((val->length != val->filter_vlen) || + memcmp(val->value, val->filter_value, + val->length)) { + + destroy_pending_request(val); + val = queue_pop_head(device->pending_requests); + continue; + } + + range = new0(struct att_range, 1); + if (!range) { + destroy_pending_request(val); + error = ATT_ECODE_INSUFF_RESOURCES; + break; + } + + range->start = val->handle; + range->end = range->start; + + /* Get proper end handle if its group type */ + type = gatt_db_get_attribute_type(gatt_db, val->handle); + if (is_service(type)) + range->end = gatt_db_get_end_handle(gatt_db, + val->handle); + + list = g_slist_append(list, range); + + destroy_pending_request(val); + val = queue_pop_head(device->pending_requests); + } + + if (list && !error) + len = enc_find_by_type_resp(list, rsp, mtu); + else + error = ATT_ECODE_ATTR_NOT_FOUND; + + g_slist_free_full(list, free); + + break; + } + case ATT_OP_EXEC_WRITE_REQ: + val = queue_pop_head(device->pending_requests); + if (val->error) { + error = val->error; + goto done; + } + + len = enc_exec_write_resp(rsp); + destroy_pending_request(val); + break; + case ATT_OP_WRITE_REQ: + val = queue_pop_head(device->pending_requests); + if (val->error) { + error = val->error; + goto done; + } + + len = enc_write_resp(rsp); + destroy_pending_request(val); + break; + case ATT_OP_PREP_WRITE_REQ: + val = queue_pop_head(device->pending_requests); + if (val->error) { + error = val->error; + goto done; + } + + len = enc_prep_write_resp(val->handle, val->offset, val->value, + val->length, rsp, mtu); + destroy_pending_request(val); + break; + default: + break; + } + +done: + if (!len) + len = enc_error_resp(opcode, 0x0000, error, rsp, mtu); + + g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL); + + queue_remove_all(device->pending_requests, NULL, NULL, + destroy_pending_request); +} + +struct request_processing_data { + uint8_t opcode; + struct gatt_device *device; +}; + +static bool match_dev_request_by_handle(const void *data, const void *user_data) +{ + const struct pending_request *handle_data = data; + uint16_t handle = PTR_TO_UINT(user_data); + + return handle_data->handle == handle; +} + +static uint8_t check_device_permissions(struct gatt_device *device, + uint8_t opcode, uint32_t permissions) +{ + GIOChannel *io; + int sec_level; + + io = g_attrib_get_channel(device->attrib); + + if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level, + BT_IO_OPT_INVALID)) + return ATT_ECODE_UNLIKELY; + + DBG("opcode %u permissions %u sec_level %u", opcode, permissions, + sec_level); + + switch (opcode) { + case ATT_OP_SIGNED_WRITE_CMD: + if (!(permissions & GATT_PERM_WRITE_SIGNED)) + return ATT_ECODE_WRITE_NOT_PERM; + + if ((permissions & GATT_PERM_WRITE_SIGNED_MITM) && + sec_level < BT_SECURITY_HIGH) + return ATT_ECODE_AUTHENTICATION; + break; + case ATT_OP_READ_BY_TYPE_REQ: + case ATT_OP_READ_REQ: + case ATT_OP_READ_BLOB_REQ: + case ATT_OP_READ_MULTI_REQ: + case ATT_OP_READ_BY_GROUP_REQ: + case ATT_OP_FIND_BY_TYPE_REQ: + case ATT_OP_FIND_INFO_REQ: + if (!(permissions & GATT_PERM_READ)) + return ATT_ECODE_READ_NOT_PERM; + + if ((permissions & GATT_PERM_READ_MITM) && + sec_level < BT_SECURITY_HIGH) + return ATT_ECODE_AUTHENTICATION; + + if ((permissions & GATT_PERM_READ_ENCRYPTED) && + sec_level < BT_SECURITY_MEDIUM) + return ATT_ECODE_INSUFF_ENC; + + if (permissions & GATT_PERM_READ_AUTHORIZATION) + return ATT_ECODE_AUTHORIZATION; + break; + case ATT_OP_WRITE_REQ: + case ATT_OP_WRITE_CMD: + case ATT_OP_PREP_WRITE_REQ: + case ATT_OP_EXEC_WRITE_REQ: + if (!(permissions & GATT_PERM_WRITE)) + return ATT_ECODE_WRITE_NOT_PERM; + + if ((permissions & GATT_PERM_WRITE_MITM) && + sec_level < BT_SECURITY_HIGH) + return ATT_ECODE_AUTHENTICATION; + + if ((permissions & GATT_PERM_WRITE_ENCRYPTED) && + sec_level < BT_SECURITY_MEDIUM) + return ATT_ECODE_INSUFF_ENC; + + if (permissions & GATT_PERM_WRITE_AUTHORIZATION) + return ATT_ECODE_AUTHORIZATION; + break; + default: + return ATT_ECODE_UNLIKELY; + } + + return 0; +} + +static void fill_gatt_response(struct pending_request *request, uint16_t handle, + uint16_t offset, uint8_t status, + uint16_t len, const uint8_t *data) +{ + request->handle = handle; + request->offset = offset; + request->length = len; + request->state = REQUEST_DONE; + request->error = status; + + if (!len) + return; + + request->value = malloc0(len); + if (!request->value) { + request->error = ATT_ECODE_INSUFF_RESOURCES; + + return; + } + + memcpy(request->value, data, len); +} + +static void fill_gatt_response_by_handle(uint16_t handle, uint16_t offset, + uint8_t status, uint16_t len, + const uint8_t *data, + struct gatt_device *dev) +{ + struct pending_request *entry; + + entry = queue_find(dev->pending_requests, match_dev_request_by_handle, + UINT_TO_PTR(handle)); + if (!entry) { + error("gatt: No pending response! Bogus android response?"); + return; + } + + fill_gatt_response(entry, handle, offset, status, len, data); +} + +static void read_requested_attributes(void *data, void *user_data) +{ + struct pending_request *resp_data = data; + struct request_processing_data *process_data = user_data; + uint32_t permissions; + uint8_t *value = NULL, error; + int value_len = 0; + + if (!gatt_db_get_attribute_permissions(gatt_db, resp_data->handle, + &permissions)) { + resp_data->error = ATT_ECODE_ATTR_NOT_FOUND; + resp_data->state = REQUEST_DONE; + return; + } + + /* + * Check if it is attribute we didn't declare permissions, like service + * declaration or included service. Set permissions to read only + */ + if (permissions == 0) + permissions = GATT_PERM_READ; + + error = check_device_permissions(process_data->device, + process_data->opcode, + permissions); + if (error != 0) { + resp_data->error = error; + resp_data->state = REQUEST_DONE; + return; + } + + resp_data->state = REQUEST_PENDING; + + if (!gatt_db_read(gatt_db, resp_data->handle, + resp_data->offset, + process_data->opcode, + &process_data->device->bdaddr, + &value, &value_len)) + error = ATT_ECODE_UNLIKELY; + + /* We have value here already if no callback will be called */ + if (value_len >= 0) + fill_gatt_response(resp_data, resp_data->handle, + resp_data->offset, error, value_len, + value); +} + +static void process_dev_pending_requests(struct gatt_device *device, + uint8_t att_opcode) +{ + struct request_processing_data process_data; + + if (queue_isempty(device->pending_requests)) + return; + + process_data.device = device; + process_data.opcode = att_opcode; + + /* Process pending requests and prepare response */ + queue_foreach(device->pending_requests, read_requested_attributes, + &process_data); + + send_dev_complete_response(device, att_opcode); +} + +static struct pending_trans_data *conn_add_transact(struct app_connection *conn, + uint8_t opcode) +{ + struct pending_trans_data *transaction; + static int32_t trans_id = 1; + + transaction = new0(struct pending_trans_data, 1); + if (!transaction) + return NULL; + + if (!queue_push_tail(conn->transactions, transaction)) { + free(transaction); + return NULL; + } + + transaction->id = trans_id++; + transaction->opcode = opcode; + + return transaction; +} + +static void read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, + bdaddr_t *bdaddr, void *user_data) +{ + struct pending_trans_data *transaction; + struct hal_ev_gatt_server_request_read ev; + struct gatt_app *app; + struct app_connection *conn; + int32_t id = PTR_TO_INT(user_data); + struct gatt_device *dev; + + app = find_app_by_id(id); + if (!app) { + error("gatt: read_cb, cound not found app id"); + goto failed; + } + + conn = find_conn(bdaddr, app->id); + if (!conn) { + error("gatt: read_cb, cound not found connection"); + goto failed; + } + + memset(&ev, 0, sizeof(ev)); + + /* Store the request data, complete callback and transaction id */ + transaction = conn_add_transact(conn, att_opcode); + if (!transaction) + goto failed; + + bdaddr2android(bdaddr, ev.bdaddr); + ev.conn_id = conn->id; + ev.attr_handle = handle; + ev.offset = offset; + ev.is_long = att_opcode == ATT_OP_READ_BLOB_REQ; + ev.trans_id = transaction->id; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_REQUEST_READ, + sizeof(ev), &ev); + + return; + +failed: + dev = find_device_by_addr(bdaddr); + if (dev) + fill_gatt_response_by_handle(handle, 0, ATT_ECODE_UNLIKELY, 0, + NULL, dev); +} + +static void write_cb(uint16_t handle, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t att_opcode, bdaddr_t *bdaddr, + void *user_data) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_gatt_server_request_write *ev = (void *) buf; + struct pending_trans_data *transaction; + struct gatt_app *app; + int32_t id = PTR_TO_INT(user_data); + struct app_connection *conn; + struct gatt_device *dev; + + app = find_app_by_id(id); + if (!app) { + error("gatt: write_cb could not found app id"); + goto failed; + } + + conn = find_conn(bdaddr, app->id); + if (!conn) { + error("gatt: write_cb could not found connection"); + goto failed; + } + + /* + * Remember that this application has ongoing prep write + * Need it later to find out where to send execute write + */ + if (att_opcode == ATT_OP_PREP_WRITE_REQ) + conn->wait_execute_write = true; + + /* Store the request data, complete callback and transaction id */ + transaction = conn_add_transact(conn, att_opcode); + if (!transaction) + goto failed; + + memset(ev, 0, sizeof(*ev)); + + bdaddr2android(bdaddr, &ev->bdaddr); + ev->attr_handle = handle; + ev->offset = offset; + + ev->conn_id = conn->id; + ev->trans_id = transaction->id; + + ev->is_prep = att_opcode == ATT_OP_PREP_WRITE_REQ; + + if (att_opcode == ATT_OP_WRITE_REQ || + att_opcode == ATT_OP_PREP_WRITE_REQ) + ev->need_rsp = 0x01; + + ev->length = len; + memcpy(ev->value, value, len); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_REQUEST_WRITE, + sizeof(*ev) + ev->length , ev); + return; + +failed: + dev = find_device_by_addr(bdaddr); + if (dev) + fill_gatt_response_by_handle(handle, 0, ATT_ECODE_UNLIKELY, 0, + NULL, dev); +} + +static uint32_t android_to_gatt_permissions(int32_t hal_permissions) +{ + uint32_t permissions = 0; + + if (hal_permissions & HAL_GATT_PERMISSION_READ) + permissions |= GATT_PERM_READ; + + if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED) + permissions |= GATT_PERM_READ_ENCRYPTED | GATT_PERM_READ; + + if (hal_permissions & HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM) + permissions |= GATT_PERM_READ_MITM | GATT_PERM_READ_ENCRYPTED | + GATT_PERM_READ; + + if (hal_permissions & HAL_GATT_PERMISSION_WRITE) + permissions |= GATT_PERM_WRITE; + + if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED) + permissions |= GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE; + + if (hal_permissions & HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM) + permissions |= GATT_PERM_WRITE_MITM | + GATT_PERM_WRITE_ENCRYPTED | GATT_PERM_WRITE; + + if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED) + permissions |= GATT_PERM_WRITE_SIGNED; + + if (hal_permissions & HAL_GATT_PERMISSION_WRITE_SIGNED_MITM) + permissions |= GATT_PERM_WRITE_SIGNED_MITM | + GATT_PERM_WRITE_SIGNED; + + return permissions; +} + +static void handle_server_add_characteristic(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_add_characteristic *cmd = buf; + struct hal_ev_gatt_server_characteristic_added ev; + struct gatt_app *server; + bt_uuid_t uuid; + uint8_t status; + uint32_t permissions; + int32_t app_id = cmd->server_if; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(app_id); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2uuid(cmd->uuid, &uuid); + permissions = android_to_gatt_permissions(cmd->permissions); + + ev.char_handle = gatt_db_add_characteristic(gatt_db, + cmd->service_handle, + &uuid, permissions, + cmd->properties, + read_cb, write_cb, + INT_TO_PTR(app_id)); + if (!ev.char_handle) + status = HAL_STATUS_FAILED; + else + status = HAL_STATUS_SUCCESS; + +failed: + ev.srvc_handle = cmd->service_handle; + ev.status = status; + ev.server_if = app_id; + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_CHAR_ADDED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, status); +} + +static void handle_server_add_descriptor(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_add_descriptor *cmd = buf; + struct hal_ev_gatt_server_descriptor_added ev; + struct gatt_app *server; + bt_uuid_t uuid; + uint8_t status; + uint32_t permissions; + int32_t app_id = cmd->server_if; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(app_id); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2uuid(cmd->uuid, &uuid); + permissions = android_to_gatt_permissions(cmd->permissions); + + ev.descr_handle = gatt_db_add_char_descriptor(gatt_db, + cmd->service_handle, + &uuid, permissions, + read_cb, write_cb, + INT_TO_PTR(app_id)); + if (!ev.descr_handle) + status = HAL_STATUS_FAILED; + else + status = HAL_STATUS_SUCCESS; + +failed: + ev.server_if = app_id; + ev.srvc_handle = cmd->service_handle; + memcpy(ev.uuid, cmd->uuid, sizeof(cmd->uuid)); + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, status); +} + +static void notify_service_change(void *data, void *user_data) +{ + struct att_range range; + + range.start = PTR_TO_UINT(user_data); + range.end = gatt_db_get_end_handle(gatt_db, range.start); + + /* In case of db error */ + if (!range.end) + return; + + notify_att_range_change(data, &range); +} + +static sdp_record_t *get_sdp_record(uuid_t *uuid, uint16_t start, uint16_t end, + const char *name) +{ + sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto; + uuid_t root_uuid, proto_uuid, l2cap; + sdp_record_t *record; + sdp_data_t *psm, *sh, *eh; + uint16_t lp = ATT_PSM; + + record = sdp_record_alloc(); + if (record == NULL) + return NULL; + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + sdp_list_free(root, NULL); + + svclass_id = sdp_list_append(NULL, uuid); + sdp_set_service_classes(record, svclass_id); + sdp_list_free(svclass_id, NULL); + + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap); + psm = sdp_data_alloc(SDP_UINT16, &lp); + proto[0] = sdp_list_append(proto[0], psm); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&proto_uuid, ATT_UUID); + proto[1] = sdp_list_append(NULL, &proto_uuid); + sh = sdp_data_alloc(SDP_UINT16, &start); + proto[1] = sdp_list_append(proto[1], sh); + eh = sdp_data_alloc(SDP_UINT16, &end); + proto[1] = sdp_list_append(proto[1], eh); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + if (name) + sdp_set_info_attr(record, name, "BlueZ for Android", NULL); + + sdp_data_free(psm); + sdp_data_free(sh); + sdp_data_free(eh); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(aproto, NULL); + + return record; +} + +static uint32_t add_sdp_record(const bt_uuid_t *uuid, uint16_t start, + uint16_t end, const char *name) +{ + sdp_record_t *rec; + uuid_t u, u32; + + switch (uuid->type) { + case BT_UUID16: + sdp_uuid16_create(&u, uuid->value.u16); + break; + case BT_UUID32: + sdp_uuid32_create(&u32, uuid->value.u32); + sdp_uuid32_to_uuid128(&u, &u32); + break; + case BT_UUID128: + sdp_uuid128_create(&u, &uuid->value.u128); + break; + default: + return 0; + } + + rec = get_sdp_record(&u, start, end, name); + if (!rec) + return 0; + + if (bt_adapter_add_record(rec, 0) < 0) { + error("gatt: Failed to register SDP record"); + sdp_record_free(rec); + return 0; + } + + return rec->handle; +} + +static bool match_service_sdp(const void *data, const void *user_data) +{ + const struct service_sdp *s = data; + + return s->service_handle == PTR_TO_INT(user_data); +} + +static struct service_sdp *new_service_sdp_record(int32_t service_handle) +{ + bt_uuid_t uuid; + struct service_sdp *s; + uint16_t end_handle; + + end_handle = gatt_db_get_end_handle(gatt_db, service_handle); + if (!end_handle) + return NULL; + + if (!gatt_db_get_service_uuid(gatt_db, service_handle, &uuid)) + return NULL; + + s = new0(struct service_sdp, 1); + if (!s) + return NULL; + + s->service_handle = service_handle; + s->sdp_handle = add_sdp_record(&uuid, service_handle, end_handle, NULL); + if (!s->sdp_handle) { + free(s); + return NULL; + } + + return s; +} + +static void free_service_sdp_record(void *data) +{ + struct service_sdp *s = data; + + if (!s) + return; + + bt_adapter_remove_record(s->sdp_handle); + free(s); +} + +static bool add_service_sdp_record(int32_t service_handle) +{ + struct service_sdp *s; + + s = queue_find(services_sdp, match_service_sdp, + INT_TO_PTR(service_handle)); + if (s) + return true; + + s = new_service_sdp_record(service_handle); + if (!s) + return false; + + if (!queue_push_tail(services_sdp, s)) { + free_service_sdp_record(s); + return false; + } + + return true; +} + +static void remove_service_sdp_record(int32_t service_handle) +{ + struct service_sdp *s; + + s = queue_remove_if(services_sdp, match_service_sdp, + INT_TO_PTR(service_handle)); + if (!s) + return; + + free_service_sdp_record(s); +} + +static void handle_server_start_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_start_service *cmd = buf; + struct hal_ev_gatt_server_service_started ev; + struct gatt_app *server; + uint8_t status; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + switch (cmd->transport) { + case GATT_SERVER_TRANSPORT_BREDR: + case GATT_SERVER_TRANSPORT_LE_BREDR: + if (!add_service_sdp_record(cmd->service_handle)) { + status = HAL_STATUS_FAILED; + goto failed; + } + break; + case GATT_SERVER_TRANSPORT_LE: + break; + default: + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, true)) { + /* + * no need to clean SDP since this can fail only if service + * handle is invalid in which case add_sdp_record() also fails + */ + status = HAL_STATUS_FAILED; + goto failed; + } + + queue_foreach(gatt_devices, notify_service_change, + UINT_TO_PTR(cmd->service_handle)); + + status = HAL_STATUS_SUCCESS; + +failed: + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + ev.server_if = cmd->server_if; + ev.srvc_handle = cmd->service_handle; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_SERVICE_STARTED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_START_SERVICE, status); +} + +static void handle_server_stop_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_stop_service *cmd = buf; + struct hal_ev_gatt_server_service_stopped ev; + struct gatt_app *server; + uint8_t status; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!gatt_db_service_set_active(gatt_db, cmd->service_handle, false)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + remove_service_sdp_record(cmd->service_handle); + + status = HAL_STATUS_SUCCESS; + + queue_foreach(gatt_devices, notify_service_change, + UINT_TO_PTR(cmd->service_handle)); + +failed: + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + ev.server_if = cmd->server_if; + ev.srvc_handle = cmd->service_handle; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_SERVICE_STOPPED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_STOP_SERVICE, status); +} + +static void handle_server_delete_service(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_delete_service *cmd = buf; + struct hal_ev_gatt_server_service_deleted ev; + struct gatt_app *server; + uint8_t status; + + DBG(""); + + memset(&ev, 0, sizeof(ev)); + + server = find_app_by_id(cmd->server_if); + if (!server) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!gatt_db_remove_service(gatt_db, cmd->service_handle)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + remove_service_sdp_record(cmd->service_handle); + + status = HAL_STATUS_SUCCESS; + +failed: + ev.status = status == HAL_STATUS_SUCCESS ? GATT_SUCCESS : GATT_FAILURE; + ev.srvc_handle = cmd->service_handle; + ev.server_if = cmd->server_if; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_SERVICE_DELETED, sizeof(ev), &ev); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_DELETE_SERVICE, status); +} + +static void handle_server_send_indication(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_send_indication *cmd = buf; + struct app_connection *conn; + uint8_t status; + uint16_t length; + uint8_t *pdu; + size_t mtu; + + DBG(""); + + conn = find_connection_by_id(cmd->conn_id); + if (!conn) { + error("gatt: Could not find connection"); + status = HAL_STATUS_FAILED; + goto reply; + } + + pdu = g_attrib_get_buffer(conn->device->attrib, &mtu); + + if (cmd->confirm) + /* TODO: Add data to track confirmation for this request */ + length = enc_indication(cmd->attribute_handle, + (uint8_t *)cmd->value, cmd->len, pdu, + mtu); + else + length = enc_notification(cmd->attribute_handle, + (uint8_t *)cmd->value, cmd->len, + pdu, mtu); + + if (length == 0) { + error("gatt: Failed to encode indication"); + status = HAL_STATUS_FAILED; + } else { + g_attrib_send(conn->device->attrib, 0, pdu, length, NULL, NULL, + NULL); + status = HAL_STATUS_SUCCESS; + } + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_SEND_INDICATION, status); +} + +static bool match_trans_id(const void *data, const void *user_data) +{ + const struct pending_trans_data *transaction = data; + + return transaction->id == PTR_TO_UINT(user_data); +} + + +static bool find_conn_waiting_exec_write(const void *data, + const void *user_data) +{ + const struct app_connection *conn = data; + + return conn->wait_execute_write; +} + +static bool pending_execute_write(void) +{ + return queue_find(app_connections, find_conn_waiting_exec_write, NULL); +} + +static void handle_server_send_response(const void *buf, uint16_t len) +{ + const struct hal_cmd_gatt_server_send_response *cmd = buf; + struct pending_trans_data *transaction; + uint16_t handle = cmd->handle; + struct app_connection *conn; + uint8_t status; + + DBG(""); + + conn = find_connection_by_id(cmd->conn_id); + if (!conn) { + error("gatt: could not found connection"); + status = HAL_STATUS_FAILED; + goto reply; + } + + transaction = queue_remove_if(conn->transactions, match_trans_id, + UINT_TO_PTR(cmd->trans_id)); + if (!transaction) { + error("gatt: transaction ID = %d not found", cmd->trans_id); + status = HAL_STATUS_FAILED; + goto reply; + } + + if (transaction->opcode == ATT_OP_EXEC_WRITE_REQ) { + conn->wait_execute_write = false; + + /* Check for execute response from all server applications */ + if (pending_execute_write()) + goto done; + + /* Make sure handle is 0. We need it to find pending request */ + handle = 0; + + /* + * FIXME: Handle situation when not all server applications + * respond with a success. + */ + } + + fill_gatt_response_by_handle(handle, cmd->offset, cmd->status, cmd->len, + cmd->data, conn->device); + send_dev_complete_response(conn->device, transaction->opcode); + +done: + /* Clean request data */ + free(transaction); + + status = HAL_STATUS_SUCCESS; + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_SEND_RESPONSE, status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_GATT_CLIENT_REGISTER */ + { handle_client_register, false, + sizeof(struct hal_cmd_gatt_client_register) }, + /* HAL_OP_GATT_CLIENT_UNREGISTER */ + { handle_client_unregister, false, + sizeof(struct hal_cmd_gatt_client_unregister) }, + /* HAL_OP_GATT_CLIENT_SCAN */ + { handle_client_scan, false, + sizeof(struct hal_cmd_gatt_client_scan) }, + /* HAL_OP_GATT_CLIENT_CONNECT */ + { handle_client_connect, false, + sizeof(struct hal_cmd_gatt_client_connect) }, + /* HAL_OP_GATT_CLIENT_DISCONNECT */ + { handle_client_disconnect, false, + sizeof(struct hal_cmd_gatt_client_disconnect) }, + /* HAL_OP_GATT_CLIENT_LISTEN */ + { handle_client_listen, false, + sizeof(struct hal_cmd_gatt_client_listen) }, + /* HAL_OP_GATT_CLIENT_REFRESH */ + { handle_client_refresh, false, + sizeof(struct hal_cmd_gatt_client_refresh) }, + /* HAL_OP_GATT_CLIENT_SEARCH_SERVICE */ + { handle_client_search_service, true, + sizeof(struct hal_cmd_gatt_client_search_service) }, + /* HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE */ + { handle_client_get_included_service, true, + sizeof(struct hal_cmd_gatt_client_get_included_service) }, + /* HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC */ + { handle_client_get_characteristic, true, + sizeof(struct hal_cmd_gatt_client_get_characteristic) }, + /* HAL_OP_GATT_CLIENT_GET_DESCRIPTOR */ + { handle_client_get_descriptor, true, + sizeof(struct hal_cmd_gatt_client_get_descriptor) }, + /* HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC */ + { handle_client_read_characteristic, false, + sizeof(struct hal_cmd_gatt_client_read_characteristic) }, + /* HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC */ + { handle_client_write_characteristic, true, + sizeof(struct hal_cmd_gatt_client_write_characteristic) }, + /* HAL_OP_GATT_CLIENT_READ_DESCRIPTOR */ + { handle_client_read_descriptor, false, + sizeof(struct hal_cmd_gatt_client_read_descriptor) }, + /* HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR */ + { handle_client_write_descriptor, true, + sizeof(struct hal_cmd_gatt_client_write_descriptor) }, + /* HAL_OP_GATT_CLIENT_EXECUTE_WRITE */ + { handle_client_execute_write, false, + sizeof(struct hal_cmd_gatt_client_execute_write)}, + /* HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION */ + { handle_client_register_for_notification, false, + sizeof(struct hal_cmd_gatt_client_register_for_notification) }, + /* HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION */ + { handle_client_deregister_for_notification, false, + sizeof(struct hal_cmd_gatt_client_deregister_for_notification) }, + /* HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI */ + { handle_client_read_remote_rssi, false, + sizeof(struct hal_cmd_gatt_client_read_remote_rssi) }, + /* HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE */ + { handle_client_get_device_type, false, + sizeof(struct hal_cmd_gatt_client_get_device_type) }, + /* HAL_OP_GATT_CLIENT_SET_ADV_DATA */ + { handle_client_set_adv_data, true, + sizeof(struct hal_cmd_gatt_client_set_adv_data) }, + /* HAL_OP_GATT_CLIENT_TEST_COMMAND */ + { handle_client_test_command, false, + sizeof(struct hal_cmd_gatt_client_test_command) }, + /* HAL_OP_GATT_SERVER_REGISTER */ + { handle_server_register, false, + sizeof(struct hal_cmd_gatt_server_register) }, + /* HAL_OP_GATT_SERVER_UNREGISTER */ + { handle_server_unregister, false, + sizeof(struct hal_cmd_gatt_server_unregister) }, + /* HAL_OP_GATT_SERVER_CONNECT */ + { handle_server_connect, false, + sizeof(struct hal_cmd_gatt_server_connect) }, + /* HAL_OP_GATT_SERVER_DISCONNECT */ + { handle_server_disconnect, false, + sizeof(struct hal_cmd_gatt_server_disconnect) }, + /* HAL_OP_GATT_SERVER_ADD_SERVICE */ + { handle_server_add_service, false, + sizeof(struct hal_cmd_gatt_server_add_service) }, + /* HAL_OP_GATT_SERVER_ADD_INC_SERVICE */ + { handle_server_add_included_service, false, + sizeof(struct hal_cmd_gatt_server_add_inc_service) }, + /* HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC */ + { handle_server_add_characteristic, false, + sizeof(struct hal_cmd_gatt_server_add_characteristic) }, + /* HAL_OP_GATT_SERVER_ADD_DESCRIPTOR */ + { handle_server_add_descriptor, false, + sizeof(struct hal_cmd_gatt_server_add_descriptor) }, + /* HAL_OP_GATT_SERVER_START_SERVICE */ + { handle_server_start_service, false, + sizeof(struct hal_cmd_gatt_server_start_service) }, + /* HAL_OP_GATT_SERVER_STOP_SERVICE */ + { handle_server_stop_service, false, + sizeof(struct hal_cmd_gatt_server_stop_service) }, + /* HAL_OP_GATT_SERVER_DELETE_SERVICE */ + { handle_server_delete_service, false, + sizeof(struct hal_cmd_gatt_server_delete_service) }, + /* HAL_OP_GATT_SERVER_SEND_INDICATION */ + { handle_server_send_indication, true, + sizeof(struct hal_cmd_gatt_server_send_indication) }, + /* HAL_OP_GATT_SERVER_SEND_RESPONSE */ + { handle_server_send_response, true, + sizeof(struct hal_cmd_gatt_server_send_response) }, +}; + +static uint8_t read_by_group_type(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *device) +{ + uint16_t start, end; + int len; + bt_uuid_t uuid; + struct queue *q; + + len = dec_read_by_grp_req(cmd, cmd_len, &start, &end, &uuid); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (start > end || start == 0) + return ATT_ECODE_INVALID_HANDLE; + + q = queue_new(); + if (!q) + return ATT_ECODE_INSUFF_RESOURCES; + + gatt_db_read_by_group_type(gatt_db, start, end, uuid, q); + + if (queue_isempty(q)) { + queue_destroy(q, NULL); + return ATT_ECODE_ATTR_NOT_FOUND; + } + + while (queue_peek_head(q)) { + uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + struct pending_request *entry; + + entry = new0(struct pending_request, 1); + if (!entry) { + queue_destroy(q, destroy_pending_request); + return ATT_ECODE_UNLIKELY; + } + + entry->handle = handle; + entry->state = REQUEST_INIT; + + if (!queue_push_tail(device->pending_requests, entry)) { + queue_remove_all(device->pending_requests, NULL, NULL, + destroy_pending_request); + free(entry); + queue_destroy(q, NULL); + return ATT_ECODE_UNLIKELY; + } + } + + queue_destroy(q, NULL); + process_dev_pending_requests(device, cmd[0]); + + return 0; +} + +static uint8_t read_by_type(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *device) +{ + uint16_t start, end; + uint16_t len; + bt_uuid_t uuid; + struct queue *q; + + DBG(""); + + len = dec_read_by_type_req(cmd, cmd_len, &start, &end, &uuid); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (start > end || start == 0) + return ATT_ECODE_INVALID_HANDLE; + + q = queue_new(); + if (!q) + return ATT_ECODE_INSUFF_RESOURCES; + + gatt_db_read_by_type(gatt_db, start, end, uuid, q); + + if (queue_isempty(q)) { + queue_destroy(q, NULL); + return ATT_ECODE_ATTR_NOT_FOUND; + } + + while (queue_peek_head(q)) { + struct pending_request *data; + uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + + data = new0(struct pending_request, 1); + if (!data) { + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + data->state = REQUEST_INIT; + data->handle = handle; + queue_push_tail(device->pending_requests, data); + } + + queue_destroy(q, NULL); + + process_dev_pending_requests(device, ATT_OP_READ_BY_TYPE_REQ); + + return 0; +} + +static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint16_t handle; + uint16_t len; + uint16_t offset; + struct pending_request *data; + + DBG(""); + + switch (cmd[0]) { + case ATT_OP_READ_BLOB_REQ: + len = dec_read_blob_req(cmd, cmd_len, &handle, &offset); + if (!len) + return ATT_ECODE_INVALID_PDU; + break; + case ATT_OP_READ_REQ: + len = dec_read_req(cmd, cmd_len, &handle); + if (!len) + return ATT_ECODE_INVALID_PDU; + offset = 0; + break; + default: + error("gatt: Unexpected read type 0x%02x", cmd[0]); + return ATT_ECODE_REQ_NOT_SUPP; + } + + if (handle == 0) + return ATT_ECODE_INVALID_HANDLE; + + data = new0(struct pending_request, 1); + if (!data) + return ATT_ECODE_INSUFF_RESOURCES; + + data->offset = offset; + data->handle = handle; + data->state = REQUEST_INIT; + if (!queue_push_tail(dev->pending_requests, data)) { + free(data); + return ATT_ECODE_INSUFF_RESOURCES; + } + + process_dev_pending_requests(dev, cmd[0]); + + return 0; +} + +static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint16_t mtu, imtu, omtu; + size_t length; + GIOChannel *io; + GError *gerr = NULL; + uint16_t len; + uint8_t *rsp; + + DBG(""); + + len = dec_mtu_req(cmd, cmd_len, &mtu); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (mtu < ATT_DEFAULT_LE_MTU) + return ATT_ECODE_REQ_NOT_SUPP; + + io = g_attrib_get_channel(dev->attrib); + + bt_io_get(io, &gerr, + BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_OMTU, &omtu, + BT_IO_OPT_INVALID); + if (gerr) { + error("bt_io_get: %s", gerr->message); + g_error_free(gerr); + return ATT_ECODE_UNLIKELY; + } + + rsp = g_attrib_get_buffer(dev->attrib, &length); + + /* Respond with our IMTU */ + len = enc_mtu_resp(imtu, rsp, length); + if (!len) + return ATT_ECODE_UNLIKELY; + + g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL); + + /* Limit OMTU to received value */ + mtu = MIN(mtu, omtu); + g_attrib_set_mtu(dev->attrib, mtu); + + return 0; +} + +static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len, + uint8_t *rsp, size_t rsp_size, + uint16_t *length) +{ + struct queue *q; + struct att_data_list *adl; + int iterator = 0; + uint16_t start, end; + uint16_t len; + + DBG(""); + + len = dec_find_info_req(cmd, cmd_len, &start, &end); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (start > end || start == 0) + return ATT_ECODE_INVALID_HANDLE; + + q = queue_new(); + if (!q) + return ATT_ECODE_UNLIKELY; + + gatt_db_find_information(gatt_db, start, end, q); + + if (queue_isempty(q)) { + queue_destroy(q, NULL); + return ATT_ECODE_ATTR_NOT_FOUND; + } + + len = queue_length(q); + adl = att_data_list_alloc(len, 2 * sizeof(uint16_t)); + if (!adl) { + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + while (queue_peek_head(q)) { + uint8_t *value; + const bt_uuid_t *type; + uint16_t handle = PTR_TO_UINT(queue_pop_head(q)); + + type = gatt_db_get_attribute_type(gatt_db, handle); + if (!type) + break; + + value = adl->data[iterator++]; + + put_le16(handle, value); + memcpy(&value[2], &type->value.u16, bt_uuid_len(type)); + + } + + len = enc_find_info_resp(ATT_FIND_INFO_RESP_FMT_16BIT, adl, rsp, + rsp_size); + if (!len) + return ATT_ECODE_UNLIKELY; + + *length = len; + att_data_list_free(adl); + queue_destroy(q, free); + + return 0; +} + +static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *device) +{ + uint8_t search_value[cmd_len]; + size_t search_vlen; + uint16_t start, end; + uint16_t handle; + struct queue *q; + bt_uuid_t uuid; + uint16_t len; + + DBG(""); + + len = dec_find_by_type_req(cmd, cmd_len, &start, &end, &uuid, + search_value, &search_vlen); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (start > end || start == 0) + return ATT_ECODE_INVALID_HANDLE; + + q = queue_new(); + if (!q) + return ATT_ECODE_UNLIKELY; + + gatt_db_find_by_type(gatt_db, start, end, &uuid, q); + + handle = PTR_TO_UINT(queue_pop_head(q)); + while (handle) { + struct pending_request *data; + + data = new0(struct pending_request, 1); + if (!data) { + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + data->filter_value = malloc0(search_vlen); + if (!data->filter_value) { + destroy_pending_request(data); + queue_destroy(q, NULL); + return ATT_ECODE_INSUFF_RESOURCES; + } + + data->state = REQUEST_INIT; + data->handle = handle; + data->filter_vlen = search_vlen; + memcpy(data->filter_value, search_value, search_vlen); + + queue_push_tail(device->pending_requests, data); + + handle = PTR_TO_UINT(queue_pop_head(q)); + } + + queue_destroy(q, NULL); + + process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ); + + return 0; +} + +static void write_cmd_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint8_t value[cmd_len]; + uint32_t permissions; + uint16_t handle; + uint16_t len; + size_t vlen; + + len = dec_write_cmd(cmd, cmd_len, &handle, value, &vlen); + if (!len) + return; + + if (handle == 0) + return; + + if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions)) + return; + + if (check_device_permissions(dev, cmd[0], permissions)) + return; + + gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], &dev->bdaddr); +} + +static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint8_t value[ATT_DEFAULT_LE_MTU]; + uint8_t s[ATT_SIGNATURE_LEN]; + uint32_t permissions; + uint16_t handle; + uint16_t len; + size_t vlen; + uint8_t csrk[16]; + uint32_t sign_cnt; + + if (get_cid(dev) != ATT_CID) { + error("gatt: Remote tries write signed on BR/EDR bearer"); + connection_cleanup(dev); + return; + } + + if (get_sec_level(dev) != BT_SECURITY_LOW) { + error("gatt: Remote tries write signed on encrypted link"); + connection_cleanup(dev); + return; + } + + if (!bt_get_csrk(&dev->bdaddr, REMOTE_CSRK, csrk, &sign_cnt)) { + error("gatt: No valid csrk from remote device"); + return; + } + + len = dec_signed_write_cmd(cmd, cmd_len, &handle, value, &vlen, s); + + if (handle == 0) + return; + + if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions)) + return; + + if (check_device_permissions(dev, cmd[0], permissions)) + return; + + if (len) { + uint8_t t[ATT_SIGNATURE_LEN]; + uint32_t r_sign_cnt = get_le32(s); + + if (r_sign_cnt != sign_cnt) { + error("gatt: sign_cnt does not match (%d!=%d)", + sign_cnt, r_sign_cnt); + return; + } + + /* Generate signature and verify it */ + if (!bt_crypto_sign_att(crypto, csrk, cmd, + cmd_len - ATT_SIGNATURE_LEN, + sign_cnt, t)) { + error("gatt: Error when generating att signature"); + return; + } + + if (memcmp(t, s, ATT_SIGNATURE_LEN)) { + error("gatt: signature does not match"); + return; + } + /* Signature OK, proceed with write */ + bt_update_sign_counter(&dev->bdaddr, REMOTE_CSRK); + gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], + &dev->bdaddr); + } +} + +static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint8_t value[cmd_len]; + struct pending_request *data; + uint32_t permissions; + uint16_t handle; + uint16_t len; + uint8_t error; + size_t vlen; + + len = dec_write_req(cmd, cmd_len, &handle, value, &vlen); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (handle == 0) + return ATT_ECODE_INVALID_HANDLE; + + if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions)) + return ATT_ECODE_ATTR_NOT_FOUND; + + error = check_device_permissions(dev, cmd[0], permissions); + if (error) + return error; + + data = new0(struct pending_request, 1); + if (!data) + return ATT_ECODE_INSUFF_RESOURCES; + + data->handle = handle; + data->state = REQUEST_PENDING; + + if (!queue_push_tail(dev->pending_requests, data)) { + free(data); + return ATT_ECODE_INSUFF_RESOURCES; + } + + if (!gatt_db_write(gatt_db, handle, 0, value, vlen, cmd[0], + &dev->bdaddr)) { + queue_remove(dev->pending_requests, data); + free(data); + return ATT_ECODE_UNLIKELY; + } + + send_dev_complete_response(dev, cmd[0]); + + return 0; +} + +static uint8_t write_prep_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + uint8_t value[cmd_len]; + struct pending_request *data; + uint32_t permissions; + uint16_t handle; + uint16_t offset; + uint8_t error; + uint16_t len; + size_t vlen; + + len = dec_prep_write_req(cmd, cmd_len, &handle, &offset, + value, &vlen); + if (!len) + return ATT_ECODE_INVALID_PDU; + + if (handle == 0) + return ATT_ECODE_INVALID_HANDLE; + + if (!gatt_db_get_attribute_permissions(gatt_db, handle, &permissions)) + return ATT_ECODE_ATTR_NOT_FOUND; + + error = check_device_permissions(dev, cmd[0], permissions); + if (error) + return error; + + data = new0(struct pending_request, 1); + if (!data) + return ATT_ECODE_INSUFF_RESOURCES; + + data->handle = handle; + data->offset = offset; + data->state = REQUEST_PENDING; + + if (!queue_push_tail(dev->pending_requests, data)) { + free(data); + return ATT_ECODE_INSUFF_RESOURCES; + } + + if (!gatt_db_write(gatt_db, handle, offset, value, vlen, cmd[0], + &dev->bdaddr)) + return ATT_ECODE_UNLIKELY; + + return 0; +} + +static void send_server_write_execute_notify(void *data, void *user_data) +{ + struct hal_ev_gatt_server_request_exec_write *ev = user_data; + struct pending_trans_data *transaction; + struct app_connection *conn = data; + + if (!conn->wait_execute_write) + return; + + ev->conn_id = conn->id; + + transaction = conn_add_transact(conn, ATT_OP_EXEC_WRITE_REQ); + if (!transaction) { + conn->wait_execute_write = false; + return; + } + + ev->trans_id = transaction->id; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, + HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE, + sizeof(*ev), ev); +} + +static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len, + struct gatt_device *dev) +{ + struct hal_ev_gatt_server_request_exec_write ev; + uint8_t value; + struct pending_request *data; + + /* + * Check if there was any write prep before. + * TODO: Try to find better error code if possible + */ + if (!pending_execute_write()) + return ATT_ECODE_UNLIKELY; + + if (!dec_exec_write_req(cmd, cmd_len, &value)) + return ATT_ECODE_INVALID_PDU; + + memset(&ev, 0, sizeof(ev)); + bdaddr2android(&dev->bdaddr, &ev.bdaddr); + ev.exec_write = value; + + data = new0(struct pending_request, 1); + if (!data) + return ATT_ECODE_INSUFF_RESOURCES; + + data->state = REQUEST_PENDING; + if (!queue_push_tail(dev->pending_requests, data)) { + free(data); + return ATT_ECODE_INSUFF_RESOURCES; + } + + queue_foreach(app_connections, send_server_write_execute_notify, &ev); + + return 0; +} + +static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data) +{ + struct gatt_device *dev = user_data; + uint8_t status; + uint16_t resp_length = 0; + size_t length; + uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length); + + DBG("op 0x%02x", ipdu[0]); + + if (len > length) { + error("gatt: Too much data on ATT socket %p", opdu); + status = ATT_ECODE_INVALID_PDU; + goto done; + } + + switch (ipdu[0]) { + case ATT_OP_READ_BY_GROUP_REQ: + status = read_by_group_type(ipdu, len, dev); + break; + case ATT_OP_READ_BY_TYPE_REQ: + status = read_by_type(ipdu, len, dev); + break; + case ATT_OP_READ_REQ: + case ATT_OP_READ_BLOB_REQ: + status = read_request(ipdu, len, dev); + break; + case ATT_OP_MTU_REQ: + status = mtu_att_handle(ipdu, len, dev); + break; + case ATT_OP_FIND_INFO_REQ: + status = find_info_handle(ipdu, len, opdu, length, + &resp_length); + break; + case ATT_OP_WRITE_REQ: + status = write_req_request(ipdu, len, dev); + if (!status) + return; + break; + case ATT_OP_WRITE_CMD: + write_cmd_request(ipdu, len, dev); + /* No response on write cmd */ + return; + case ATT_OP_SIGNED_WRITE_CMD: + write_signed_cmd_request(ipdu, len, dev); + /* No response on write signed cmd */ + return; + case ATT_OP_PREP_WRITE_REQ: + status = write_prep_request(ipdu, len, dev); + if (!status) + return; + break; + case ATT_OP_FIND_BY_TYPE_REQ: + status = find_by_type_request(ipdu, len, dev); + break; + case ATT_OP_HANDLE_IND: + /* + * We have to send confirmation here. If some client is + * registered for this indication, event will be send in + * handle_notification + */ + resp_length = enc_confirmation(opdu, length); + status = 0; + break; + case ATT_OP_HANDLE_NOTIFY: + /* Client will handle this */ + return; + case ATT_OP_EXEC_WRITE_REQ: + status = write_execute_request(ipdu, len, dev); + if (!status) + return; + break; + case ATT_OP_HANDLE_CNF: + case ATT_OP_READ_MULTI_REQ: + default: + DBG("Unsupported request 0x%02x", ipdu[0]); + status = ATT_ECODE_REQ_NOT_SUPP; + goto done; + } + +done: + if (status) + resp_length = enc_error_resp(ipdu[0], 0x0000, status, opdu, + length); + + if (resp_length) + g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, + NULL); +} + +static void create_listen_connections(void *data, void *user_data) +{ + struct gatt_device *dev = user_data; + int32_t id = PTR_TO_INT(data); + struct gatt_app *app; + + app = find_app_by_id(id); + if (app) + create_connection(dev, app); +} + +static void connect_confirm(GIOChannel *io, void *user_data) +{ + struct gatt_device *dev; + uint8_t dst_type; + bdaddr_t dst; + GError *gerr = NULL; + + DBG(""); + + bt_io_get(io, &gerr, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST_TYPE, &dst_type, + BT_IO_OPT_INVALID); + if (gerr) { + error("gatt: bt_io_get: %s", gerr->message); + g_error_free(gerr); + return; + } + + /* TODO Handle collision */ + dev = find_device_by_addr(&dst); + if (!dev) { + dev = create_device(&dst); + if (!dev) { + error("gatt: Could not create device"); + goto drop; + } + } else { + if ((dev->state != DEVICE_DISCONNECTED) && + !(dev->state == DEVICE_CONNECT_INIT && + bt_kernel_conn_control())) { + char addr[18]; + + ba2str(&dst, addr); + info("gatt: Rejecting incoming connection from %s", + addr); + goto drop; + } + } + + dev->bdaddr_type = dst_type; + + if (!bt_io_accept(io, connect_cb, device_ref(dev), NULL, NULL)) { + error("gatt: failed to accept connection"); + device_unref(dev); + goto drop; + } + + queue_foreach(listen_apps, create_listen_connections, dev); + device_set_state(dev, DEVICE_CONNECT_READY); + + return; + +drop: + g_io_channel_shutdown(io, TRUE, NULL); +} + +struct gap_srvc_handles { + uint16_t srvc; + + /* Characteristics */ + uint16_t dev_name; + uint16_t appear; + uint16_t priv; +}; + +static struct gap_srvc_handles gap_srvc_data; + +#define APPEARANCE_GENERIC_PHONE 0x0040 +#define PERIPHERAL_PRIVACY_DISABLE 0x00 + +static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode, + bdaddr_t *bdaddr, void *user_data) +{ + struct pending_request *entry; + struct gatt_device *dev; + + DBG(""); + + dev = find_device_by_addr(bdaddr); + if (!dev) { + error("gatt: Could not find device ?!"); + return; + } + + entry = queue_find(dev->pending_requests, match_dev_request_by_handle, + UINT_TO_PTR(handle)); + if (!entry) + return; + + if (handle == gap_srvc_data.dev_name) { + const char *name = bt_get_adapter_name(); + + entry->value = malloc0(strlen(name)); + if (!entry->value) { + entry->error = ATT_ECODE_INSUFF_RESOURCES; + goto done; + } + + entry->length = strlen(name); + memcpy(entry->value, bt_get_adapter_name(), entry->length); + } else if (handle == gap_srvc_data.appear) { + entry->value = malloc0(2); + if (!entry->value) { + entry->error = ATT_ECODE_INSUFF_RESOURCES; + goto done; + } + + put_le16(APPEARANCE_GENERIC_PHONE, entry->value); + entry->length = sizeof(uint8_t) * 2; + } else if (handle == gap_srvc_data.priv) { + entry->value = malloc0(1); + if (!entry->value) { + entry->error = ATT_ECODE_INSUFF_RESOURCES; + goto done; + } + + *entry->value = PERIPHERAL_PRIVACY_DISABLE; + entry->length = sizeof(uint8_t); + } else { + entry->error = ATT_ECODE_ATTR_NOT_FOUND; + } + + entry->offset = offset; + +done: + entry->state = REQUEST_DONE; +} + +static void register_gap_service(void) +{ + uint16_t start, end; + bt_uuid_t uuid; + + /* GAP UUID */ + bt_uuid16_create(&uuid, 0x1800); + gap_srvc_data.srvc = gatt_db_add_service(gatt_db, &uuid, true, 7); + + /* Device name characteristic */ + bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); + gap_srvc_data.dev_name = + gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + gap_read_cb, NULL, + NULL); + + /* Appearance */ + bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE); + gap_srvc_data.appear = + gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + gap_read_cb, NULL, + NULL); + + /* Pripheral privacy flag */ + bt_uuid16_create(&uuid, GATT_CHARAC_PERIPHERAL_PRIV_FLAG); + gap_srvc_data.priv = + gatt_db_add_characteristic(gatt_db, gap_srvc_data.srvc, + &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + gap_read_cb, NULL, + NULL); + + gatt_db_service_set_active(gatt_db, gap_srvc_data.srvc , true); + + /* SDP */ + bt_uuid16_create(&uuid, 0x1800); + start = gap_srvc_data.srvc; + end = gatt_db_get_end_handle(gatt_db, gap_srvc_data.srvc); + gap_sdp_handle = add_sdp_record(&uuid, start, end, + "Generic Access Profile"); + if (!gap_sdp_handle) + error("gatt: Failed to register GAP SDP record"); +} + +/* TODO: Get those data from device possible via androig/bluetooth.c */ +static struct device_info { + const char *manufacturer_name; + const char *model_number; + const char *serial_number; + const char *firmware_rev; + const char *hardware_rev; + const char *software_rev; +} device_info = { + .manufacturer_name = "BlueZ for Android", + .model_number = "model no", + .serial_number = "serial no", + .firmware_rev = "firmware rev", + .hardware_rev = "hardware rev", + .software_rev = "software rev", +}; + +static void device_info_read_cb(uint16_t handle, uint16_t offset, + uint8_t att_opcode, bdaddr_t *bdaddr, + void *user_data) +{ + struct pending_request *entry; + struct gatt_device *dev; + char *buf = user_data; + + dev = find_device_by_addr(bdaddr); + if (!dev) { + error("gatt: Could not find device ?!"); + return; + } + + entry = queue_find(dev->pending_requests, match_dev_request_by_handle, + UINT_TO_PTR(handle)); + if (!entry) + return; + + entry->value = malloc0(strlen(buf)); + if (!entry->value) { + entry->error = ATT_ECODE_UNLIKELY; + goto done; + } + + entry->length = strlen(buf); + memcpy(entry->value, buf, entry->length); + entry->offset = offset; + +done: + entry->state = REQUEST_DONE; +} + +static void register_device_info_service(void) +{ + bt_uuid_t uuid; + uint16_t srvc_handle, end_handle; + + DBG(""); + + /* Device Information Service */ + bt_uuid16_create(&uuid, 0x180a); + srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 15); + + /* User data are not const hence (void *) cast is used */ + bt_uuid16_create(&uuid, GATT_CHARAC_MODEL_NUMBER_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.model_number); + + bt_uuid16_create(&uuid, GATT_CHARAC_SERIAL_NUMBER_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.serial_number); + + bt_uuid16_create(&uuid, GATT_CHARAC_FIRMWARE_REVISION_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.firmware_rev); + + bt_uuid16_create(&uuid, GATT_CHARAC_HARDWARE_REVISION_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.hardware_rev); + + bt_uuid16_create(&uuid, GATT_CHARAC_SOFTWARE_REVISION_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.software_rev); + + bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING); + gatt_db_add_characteristic(gatt_db, srvc_handle, &uuid, GATT_PERM_READ, + GATT_CHR_PROP_READ, + device_info_read_cb, NULL, + (void *) device_info.manufacturer_name); + + gatt_db_service_set_active(gatt_db, srvc_handle, true); + + /* SDP */ + bt_uuid16_create(&uuid, 0x180a); + end_handle = gatt_db_get_end_handle(gatt_db, srvc_handle); + dis_sdp_handle = add_sdp_record(&uuid, srvc_handle, end_handle, + "Device Information Service"); + if (!dis_sdp_handle) + error("gatt: Failed to register DIS SDP record"); +} + +static void gatt_srvc_change_write_cb(uint16_t handle, uint16_t offset, + const uint8_t *val, size_t len, + uint8_t att_opcode, + bdaddr_t *bdaddr, + void *user_data) +{ + struct pending_request *entry; + struct gatt_device *dev; + + dev = find_device_by_addr(bdaddr); + if (!dev) { + error("gatt: Could not find device ?!"); + return; + } + + entry = queue_find(dev->pending_requests, match_dev_request_by_handle, + UINT_TO_PTR(handle)); + if (!entry) + return; + + entry->state = REQUEST_DONE; + + if (!bt_device_is_bonded(bdaddr)) { + entry->error = ATT_ECODE_AUTHORIZATION; + return; + } + + /* Set services changed indication value */ + bt_store_gatt_ccc(bdaddr, *val); +} + +static void gatt_srvc_change_read_cb(uint16_t handle, uint16_t offset, + uint8_t att_opcode, bdaddr_t *bdaddr, + void *user_data) +{ + struct pending_request *entry; + struct gatt_device *dev; + uint16_t ccc = 0; + + dev = find_device_by_addr(bdaddr); + if (!dev) { + error("gatt: Could not find device ?!"); + return; + } + + entry = queue_find(dev->pending_requests, match_dev_request_by_handle, + UINT_TO_PTR(handle)); + if (!entry) + return; + + ccc = bt_get_gatt_ccc(&dev->bdaddr); + entry->state = REQUEST_DONE; + + entry->value = new0(uint8_t, 2); + if (!entry->value) { + entry->error = ATT_ECODE_INSUFF_RESOURCES; + + return; + } + + entry->length = sizeof(uint16_t); + memcpy(entry->value, &ccc, sizeof(ccc)); +} + +static void register_gatt_service(void) +{ + uint16_t srvc_handle, end_handle; + bt_uuid_t uuid; + + DBG(""); + + bt_uuid16_create(&uuid, 0x1801); + srvc_handle = gatt_db_add_service(gatt_db, &uuid, true, 4); + + bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); + service_changed_handle = gatt_db_add_characteristic(gatt_db, + srvc_handle, &uuid, GATT_PERM_NONE, + GATT_CHR_PROP_INDICATE, NULL, NULL, + NULL); + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + gatt_db_add_char_descriptor(gatt_db, srvc_handle, &uuid, + GATT_PERM_READ | GATT_PERM_WRITE, + gatt_srvc_change_read_cb, + gatt_srvc_change_write_cb, NULL); + + gatt_db_service_set_active(gatt_db, srvc_handle, true); + + /* SDP */ + bt_uuid16_create(&uuid, 0x1801); + end_handle = gatt_db_get_end_handle(gatt_db, srvc_handle); + gatt_sdp_handle = add_sdp_record(&uuid, srvc_handle, end_handle, + "Generic Attribute Profile"); + + if (!gatt_sdp_handle) + error("gatt: Failed to register GATT SDP record"); +} + +static bool start_listening(void) +{ + /* BR/EDR socket */ + bredr_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL, + BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR, + BT_IO_OPT_PSM, ATT_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + + /* LE socket */ + le_io = bt_io_listen(NULL, connect_confirm, NULL, NULL, NULL, + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, + BT_IO_OPT_CID, ATT_CID, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + + if (!le_io && !bredr_io) { + error("gatt: Failed to start listening IO"); + return false; + } + + return true; +} + +static void gatt_unpaired_cb(const bdaddr_t *addr, uint8_t type) +{ + struct gatt_device *dev; + char address[18]; + + dev = find_device_by_addr(addr); + if (!dev) + return; + + if (dev->bdaddr_type != type) + return; + + ba2str(addr, address); + DBG("Unpaired device %s", address); + + queue_remove(gatt_devices, dev); + destroy_device(dev); +} + +bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr) +{ + DBG(""); + + if (!bt_unpaired_register(gatt_unpaired_cb)) { + error("gatt: Could not register unpaired callback"); + return false; + } + + if (!start_listening()) + return false; + + crypto = bt_crypto_new(); + if (!crypto) { + error("gatt: Failed to setup crypto"); + goto failed; + } + + gatt_devices = queue_new(); + gatt_apps = queue_new(); + app_connections = queue_new(); + listen_apps = queue_new(); + services_sdp = queue_new(); + gatt_db = gatt_db_new(); + + if (!gatt_devices || !gatt_apps || !listen_apps || !app_connections || + !services_sdp || !gatt_db) { + error("gatt: Failed to allocate memory for queues"); + goto failed; + } + + if (!bt_le_register(le_device_found_handler)) { + error("gatt: bt_le_register failed"); + goto failed; + } + + bacpy(&adapter_addr, addr); + + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + register_gap_service(); + register_device_info_service(); + register_gatt_service(); + + info("gatt: LE: %s BR/EDR: %s", le_io ? "enabled" : "disabled", + bredr_io ? "enabled" : "disabled"); + + return true; + +failed: + queue_destroy(gatt_apps, NULL); + gatt_apps = NULL; + + queue_destroy(gatt_devices, NULL); + gatt_devices = NULL; + + queue_destroy(app_connections, NULL); + app_connections = NULL; + + queue_destroy(listen_apps, NULL); + listen_apps = NULL; + + queue_destroy(services_sdp, NULL); + services_sdp = NULL; + + gatt_db_destroy(gatt_db); + gatt_db = NULL; + + bt_crypto_unref(crypto); + crypto = NULL; + + if (le_io) { + g_io_channel_unref(le_io); + le_io = NULL; + } + + if (bredr_io) { + g_io_channel_unref(bredr_io); + bredr_io = NULL; + } + + return false; +} + +void bt_gatt_unregister(void) +{ + DBG(""); + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT); + hal_ipc = NULL; + + queue_destroy(gatt_apps, destroy_gatt_app); + gatt_apps = NULL; + + queue_destroy(app_connections, destroy_connection); + app_connections = NULL; + + queue_destroy(gatt_devices, destroy_device); + gatt_devices = NULL; + + queue_destroy(services_sdp, free_service_sdp_record); + services_sdp = NULL; + + queue_destroy(listen_apps, NULL); + listen_apps = NULL; + + gatt_db_destroy(gatt_db); + gatt_db = NULL; + + g_io_channel_unref(le_io); + le_io = NULL; + + g_io_channel_unref(bredr_io); + bredr_io = NULL; + + if (gap_sdp_handle) { + bt_adapter_remove_record(gap_sdp_handle); + gap_sdp_handle = 0; + } + + if (gatt_sdp_handle) { + bt_adapter_remove_record(gatt_sdp_handle); + gatt_sdp_handle = 0; + } + + if (dis_sdp_handle) { + bt_adapter_remove_record(dis_sdp_handle); + dis_sdp_handle = 0; + } + + bt_crypto_unref(crypto); + crypto = NULL; + + bt_le_unregister(); + bt_unpaired_unregister(gatt_unpaired_cb); +} + +unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type, + gatt_conn_cb_t func) +{ + struct gatt_app *app; + bt_uuid_t uuid128; + + bt_string_to_uuid(&uuid128, uuid); + app = register_app((void *) &uuid128.value.u128, type); + if (!app) + return 0; + + app->func = func; + + return app->id; +} + +bool bt_gatt_unregister_app(unsigned int id) +{ + uint8_t status; + + status = unregister_app(id); + + return status != HAL_STATUS_FAILED; +} + +bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr) +{ + uint8_t status; + + status = handle_connect(id, addr); + + return status != HAL_STATUS_FAILED; +} + +bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr) +{ + struct app_connection match; + struct app_connection *conn; + struct gatt_device *device; + struct gatt_app *app; + + app = find_app_by_id(id); + if (!app) + return false; + + device = find_device_by_addr(addr); + if (!device) + return false; + + match.device = device; + match.app = app; + + conn = queue_find(app_connections, match_connection_by_device_and_app, + &match); + if (!conn) + return false; + + trigger_disconnection(conn); + + return true; +} + +bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr) +{ + struct gatt_device *dev; + struct gatt_app *app; + + DBG(""); + + app = find_app_by_id(id); + if (!app) { + error("gatt: App ID=%d not found", id); + return false; + } + + dev = find_device_by_addr(addr); + if (!dev) { + error("gatt: Device not found"); + return false; + } + + /* Take reference of device for auto connect purpose */ + if (queue_isempty(dev->autoconnect_apps)) + device_ref(dev); + + if (!queue_find(dev->autoconnect_apps, match_by_value, + INT_TO_PTR(id))) + return queue_push_head(dev->autoconnect_apps, INT_TO_PTR(id)); + + return true; +} + +void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr) +{ + struct gatt_device *dev; + + DBG(""); + + dev = find_device_by_addr(addr); + if (!dev) { + error("gatt: Device not found"); + return; + } + + queue_remove(dev->autoconnect_apps, INT_TO_PTR(id)); + + if (queue_isempty(dev->autoconnect_apps)) + remove_autoconnect_device(dev); + + device_unref(dev); +} diff -Nru bluez-4.101/android/gatt.h bluez-5.23/android/gatt.h --- bluez-4.101/android/gatt.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/gatt.h 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr); +void bt_gatt_unregister(void); + + +typedef enum { + GATT_CLIENT, + GATT_SERVER, +} gatt_type_t; + +typedef void (*gatt_conn_cb_t)(const bdaddr_t *addr, int err, void *attrib); + +unsigned int bt_gatt_register_app(const char *uuid, gatt_type_t type, + gatt_conn_cb_t func); +bool bt_gatt_unregister_app(unsigned int id); + +bool bt_gatt_connect_app(unsigned int id, const bdaddr_t *addr); +bool bt_gatt_disconnect_app(unsigned int id, const bdaddr_t *addr); +bool bt_gatt_set_security(const bdaddr_t *bdaddr, int sec_level); +bool bt_gatt_add_autoconnect(unsigned int id, const bdaddr_t *addr); +void bt_gatt_remove_autoconnect(unsigned int id, const bdaddr_t *addr); diff -Nru bluez-4.101/android/hal-a2dp.c bluez-5.23/android/hal-a2dp.c --- bluez-4.101/android/hal-a2dp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-a2dp.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "hal-ipc.h" + +static const btav_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void handle_conn_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_a2dp_conn_state *ev = buf; + + if (cbs->connection_state_cb) + cbs->connection_state_cb(ev->state, + (bt_bdaddr_t *) (ev->bdaddr)); +} + +static void handle_audio_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_a2dp_audio_state *ev = buf; + + if (cbs->audio_state_cb) + cbs->audio_state_cb(ev->state, (bt_bdaddr_t *)(ev->bdaddr)); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_A2DP_CONN_STATE */ + { handle_conn_state, false, sizeof(struct hal_ev_a2dp_conn_state) }, + /* HAL_EV_A2DP_AUDIO_STATE */ + { handle_audio_state, false, sizeof(struct hal_ev_a2dp_audio_state) }, +}; + +static bt_status_t a2dp_connect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_a2dp_connect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_CONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t disconnect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_a2dp_disconnect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_A2DP, HAL_OP_A2DP_DISCONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t init(btav_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + cbs = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_A2DP, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_A2DP; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_A2DP); + } + + return ret; +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_A2DP; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_A2DP); +} + +static btav_interface_t iface = { + .size = sizeof(iface), + .init = init, + .connect = a2dp_connect, + .disconnect = disconnect, + .cleanup = cleanup +}; + +btav_interface_t *bt_get_a2dp_interface(void) +{ + return &iface; +} diff -Nru bluez-4.101/android/hal-audio-aptx.c bluez-5.23/android/hal-audio-aptx.c --- bluez-4.101/android/hal-audio-aptx.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-audio-aptx.c 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2014 Tieto Poland + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include + +#include "audio-msg.h" +#include "hal-audio.h" +#include "hal-log.h" +#include "src/shared/util.h" +#include "profiles/audio/a2dp-codecs.h" + +#define APTX_SO_NAME "libbt-aptx.so" + +struct aptx_data { + a2dp_aptx_t aptx; + + void *enc; +}; + +static const a2dp_aptx_t aptx_presets[] = { + { + .info = { + .vendor_id = APTX_VENDOR_ID, + .codec_id = APTX_CODEC_ID, + }, + .frequency = APTX_SAMPLING_FREQ_44100 | + APTX_SAMPLING_FREQ_48000, + .channel_mode = APTX_CHANNEL_MODE_STEREO, + }, + { + .info = { + .vendor_id = APTX_VENDOR_ID, + .codec_id = APTX_CODEC_ID, + }, + .frequency = APTX_SAMPLING_FREQ_48000, + .channel_mode = APTX_CHANNEL_MODE_STEREO, + }, + { + .info = { + .vendor_id = APTX_VENDOR_ID, + .codec_id = APTX_CODEC_ID, + }, + .frequency = APTX_SAMPLING_FREQ_44100, + .channel_mode = APTX_CHANNEL_MODE_STEREO, + }, +}; + +static void *aptx_handle; +static int aptx_btencsize; +static int (*aptx_init)(void *, short); +static int (*aptx_encode)(void *, void *, void *, void *); + +static bool aptx_load(void) +{ + const char * (*aptx_version)(void); + const char * (*aptx_build)(void); + int (*aptx_sizeofenc)(void); + + aptx_handle = dlopen(APTX_SO_NAME, RTLD_LAZY); + if (!aptx_handle) { + error("APTX: failed to open library %s (%s)", APTX_SO_NAME, + dlerror()); + return false; + } + + aptx_version = dlsym(aptx_handle, "aptxbtenc_version"); + aptx_build = dlsym(aptx_handle, "aptxbtenc_build"); + + if (aptx_version && aptx_build) + info("APTX: using library version %s build %s", aptx_version(), + aptx_build()); + else + warn("APTX: cannot retrieve library version"); + + aptx_sizeofenc = dlsym(aptx_handle, "SizeofAptxbtenc"); + aptx_init = dlsym(aptx_handle, "aptxbtenc_init"); + aptx_encode = dlsym(aptx_handle, "aptxbtenc_encodestereo"); + if (!aptx_sizeofenc || !aptx_init || !aptx_encode) { + error("APTX: failed initialize library"); + dlclose(aptx_handle); + aptx_handle = NULL; + return false; + } + aptx_btencsize = aptx_sizeofenc(); + + info("APTX: codec library initialized (size=%d)", aptx_btencsize); + + return true; +} + +static void aptx_unload(void) +{ + if (!aptx_handle) + return; + + dlclose(aptx_handle); + aptx_handle = NULL; +} + +static int aptx_get_presets(struct audio_preset *preset, size_t *len) +{ + int i; + int count; + size_t new_len = 0; + uint8_t *ptr = (uint8_t *) preset; + size_t preset_size = sizeof(*preset) + sizeof(a2dp_aptx_t); + + DBG(""); + + count = sizeof(aptx_presets) / sizeof(aptx_presets[0]); + + for (i = 0; i < count; i++) { + preset = (struct audio_preset *) ptr; + + if (new_len + preset_size > *len) + break; + + preset->len = sizeof(a2dp_aptx_t); + memcpy(preset->data, &aptx_presets[i], preset->len); + + new_len += preset_size; + ptr += preset_size; + } + + *len = new_len; + + return i; +} + +static bool aptx_codec_init(struct audio_preset *preset, uint16_t payload_len, + void **codec_data) +{ + struct aptx_data *aptx_data; + + DBG(""); + + if (preset->len != sizeof(a2dp_aptx_t)) { + error("APTX: preset size mismatch"); + return false; + } + + aptx_data = new0(struct aptx_data, 1); + if (!aptx_data) + return false; + + memcpy(&aptx_data->aptx, preset->data, preset->len); + + aptx_data->enc = calloc(1, aptx_btencsize); + if (!aptx_data->enc) { + error("APTX: failed to create encoder"); + free(aptx_data); + return false; + } + + /* 1 = big-endian, this is what devices are using */ + aptx_init(aptx_data->enc, 1); + + *codec_data = aptx_data; + + return true; +} + +static bool aptx_cleanup(void *codec_data) +{ + struct aptx_data *aptx_data = (struct aptx_data *) codec_data; + + free(aptx_data->enc); + free(codec_data); + + return true; +} + +static bool aptx_get_config(void *codec_data, struct audio_input_config *config) +{ + struct aptx_data *aptx_data = (struct aptx_data *) codec_data; + + config->rate = aptx_data->aptx.frequency & APTX_SAMPLING_FREQ_48000 ? + 48000 : 44100; + config->channels = AUDIO_CHANNEL_OUT_STEREO; + config->format = AUDIO_FORMAT_PCM_16_BIT; + + return true; +} + +static size_t aptx_get_buffer_size(void *codec_data) +{ + /* TODO: return actual value */ + return 0; +} + +static size_t aptx_get_mediapacket_duration(void *codec_data) +{ + /* TODO: return actual value */ + return 0; +} + +static ssize_t aptx_encode_mediapacket(void *codec_data, const uint8_t *buffer, + size_t len, struct media_packet *mp, + size_t mp_data_len, size_t *written) +{ + struct aptx_data *aptx_data = (struct aptx_data *) codec_data; + const int16_t *ptr = (const void *) buffer; + size_t bytes_in = 0; + size_t bytes_out = 0; + + while ((len - bytes_in) >= 16 && (mp_data_len - bytes_out) >= 4) { + int pcm_l[4], pcm_r[4]; + int i; + + for (i = 0; i < 4; i++) { + pcm_l[i] = ptr[0]; + pcm_r[i] = ptr[1]; + ptr += 2; + } + + aptx_encode(aptx_data->enc, pcm_l, pcm_r, &mp->data[bytes_out]); + + bytes_in += 16; + bytes_out += 4; + } + + *written = bytes_out; + + return bytes_in; +} + +static bool aptx_update_qos(void *codec_data, uint8_t op) +{ + /* + * aptX has constant bitrate of 352kbps (with constant 4:1 compression + * ratio) thus QoS is not possible here. + */ + + return false; +} + +static const struct audio_codec codec = { + .type = A2DP_CODEC_VENDOR, + .use_rtp = false, + + .load = aptx_load, + .unload = aptx_unload, + + .get_presets = aptx_get_presets, + + .init = aptx_codec_init, + .cleanup = aptx_cleanup, + .get_config = aptx_get_config, + .get_buffer_size = aptx_get_buffer_size, + .get_mediapacket_duration = aptx_get_mediapacket_duration, + .encode_mediapacket = aptx_encode_mediapacket, + .update_qos = aptx_update_qos, +}; + +const struct audio_codec *codec_aptx(void) +{ + return &codec; +} diff -Nru bluez-4.101/android/hal-audio.c bluez-5.23/android/hal-audio.c --- bluez-4.101/android/hal-audio.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-audio.c 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,1527 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "audio-msg.h" +#include "ipc-common.h" +#include "hal-log.h" +#include "hal-msg.h" +#include "hal-audio.h" +#include "src/shared/util.h" +#include "src/shared/queue.h" + +#define FIXED_A2DP_PLAYBACK_LATENCY_MS 25 + +#define FIXED_BUFFER_SIZE (20 * 512) + +#define MAX_DELAY 100000 /* 100ms */ + +static const uint8_t a2dp_src_uuid[] = { + 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }; + +static int listen_sk = -1; +static int audio_sk = -1; + +static pthread_t ipc_th = 0; +static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void timespec_add(struct timespec *base, uint64_t time_us, + struct timespec *res) +{ + res->tv_sec = base->tv_sec + time_us / 1000000; + res->tv_nsec = base->tv_nsec + (time_us % 1000000) * 1000; + + if (res->tv_nsec >= 1000000000) { + res->tv_sec++; + res->tv_nsec -= 1000000000; + } +} + +static void timespec_diff(struct timespec *a, struct timespec *b, + struct timespec *res) +{ + res->tv_sec = a->tv_sec - b->tv_sec; + res->tv_nsec = a->tv_nsec - b->tv_nsec; + + if (res->tv_nsec < 0) { + res->tv_sec--; + res->tv_nsec += 1000000000; /* 1sec */ + } +} + +static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b) +{ + struct timespec res; + + timespec_diff(a, b, &res); + + return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll; +} + +#if defined(ANDROID) +/* + * Bionic does not have clock_nanosleep() prototype in time.h even though + * it provides its implementation. + */ +extern int clock_nanosleep(clockid_t clock_id, int flags, + const struct timespec *request, + struct timespec *remain); +#endif + +static const audio_codec_get_t audio_codecs[] = { + codec_aptx, + codec_sbc, +}; + +#define NUM_CODECS (sizeof(audio_codecs) / sizeof(audio_codecs[0])) + +#define MAX_AUDIO_ENDPOINTS NUM_CODECS + +static struct queue *loaded_codecs; + +struct audio_endpoint { + uint8_t id; + const struct audio_codec *codec; + void *codec_data; + int fd; + + struct media_packet *mp; + size_t mp_data_len; + + uint16_t seq; + uint32_t samples; + struct timespec start; + + bool resync; +}; + +static struct audio_endpoint audio_endpoints[MAX_AUDIO_ENDPOINTS]; + +enum a2dp_state_t { + AUDIO_A2DP_STATE_NONE, + AUDIO_A2DP_STATE_STANDBY, + AUDIO_A2DP_STATE_SUSPENDED, + AUDIO_A2DP_STATE_STARTED +}; + +struct a2dp_stream_out { + struct audio_stream_out stream; + + struct audio_endpoint *ep; + enum a2dp_state_t audio_state; + struct audio_input_config cfg; + + uint8_t *downmix_buf; +}; + +struct a2dp_audio_dev { + struct audio_hw_device dev; + struct a2dp_stream_out *out; +}; + +static int audio_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, + void *param, size_t *rsp_len, void *rsp, int *fd) +{ + ssize_t ret; + struct msghdr msg; + struct iovec iv[2]; + struct ipc_hdr cmd; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct ipc_status s; + size_t s_len = sizeof(s); + + pthread_mutex_lock(&sk_mutex); + + if (audio_sk < 0) { + error("audio: Invalid cmd socket passed to audio_ipc_cmd"); + goto failed; + } + + if (!rsp || !rsp_len) { + memset(&s, 0, s_len); + rsp_len = &s_len; + rsp = &s; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.service_id = service_id; + cmd.opcode = opcode; + cmd.len = len; + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = param; + iv[1].iov_len = len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + ret = sendmsg(audio_sk, &msg, 0); + if (ret < 0) { + error("audio: Sending command failed:%s", strerror(errno)); + goto failed; + } + + /* socket was shutdown */ + if (ret == 0) { + error("audio: Command socket closed"); + goto failed; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = rsp; + iv[1].iov_len = *rsp_len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + if (fd) { + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + } + + ret = recvmsg(audio_sk, &msg, 0); + if (ret < 0) { + error("audio: Receiving command response failed:%s", + strerror(errno)); + goto failed; + } + + if (ret < (ssize_t) sizeof(cmd)) { + error("audio: Too small response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.service_id != service_id) { + error("audio: Invalid service id (%u vs %u)", cmd.service_id, + service_id); + goto failed; + } + + if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { + error("audio: Malformed response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.opcode != opcode && cmd.opcode != AUDIO_OP_STATUS) { + error("audio: Invalid opcode received (%u vs %u)", + cmd.opcode, opcode); + goto failed; + } + + if (cmd.opcode == AUDIO_OP_STATUS) { + struct ipc_status *s = rsp; + + if (sizeof(*s) != cmd.len) { + error("audio: Invalid status length"); + goto failed; + } + + if (s->code == AUDIO_STATUS_SUCCESS) { + error("audio: Invalid success status response"); + goto failed; + } + + pthread_mutex_unlock(&sk_mutex); + + return s->code; + } + + pthread_mutex_unlock(&sk_mutex); + + /* Receive auxiliary data in msg */ + if (fd) { + struct cmsghdr *cmsg; + + *fd = -1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); + break; + } + } + + if (*fd < 0) + goto failed; + } + + *rsp_len = cmd.len; + + return AUDIO_STATUS_SUCCESS; + +failed: + /* Some serious issue happen on IPC - recover */ + shutdown(audio_sk, SHUT_RDWR); + pthread_mutex_unlock(&sk_mutex); + + return AUDIO_STATUS_FAILED; +} + +static int ipc_open_cmd(const struct audio_codec *codec) +{ + uint8_t buf[BLUEZ_AUDIO_MTU]; + struct audio_cmd_open *cmd = (struct audio_cmd_open *) buf; + struct audio_rsp_open rsp; + size_t cmd_len = sizeof(buf) - sizeof(*cmd); + size_t rsp_len = sizeof(rsp); + int result; + + DBG(""); + + memcpy(cmd->uuid, a2dp_src_uuid, sizeof(a2dp_src_uuid)); + + cmd->codec = codec->type; + cmd->presets = codec->get_presets(cmd->preset, &cmd_len); + + cmd_len += sizeof(*cmd); + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN, cmd_len, cmd, + &rsp_len, &rsp, NULL); + + if (result != AUDIO_STATUS_SUCCESS) + return 0; + + return rsp.id; +} + +static int ipc_close_cmd(uint8_t endpoint_id) +{ + struct audio_cmd_close cmd; + int result; + + DBG(""); + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + return result; +} + +static int ipc_open_stream_cmd(uint8_t *endpoint_id, uint16_t *mtu, int *fd, + struct audio_preset **caps) +{ + char buf[BLUEZ_AUDIO_MTU]; + struct audio_cmd_open_stream cmd; + struct audio_rsp_open_stream *rsp = + (struct audio_rsp_open_stream *) &buf; + size_t rsp_len = sizeof(buf); + int result; + + DBG(""); + + if (!caps) + return AUDIO_STATUS_FAILED; + + cmd.id = *endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_OPEN_STREAM, + sizeof(cmd), &cmd, &rsp_len, rsp, fd); + if (result == AUDIO_STATUS_SUCCESS) { + size_t buf_len = sizeof(struct audio_preset) + + rsp->preset[0].len; + *endpoint_id = rsp->id; + *mtu = rsp->mtu; + *caps = malloc(buf_len); + memcpy(*caps, &rsp->preset, buf_len); + } else { + *caps = NULL; + } + + return result; +} + +static int ipc_close_stream_cmd(uint8_t endpoint_id) +{ + struct audio_cmd_close_stream cmd; + int result; + + DBG(""); + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_CLOSE_STREAM, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + return result; +} + +static int ipc_resume_stream_cmd(uint8_t endpoint_id) +{ + struct audio_cmd_resume_stream cmd; + int result; + + DBG(""); + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_RESUME_STREAM, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + return result; +} + +static int ipc_suspend_stream_cmd(uint8_t endpoint_id) +{ + struct audio_cmd_suspend_stream cmd; + int result; + + DBG(""); + + cmd.id = endpoint_id; + + result = audio_ipc_cmd(AUDIO_SERVICE_ID, AUDIO_OP_SUSPEND_STREAM, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + return result; +} + +struct register_state { + struct audio_endpoint *ep; + bool error; +}; + +static void register_endpoint(void *data, void *user_data) +{ + struct audio_codec *codec = data; + struct register_state *state = user_data; + struct audio_endpoint *ep = state->ep; + + /* don't even try to register more endpoints if one failed */ + if (state->error) + return; + + ep->id = ipc_open_cmd(codec); + + if (!ep->id) { + state->error = true; + error("Failed to register endpoint"); + return; + } + + ep->codec = codec; + ep->codec_data = NULL; + ep->fd = -1; + + state->ep++; +} + +static int register_endpoints(void) +{ + struct register_state state; + + state.ep = &audio_endpoints[0]; + state.error = false; + + queue_foreach(loaded_codecs, register_endpoint, &state); + + return state.error ? AUDIO_STATUS_FAILED : AUDIO_STATUS_SUCCESS; +} + +static void unregister_endpoints(void) +{ + size_t i; + + for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) { + struct audio_endpoint *ep = &audio_endpoints[i]; + + if (ep->id) { + ipc_close_cmd(ep->id); + memset(ep, 0, sizeof(*ep)); + } + } +} + +static bool open_endpoint(struct audio_endpoint **epp, + struct audio_input_config *cfg) +{ + struct audio_preset *preset; + struct audio_endpoint *ep = *epp; + const struct audio_codec *codec; + uint16_t mtu; + uint16_t payload_len; + int fd; + size_t i; + uint8_t ep_id = 0; + + if (ep) + ep_id = ep->id; + + if (ipc_open_stream_cmd(&ep_id, &mtu, &fd, &preset) != + AUDIO_STATUS_SUCCESS) + return false; + + DBG("ep_id=%d mtu=%u", ep_id, mtu); + + for (i = 0; i < MAX_AUDIO_ENDPOINTS; i++) + if (audio_endpoints[i].id == ep_id) { + ep = &audio_endpoints[i]; + break; + } + + if (!ep) { + error("Cound not find opened endpoint"); + goto failed; + } + + *epp = ep; + + payload_len = mtu; + if (ep->codec->use_rtp) + payload_len -= sizeof(struct rtp_header); + + ep->fd = fd; + + codec = ep->codec; + codec->init(preset, payload_len, &ep->codec_data); + codec->get_config(ep->codec_data, cfg); + + ep->mp = calloc(mtu, 1); + if (!ep->mp) + goto failed; + + if (ep->codec->use_rtp) { + struct media_packet_rtp *mp_rtp = + (struct media_packet_rtp *) ep->mp; + mp_rtp->hdr.v = 2; + mp_rtp->hdr.pt = 0x60; + mp_rtp->hdr.ssrc = htonl(1); + } + + ep->mp_data_len = payload_len; + + free(preset); + + return true; + +failed: + close(fd); + free(preset); + + return false; +} + +static void close_endpoint(struct audio_endpoint *ep) +{ + ipc_close_stream_cmd(ep->id); + if (ep->fd >= 0) { + close(ep->fd); + ep->fd = -1; + } + + free(ep->mp); + + ep->codec->cleanup(ep->codec_data); + ep->codec_data = NULL; +} + +static bool resume_endpoint(struct audio_endpoint *ep) +{ + if (ipc_resume_stream_cmd(ep->id) != AUDIO_STATUS_SUCCESS) + return false; + + ep->samples = 0; + ep->resync = false; + + ep->codec->update_qos(ep->codec_data, QOS_POLICY_DEFAULT); + + return true; +} + +static void downmix_to_mono(struct a2dp_stream_out *out, const uint8_t *buffer, + size_t bytes) +{ + const int16_t *input = (const void *) buffer; + int16_t *output = (void *) out->downmix_buf; + size_t i, frames; + + /* PCM 16bit stereo */ + frames = bytes / (2 * sizeof(int16_t)); + + for (i = 0; i < frames; i++) { + int16_t l = le16_to_cpu(get_unaligned(&input[i * 2])); + int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1])); + + put_unaligned(cpu_to_le16((l + r) / 2), &output[i]); + } +} + +static bool wait_for_endpoint(struct audio_endpoint *ep, bool *writable) +{ + int ret; + + while (true) { + struct pollfd pollfd; + + pollfd.fd = ep->fd; + pollfd.events = POLLOUT; + pollfd.revents = 0; + + ret = poll(&pollfd, 1, 500); + + if (ret >= 0) { + *writable = !!(pollfd.revents & POLLOUT); + break; + } + + if (errno != EINTR) { + ret = errno; + error("poll failed (%d)", ret); + return false; + } + } + + return true; +} + +static bool write_to_endpoint(struct audio_endpoint *ep, size_t bytes) +{ + struct media_packet *mp = (struct media_packet *) ep->mp; + int ret; + + while (true) { + ret = write(ep->fd, mp, bytes); + + if (ret >= 0) + break; + + /* + * this should not happen so let's issue warning, but do not + * fail, we can try to write next packet + */ + if (errno == EAGAIN) { + ret = errno; + warn("write failed (%d)", ret); + break; + } + + if (errno != EINTR) { + ret = errno; + error("write failed (%d)", ret); + return false; + } + } + + return true; +} + +static bool write_data(struct a2dp_stream_out *out, const void *buffer, + size_t bytes) +{ + struct audio_endpoint *ep = out->ep; + struct media_packet *mp = (struct media_packet *) ep->mp; + struct media_packet_rtp *mp_rtp = (struct media_packet_rtp *) ep->mp; + size_t free_space = ep->mp_data_len; + size_t consumed = 0; + + while (consumed < bytes) { + size_t written = 0; + ssize_t read; + uint32_t samples; + int ret; + struct timespec current; + uint64_t audio_sent, audio_passed; + bool do_write = false; + + /* + * prepare media packet in advance so we don't waste time after + * wakeup + */ + if (ep->codec->use_rtp) { + mp_rtp->hdr.sequence_number = htons(ep->seq++); + mp_rtp->hdr.timestamp = htonl(ep->samples); + } + read = ep->codec->encode_mediapacket(ep->codec_data, + buffer + consumed, + bytes - consumed, mp, + free_space, &written); + + /* + * not much we can do here, let's just ignore remaining + * data and continue + */ + if (read <= 0) + return true; + + /* calculate where are we and where we should be */ + clock_gettime(CLOCK_MONOTONIC, ¤t); + if (!ep->samples) + memcpy(&ep->start, ¤t, sizeof(ep->start)); + audio_sent = ep->samples * 1000000ll / out->cfg.rate; + audio_passed = timespec_diff_us(¤t, &ep->start); + + /* + * if we're ahead of stream then wait for next write point, + * if we're lagging more than 100ms then stop writing and just + * skip data until we're back in sync + */ + if (audio_sent > audio_passed) { + struct timespec anchor; + + ep->resync = false; + + timespec_add(&ep->start, audio_sent, &anchor); + + while (true) { + ret = clock_nanosleep(CLOCK_MONOTONIC, + TIMER_ABSTIME, &anchor, + NULL); + + if (!ret) + break; + + if (ret != EINTR) { + error("clock_nanosleep failed (%d)", + ret); + return false; + } + } + } else if (!ep->resync) { + uint64_t diff = audio_passed - audio_sent; + + if (diff > MAX_DELAY) { + warn("lag is %jums, resyncing", diff / 1000); + + ep->codec->update_qos(ep->codec_data, + QOS_POLICY_DECREASE); + ep->resync = true; + } + } + + /* we send data only in case codec encoded some data, i.e. some + * codecs do internal buffering and output data only if full + * frame can be encoded + * in resync mode we'll just drop mediapackets + */ + if (written > 0 && !ep->resync) { + /* wait some time for socket to be ready for write, + * but we'll just skip writing data if timeout occurs + */ + if (!wait_for_endpoint(ep, &do_write)) + return false; + + if (do_write) { + if (ep->codec->use_rtp) + written += sizeof(struct rtp_header); + + if (!write_to_endpoint(ep, written)) + return false; + } + } + + /* + * AudioFlinger provides 16bit PCM, so sample size is 2 bytes + * multiplied by number of channels. Number of channels is + * simply number of bits set in channels mask. + */ + samples = read / (2 * popcount(out->cfg.channels)); + ep->samples += samples; + consumed += read; + } + + return true; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, + size_t bytes) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + const void *in_buf = buffer; + size_t in_len = bytes; + + /* just return in case we're closing */ + if (out->audio_state == AUDIO_A2DP_STATE_NONE) + return -1; + + /* We can auto-start only from standby */ + if (out->audio_state == AUDIO_A2DP_STATE_STANDBY) { + DBG("stream in standby, auto-start"); + + if (!resume_endpoint(out->ep)) + return -1; + + out->audio_state = AUDIO_A2DP_STATE_STARTED; + } + + if (out->audio_state != AUDIO_A2DP_STATE_STARTED) { + error("audio: stream not started"); + return -1; + } + + if (out->ep->fd < 0) { + error("audio: no transport socket"); + return -1; + } + + /* + * currently Android audioflinger is not able to provide mono stream on + * A2DP output so down mixing needs to be done in hal-audio plugin. + * + * for reference see + * AudioFlinger::PlaybackThread::readOutputParameters() + * frameworks/av/services/audioflinger/Threads.cpp:1631 + */ + if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) { + if (!out->downmix_buf) { + error("audio: downmix buffer not initialized"); + return -1; + } + + downmix_to_mono(out, buffer, bytes); + + in_buf = out->downmix_buf; + in_len = bytes / 2; + } + + if (!write_data(out, in_buf, in_len)) + return -1; + + return bytes; +} + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + return out->cfg.rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + if (rate != out->cfg.rate) { + warn("audio: cannot set sample rate to %d", rate); + return -1; + } + + return 0; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + DBG(""); + + /* + * We should return proper buffer size calculated by codec (so each + * input buffer is encoded into single media packed) but this does not + * work well with AudioFlinger and causes problems. For this reason we + * use magic value here and out_write code takes care of splitting + * input buffer into multiple media packets. + */ + return FIXED_BUFFER_SIZE; +} + +static uint32_t out_get_channels(const struct audio_stream *stream) +{ + DBG(""); + + /* + * AudioFlinger can only provide stereo stream, so we return it here and + * later we'll downmix this to mono in case codec requires it + */ + + return AUDIO_CHANNEL_OUT_STEREO; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + return out->cfg.format; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + DBG(""); + return -ENOSYS; +} + +static int out_standby(struct audio_stream *stream) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + if (out->audio_state == AUDIO_A2DP_STATE_STARTED) { + if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS) + return -1; + out->audio_state = AUDIO_A2DP_STATE_STANDBY; + } + + return 0; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + DBG(""); + return -ENOSYS; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + char *kvpair; + char *str; + char *saveptr; + bool enter_suspend = false; + bool exit_suspend = false; + + DBG("%s", kvpairs); + + str = strdup(kvpairs); + if (!str) + return -ENOMEM; + + kvpair = strtok_r(str, ";", &saveptr); + + for (; kvpair && *kvpair; kvpair = strtok_r(NULL, ";", &saveptr)) { + char *keyval; + + keyval = strchr(kvpair, '='); + if (!keyval) + continue; + + *keyval = '\0'; + keyval++; + + if (!strcmp(kvpair, "closing")) { + if (!strcmp(keyval, "true")) + out->audio_state = AUDIO_A2DP_STATE_NONE; + } else if (!strcmp(kvpair, "A2dpSuspended")) { + if (!strcmp(keyval, "true")) + enter_suspend = true; + else + exit_suspend = true; + } + } + + free(str); + + if (enter_suspend && out->audio_state == AUDIO_A2DP_STATE_STARTED) { + if (ipc_suspend_stream_cmd(out->ep->id) != AUDIO_STATUS_SUCCESS) + return -1; + out->audio_state = AUDIO_A2DP_STATE_SUSPENDED; + } + + if (exit_suspend && out->audio_state == AUDIO_A2DP_STATE_SUSPENDED) + out->audio_state = AUDIO_A2DP_STATE_STANDBY; + + return 0; +} + +static char *out_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + DBG(""); + return strdup(""); +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + struct audio_endpoint *ep = out->ep; + size_t pkt_duration; + + DBG(""); + + pkt_duration = ep->codec->get_mediapacket_duration(ep->codec_data); + + return FIXED_A2DP_PLAYBACK_LATENCY_MS + pkt_duration / 1000; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + DBG(""); + /* volume controlled in audioflinger mixer (digital) */ + return -ENOSYS; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + DBG(""); + return -ENOSYS; +} + +static int out_add_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + return -ENOSYS; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + return -ENOSYS; +} + +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + DBG(""); + return -ENOSYS; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static uint32_t in_get_channels(const struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + DBG(""); + return -ENOSYS; +} + +static int in_standby(struct audio_stream *stream) +{ + DBG(""); + return -ENOSYS; +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + DBG(""); + return -ENOSYS; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + DBG(""); + return -ENOSYS; +} + +static char *in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + DBG(""); + return strdup(""); +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + DBG(""); + return -ENOSYS; +} + +static ssize_t in_read(struct audio_stream_in *stream, void *buffer, + size_t bytes) +{ + DBG(""); + return -ENOSYS; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + DBG(""); + return -ENOSYS; +} + +static int in_add_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + return -ENOSYS; +} + +static int in_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_open_output_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out) + +{ + struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; + struct a2dp_stream_out *out; + + out = calloc(1, sizeof(struct a2dp_stream_out)); + if (!out) + return -ENOMEM; + + DBG(""); + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + + /* We want to autoselect opened endpoint */ + out->ep = NULL; + + if (!open_endpoint(&out->ep, &out->cfg)) + goto fail; + + DBG("rate=%d channels=%d format=%d", out->cfg.rate, + out->cfg.channels, out->cfg.format); + + if (out->cfg.channels == AUDIO_CHANNEL_OUT_MONO) { + out->downmix_buf = malloc(FIXED_BUFFER_SIZE / 2); + if (!out->downmix_buf) + goto fail; + } + + *stream_out = &out->stream; + a2dp_dev->out = out; + + out->audio_state = AUDIO_A2DP_STATE_STANDBY; + + return 0; + +fail: + error("audio: cannot open output stream"); + free(out); + *stream_out = NULL; + return -EIO; +} + +static void audio_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream) +{ + struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; + struct a2dp_stream_out *out = (struct a2dp_stream_out *) stream; + + DBG(""); + + close_endpoint(a2dp_dev->out->ep); + + free(out->downmix_buf); + + free(stream); + a2dp_dev->out = NULL; +} + +static int audio_set_parameters(struct audio_hw_device *dev, + const char *kvpairs) +{ + struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *) dev; + struct a2dp_stream_out *out = a2dp_dev->out; + + DBG(""); + + if (!out) + return 0; + + return out->stream.common.set_parameters((struct audio_stream *) out, + kvpairs); +} + +static char *audio_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + DBG(""); + return strdup(""); +} + +static int audio_init_check(const struct audio_hw_device *dev) +{ + DBG(""); + return 0; +} + +static int audio_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_set_master_volume(struct audio_hw_device *dev, float volume) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_set_mode(struct audio_hw_device *dev, int mode) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + DBG(""); + return -ENOSYS; +} + +static size_t audio_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + DBG(""); + return -ENOSYS; +} + +static int audio_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in) +{ + struct audio_stream_in *in; + + DBG(""); + + in = calloc(1, sizeof(struct audio_stream_in)); + if (!in) + return -ENOMEM; + + in->common.get_sample_rate = in_get_sample_rate; + in->common.set_sample_rate = in_set_sample_rate; + in->common.get_buffer_size = in_get_buffer_size; + in->common.get_channels = in_get_channels; + in->common.get_format = in_get_format; + in->common.set_format = in_set_format; + in->common.standby = in_standby; + in->common.dump = in_dump; + in->common.set_parameters = in_set_parameters; + in->common.get_parameters = in_get_parameters; + in->common.add_audio_effect = in_add_audio_effect; + in->common.remove_audio_effect = in_remove_audio_effect; + in->set_gain = in_set_gain; + in->read = in_read; + in->get_input_frames_lost = in_get_input_frames_lost; + + *stream_in = in; + + return 0; +} + +static void audio_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream_in) +{ + DBG(""); + free(stream_in); +} + +static int audio_dump(const audio_hw_device_t *device, int fd) +{ + DBG(""); + return -ENOSYS; +} + +static void unload_codec(void *data) +{ + struct audio_codec *codec = data; + + if (codec->unload) + codec->unload(); +} + +static int audio_close(hw_device_t *device) +{ + struct a2dp_audio_dev *a2dp_dev = (struct a2dp_audio_dev *)device; + + DBG(""); + + unregister_endpoints(); + + queue_destroy(loaded_codecs, unload_codec); + loaded_codecs = NULL; + + shutdown(listen_sk, SHUT_RDWR); + shutdown(audio_sk, SHUT_RDWR); + + pthread_join(ipc_th, NULL); + + close(listen_sk); + listen_sk = -1; + + free(a2dp_dev); + return 0; +} + +static void *ipc_handler(void *data) +{ + bool done = false; + struct pollfd pfd; + int sk; + + DBG(""); + + while (!done) { + DBG("Waiting for connection ..."); + + sk = accept(listen_sk, NULL, NULL); + if (sk < 0) { + int err = errno; + + if (err == EINTR) + continue; + + if (err != ECONNABORTED && err != EINVAL) + error("audio: Failed to accept socket: %d (%s)", + err, strerror(err)); + + break; + } + + pthread_mutex_lock(&sk_mutex); + audio_sk = sk; + pthread_mutex_unlock(&sk_mutex); + + DBG("Audio IPC: Connected"); + + if (register_endpoints() != AUDIO_STATUS_SUCCESS) { + error("audio: Failed to register endpoints"); + + unregister_endpoints(); + + pthread_mutex_lock(&sk_mutex); + shutdown(audio_sk, SHUT_RDWR); + close(audio_sk); + audio_sk = -1; + pthread_mutex_unlock(&sk_mutex); + + continue; + } + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = audio_sk; + pfd.events = POLLHUP | POLLERR | POLLNVAL; + + /* Check if socket is still alive. Empty while loop.*/ + while (poll(&pfd, 1, -1) < 0 && errno == EINTR); + + info("Audio HAL: Socket closed"); + + pthread_mutex_lock(&sk_mutex); + close(audio_sk); + audio_sk = -1; + pthread_mutex_unlock(&sk_mutex); + } + + /* audio_sk is closed at this point, just cleanup endpoints states */ + memset(audio_endpoints, 0, sizeof(audio_endpoints)); + + info("Closing Audio IPC thread"); + return NULL; +} + +static int audio_ipc_init(void) +{ + struct sockaddr_un addr; + int err; + int sk; + + DBG(""); + + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + err = -errno; + error("audio: Failed to create socket: %d (%s)", -err, + strerror(-err)); + return err; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_AUDIO_SK_PATH, + sizeof(BLUEZ_AUDIO_SK_PATH)); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = -errno; + error("audio: Failed to bind socket: %d (%s)", -err, + strerror(-err)); + goto failed; + } + + if (listen(sk, 1) < 0) { + err = -errno; + error("audio: Failed to listen on the socket: %d (%s)", -err, + strerror(-err)); + goto failed; + } + + listen_sk = sk; + + err = pthread_create(&ipc_th, NULL, ipc_handler, NULL); + if (err) { + err = -err; + ipc_th = 0; + error("audio: Failed to start Audio IPC thread: %d (%s)", + -err, strerror(-err)); + goto failed; + } + + return 0; + +failed: + close(sk); + return err; +} + +static int audio_open(const hw_module_t *module, const char *name, + hw_device_t **device) +{ + struct a2dp_audio_dev *a2dp_dev; + size_t i; + int err; + + DBG(""); + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) { + error("audio: interface %s not matching [%s]", name, + AUDIO_HARDWARE_INTERFACE); + return -EINVAL; + } + + err = audio_ipc_init(); + if (err < 0) + return err; + + a2dp_dev = calloc(1, sizeof(struct a2dp_audio_dev)); + if (!a2dp_dev) + return -ENOMEM; + + a2dp_dev->dev.common.tag = HARDWARE_DEVICE_TAG; + a2dp_dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT; + a2dp_dev->dev.common.module = (struct hw_module_t *) module; + a2dp_dev->dev.common.close = audio_close; + + a2dp_dev->dev.init_check = audio_init_check; + a2dp_dev->dev.set_voice_volume = audio_set_voice_volume; + a2dp_dev->dev.set_master_volume = audio_set_master_volume; + a2dp_dev->dev.set_mode = audio_set_mode; + a2dp_dev->dev.set_mic_mute = audio_set_mic_mute; + a2dp_dev->dev.get_mic_mute = audio_get_mic_mute; + a2dp_dev->dev.set_parameters = audio_set_parameters; + a2dp_dev->dev.get_parameters = audio_get_parameters; + a2dp_dev->dev.get_input_buffer_size = audio_get_input_buffer_size; + a2dp_dev->dev.open_output_stream = audio_open_output_stream; + a2dp_dev->dev.close_output_stream = audio_close_output_stream; + a2dp_dev->dev.open_input_stream = audio_open_input_stream; + a2dp_dev->dev.close_input_stream = audio_close_input_stream; + a2dp_dev->dev.dump = audio_dump; + + loaded_codecs = queue_new(); + + for (i = 0; i < NUM_CODECS; i++) { + audio_codec_get_t get_codec = audio_codecs[i]; + const struct audio_codec *codec = get_codec(); + + if (codec->load && !codec->load()) + continue; + + queue_push_tail(loaded_codecs, (void *) codec); + } + + /* + * Note that &a2dp_dev->dev.common is the same pointer as a2dp_dev. + * This results from the structure of following structs:a2dp_audio_dev, + * audio_hw_device. We will rely on this later in the code. + */ + *device = &a2dp_dev->dev.common; + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = audio_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "A2DP Bluez HW HAL", + .author = "Intel Corporation", + .methods = &hal_module_methods, + }, +}; diff -Nru bluez-4.101/android/hal-audio.h bluez-5.23/android/hal-audio.h --- bluez-4.101/android/hal-audio.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-audio.h 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_header { + unsigned cc:4; + unsigned x:1; + unsigned p:1; + unsigned v:2; + + unsigned pt:7; + unsigned m:1; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_header { + unsigned v:2; + unsigned p:1; + unsigned x:1; + unsigned cc:4; + + unsigned m:1; + unsigned pt:7; + + uint16_t sequence_number; + uint32_t timestamp; + uint32_t ssrc; + uint32_t csrc[0]; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +struct media_packet { + uint8_t data[0]; +}; + +struct media_packet_rtp { + struct rtp_header hdr; + uint8_t data[0]; +}; + +struct audio_input_config { + uint32_t rate; + uint32_t channels; + audio_format_t format; +}; + +struct audio_codec { + uint8_t type; + bool use_rtp; + + bool (*load) (void); + void (*unload) (void); + + int (*get_presets) (struct audio_preset *preset, size_t *len); + + bool (*init) (struct audio_preset *preset, uint16_t mtu, + void **codec_data); + bool (*cleanup) (void *codec_data); + bool (*get_config) (void *codec_data, + struct audio_input_config *config); + size_t (*get_buffer_size) (void *codec_data); + size_t (*get_mediapacket_duration) (void *codec_data); + ssize_t (*encode_mediapacket) (void *codec_data, const uint8_t *buffer, + size_t len, struct media_packet *mp, + size_t mp_data_len, size_t *written); + bool (*update_qos) (void *codec_data, uint8_t op); +}; + +#define QOS_POLICY_DEFAULT 0x00 +#define QOS_POLICY_DECREASE 0x01 + +typedef const struct audio_codec * (*audio_codec_get_t) (void); + +const struct audio_codec *codec_sbc(void); +const struct audio_codec *codec_aptx(void); diff -Nru bluez-4.101/android/hal-audio-sbc.c bluez-5.23/android/hal-audio-sbc.c --- bluez-4.101/android/hal-audio-sbc.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-audio-sbc.c 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include +#include "audio-msg.h" +#include "hal-audio.h" +#include "hal-log.h" +#include "../profiles/audio/a2dp-codecs.h" + +#define MAX_FRAMES_IN_PAYLOAD 15 + +#define SBC_QUALITY_MIN_BITPOOL 33 +#define SBC_QUALITY_STEP 5 + + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +struct rtp_payload { + unsigned frame_count:4; + unsigned rfa0:1; + unsigned is_last_fragment:1; + unsigned is_first_fragment:1; + unsigned is_fragmented:1; +} __attribute__ ((packed)); + +#elif __BYTE_ORDER == __BIG_ENDIAN + +struct rtp_payload { + unsigned is_fragmented:1; + unsigned is_first_fragment:1; + unsigned is_last_fragment:1; + unsigned rfa0:1; + unsigned frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +struct media_packet_sbc { + struct media_packet_rtp hdr; + struct rtp_payload payload; + uint8_t data[0]; +}; + +struct sbc_data { + a2dp_sbc_t sbc; + + sbc_t enc; + + uint16_t payload_len; + + size_t in_frame_len; + size_t in_buf_size; + + size_t out_frame_len; + + unsigned frame_duration; + unsigned frames_per_packet; +}; + +static const a2dp_sbc_t sbc_presets[] = { + { + .frequency = SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000, + .channel_mode = SBC_CHANNEL_MODE_MONO | + SBC_CHANNEL_MODE_DUAL_CHANNEL | + SBC_CHANNEL_MODE_STEREO | + SBC_CHANNEL_MODE_JOINT_STEREO, + .subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8, + .allocation_method = SBC_ALLOCATION_SNR | + SBC_ALLOCATION_LOUDNESS, + .block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 | + SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16, + .min_bitpool = MIN_BITPOOL, + .max_bitpool = MAX_BITPOOL + }, + { + .frequency = SBC_SAMPLING_FREQ_44100, + .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO, + .subbands = SBC_SUBBANDS_8, + .allocation_method = SBC_ALLOCATION_LOUDNESS, + .block_length = SBC_BLOCK_LENGTH_16, + .min_bitpool = MIN_BITPOOL, + .max_bitpool = MAX_BITPOOL + }, + { + .frequency = SBC_SAMPLING_FREQ_48000, + .channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO, + .subbands = SBC_SUBBANDS_8, + .allocation_method = SBC_ALLOCATION_LOUDNESS, + .block_length = SBC_BLOCK_LENGTH_16, + .min_bitpool = MIN_BITPOOL, + .max_bitpool = MAX_BITPOOL + }, +}; + +static int sbc_get_presets(struct audio_preset *preset, size_t *len) +{ + int i; + int count; + size_t new_len = 0; + uint8_t *ptr = (uint8_t *) preset; + size_t preset_size = sizeof(*preset) + sizeof(a2dp_sbc_t); + + count = sizeof(sbc_presets) / sizeof(sbc_presets[0]); + + for (i = 0; i < count; i++) { + preset = (struct audio_preset *) ptr; + + if (new_len + preset_size > *len) + break; + + preset->len = sizeof(a2dp_sbc_t); + memcpy(preset->data, &sbc_presets[i], preset->len); + + new_len += preset_size; + ptr += preset_size; + } + + *len = new_len; + + return i; +} + +static int sbc_freq2int(uint8_t freq) +{ + switch (freq) { + case SBC_SAMPLING_FREQ_16000: + return 16000; + case SBC_SAMPLING_FREQ_32000: + return 32000; + case SBC_SAMPLING_FREQ_44100: + return 44100; + case SBC_SAMPLING_FREQ_48000: + return 48000; + default: + return 0; + } +} + +static const char *sbc_mode2str(uint8_t mode) +{ + switch (mode) { + case SBC_CHANNEL_MODE_MONO: + return "Mono"; + case SBC_CHANNEL_MODE_DUAL_CHANNEL: + return "DualChannel"; + case SBC_CHANNEL_MODE_STEREO: + return "Stereo"; + case SBC_CHANNEL_MODE_JOINT_STEREO: + return "JointStereo"; + default: + return "(unknown)"; + } +} + +static int sbc_blocks2int(uint8_t blocks) +{ + switch (blocks) { + case SBC_BLOCK_LENGTH_4: + return 4; + case SBC_BLOCK_LENGTH_8: + return 8; + case SBC_BLOCK_LENGTH_12: + return 12; + case SBC_BLOCK_LENGTH_16: + return 16; + default: + return 0; + } +} + +static int sbc_subbands2int(uint8_t subbands) +{ + switch (subbands) { + case SBC_SUBBANDS_4: + return 4; + case SBC_SUBBANDS_8: + return 8; + default: + return 0; + } +} + +static const char *sbc_allocation2str(uint8_t allocation) +{ + switch (allocation) { + case SBC_ALLOCATION_SNR: + return "SNR"; + case SBC_ALLOCATION_LOUDNESS: + return "Loudness"; + default: + return "(unknown)"; + } +} + +static void sbc_init_encoder(struct sbc_data *sbc_data) +{ + a2dp_sbc_t *in = &sbc_data->sbc; + sbc_t *out = &sbc_data->enc; + + sbc_init_a2dp(out, 0L, in, sizeof(*in)); + + out->endian = SBC_LE; + out->bitpool = in->max_bitpool; + + DBG("frequency=%d channel_mode=%s block_length=%d subbands=%d allocation=%s bitpool=%d-%d", + sbc_freq2int(in->frequency), + sbc_mode2str(in->channel_mode), + sbc_blocks2int(in->block_length), + sbc_subbands2int(in->subbands), + sbc_allocation2str(in->allocation_method), + in->min_bitpool, in->max_bitpool); +} + +static void sbc_codec_calculate(struct sbc_data *sbc_data) +{ + size_t in_frame_len; + size_t out_frame_len; + size_t num_frames; + + in_frame_len = sbc_get_codesize(&sbc_data->enc); + out_frame_len = sbc_get_frame_length(&sbc_data->enc); + num_frames = sbc_data->payload_len / out_frame_len; + + if (num_frames > MAX_FRAMES_IN_PAYLOAD) + num_frames = MAX_FRAMES_IN_PAYLOAD; + + sbc_data->in_frame_len = in_frame_len; + sbc_data->in_buf_size = num_frames * in_frame_len; + + sbc_data->out_frame_len = out_frame_len; + + sbc_data->frame_duration = sbc_get_frame_duration(&sbc_data->enc); + sbc_data->frames_per_packet = num_frames; + + DBG("in_frame_len=%zu out_frame_len=%zu frames_per_packet=%zu", + in_frame_len, out_frame_len, num_frames); +} + +static bool sbc_codec_init(struct audio_preset *preset, uint16_t payload_len, + void **codec_data) +{ + struct sbc_data *sbc_data; + + if (preset->len != sizeof(a2dp_sbc_t)) { + error("SBC: preset size mismatch"); + return false; + } + + sbc_data = calloc(sizeof(struct sbc_data), 1); + if (!sbc_data) + return false; + + memcpy(&sbc_data->sbc, preset->data, preset->len); + + sbc_init_encoder(sbc_data); + + sbc_data->payload_len = payload_len; + + sbc_codec_calculate(sbc_data); + + *codec_data = sbc_data; + + return true; +} + +static bool sbc_cleanup(void *codec_data) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + + sbc_finish(&sbc_data->enc); + free(codec_data); + + return true; +} + +static bool sbc_get_config(void *codec_data, struct audio_input_config *config) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + + switch (sbc_data->sbc.frequency) { + case SBC_SAMPLING_FREQ_16000: + config->rate = 16000; + break; + case SBC_SAMPLING_FREQ_32000: + config->rate = 32000; + break; + case SBC_SAMPLING_FREQ_44100: + config->rate = 44100; + break; + case SBC_SAMPLING_FREQ_48000: + config->rate = 48000; + break; + default: + return false; + } + config->channels = sbc_data->sbc.channel_mode == SBC_CHANNEL_MODE_MONO ? + AUDIO_CHANNEL_OUT_MONO : + AUDIO_CHANNEL_OUT_STEREO; + config->format = AUDIO_FORMAT_PCM_16_BIT; + + return true; +} + +static size_t sbc_get_buffer_size(void *codec_data) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + + return sbc_data->in_buf_size; +} + +static size_t sbc_get_mediapacket_duration(void *codec_data) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + + return sbc_data->frame_duration * sbc_data->frames_per_packet; +} + +static ssize_t sbc_encode_mediapacket(void *codec_data, const uint8_t *buffer, + size_t len, struct media_packet *mp, + size_t mp_data_len, size_t *written) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + struct media_packet_sbc *mp_sbc = (struct media_packet_sbc *) mp; + size_t consumed = 0; + size_t encoded = 0; + uint8_t frame_count = 0; + + mp_data_len -= sizeof(mp_sbc->payload); + + while (len - consumed >= sbc_data->in_frame_len && + mp_data_len - encoded >= sbc_data->out_frame_len && + frame_count < sbc_data->frames_per_packet) { + ssize_t read; + ssize_t written = 0; + + read = sbc_encode(&sbc_data->enc, buffer + consumed, + sbc_data->in_frame_len, mp_sbc->data + encoded, + mp_data_len - encoded, &written); + + if (read < 0) { + error("SBC: failed to encode block at frame %d (%zd)", + frame_count, read); + break; + } + + frame_count++; + consumed += read; + encoded += written; + } + + *written = encoded + sizeof(mp_sbc->payload); + mp_sbc->payload.frame_count = frame_count; + + return consumed; +} + +static bool sbc_update_qos(void *codec_data, uint8_t op) +{ + struct sbc_data *sbc_data = (struct sbc_data *) codec_data; + uint8_t curr_bitpool = sbc_data->enc.bitpool; + uint8_t new_bitpool = curr_bitpool; + + switch (op) { + case QOS_POLICY_DEFAULT: + new_bitpool = sbc_data->sbc.max_bitpool; + break; + + case QOS_POLICY_DECREASE: + if (curr_bitpool > SBC_QUALITY_MIN_BITPOOL) { + new_bitpool = curr_bitpool - SBC_QUALITY_STEP; + if (new_bitpool < SBC_QUALITY_MIN_BITPOOL) + new_bitpool = SBC_QUALITY_MIN_BITPOOL; + } + break; + } + + if (new_bitpool == curr_bitpool) + return false; + + sbc_data->enc.bitpool = new_bitpool; + + sbc_codec_calculate(sbc_data); + + info("SBC: bitpool changed: %d -> %d", curr_bitpool, new_bitpool); + + return true; +} + +static const struct audio_codec codec = { + .type = A2DP_CODEC_SBC, + .use_rtp = true, + + .get_presets = sbc_get_presets, + + .init = sbc_codec_init, + .cleanup = sbc_cleanup, + .get_config = sbc_get_config, + .get_buffer_size = sbc_get_buffer_size, + .get_mediapacket_duration = sbc_get_mediapacket_duration, + .encode_mediapacket = sbc_encode_mediapacket, + .update_qos = sbc_update_qos, +}; + +const struct audio_codec *codec_sbc(void) +{ + return &codec; +} diff -Nru bluez-4.101/android/hal-avrcp.c bluez-5.23/android/hal-avrcp.c --- bluez-4.101/android/hal-avrcp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-avrcp.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,686 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" + +static const btrc_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void handle_remote_features(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_remote_features *ev = buf; + + if (cbs->remote_features_cb) + cbs->remote_features_cb((bt_bdaddr_t *) (ev->bdaddr), + ev->features); +} + +static void handle_get_play_status(void *buf, uint16_t len, int fd) +{ + if (cbs->get_play_status_cb) + cbs->get_play_status_cb(); +} + +static void handle_list_player_attrs(void *buf, uint16_t len, int fd) +{ + if (cbs->list_player_app_attr_cb) + cbs->list_player_app_attr_cb(); +} + +static void handle_list_player_values(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_list_player_values *ev = buf; + + if (cbs->list_player_app_values_cb) + cbs->list_player_app_values_cb(ev->attr); +} + +static void handle_get_player_values(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_get_player_values *ev = buf; + btrc_player_attr_t attrs[4]; + int i; + + if (!cbs->get_player_app_value_cb) + return; + + /* Convert uint8_t array to btrc_player_attr_t array */ + for (i = 0; i < ev->number; i++) + attrs[i] = ev->attrs[i]; + + cbs->get_player_app_value_cb(ev->number, attrs); +} + +static void handle_get_player_attrs_text(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_get_player_attrs_text *ev = buf; + btrc_player_attr_t attrs[4]; + int i; + + if (!cbs->get_player_app_attrs_text_cb) + return; + + /* Convert uint8_t array to btrc_player_attr_t array */ + for (i = 0; i < ev->number; i++) + attrs[i] = ev->attrs[i]; + + cbs->get_player_app_attrs_text_cb(ev->number, attrs); +} + +static void handle_get_player_values_text(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_get_player_values_text *ev = buf; + + if (cbs->get_player_app_values_text_cb) + cbs->get_player_app_values_text_cb(ev->attr, ev->number, + ev->values); +} + +static void handle_set_player_value(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_set_player_values *ev = buf; + struct hal_avrcp_player_attr_value *attrs; + btrc_player_settings_t values; + int i; + + if (!cbs->set_player_app_value_cb) + return; + + attrs = (struct hal_avrcp_player_attr_value *) ev->attrs; + + /* Convert to btrc_player_settings_t */ + values.num_attr = ev->number; + for (i = 0; i < ev->number; i++) { + values.attr_ids[i] = attrs[i].attr; + values.attr_values[i] = attrs[i].value; + } + + cbs->set_player_app_value_cb(&values); +} + +static void handle_get_element_attrs(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_get_element_attrs *ev = buf; + btrc_media_attr_t attrs[BTRC_MAX_APP_SETTINGS]; + int i; + + if (!cbs->get_element_attr_cb) + return; + + /* Convert uint8_t array to btrc_media_attr_t array */ + for (i = 0; i < ev->number; i++) + attrs[i] = ev->attrs[i]; + + cbs->get_element_attr_cb(ev->number, attrs); +} + +static void handle_register_notification(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_register_notification *ev = buf; + + if (cbs->register_notification_cb) + cbs->register_notification_cb(ev->event, ev->param); +} + +static void handle_volume_changed(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_volume_changed *ev = buf; + + if (cbs->volume_change_cb) + cbs->volume_change_cb(ev->volume, ev->type); +} + +static void handle_passthrough_cmd(void *buf, uint16_t len, int fd) +{ + struct hal_ev_avrcp_passthrough_cmd *ev = buf; + + if (cbs->passthrough_cmd_cb) + cbs->passthrough_cmd_cb(ev->id, ev->state); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_AVRCP_REMOTE_FEATURES */ + { handle_remote_features, false, + sizeof(struct hal_ev_avrcp_remote_features) }, + /* HAL_EV_AVRCP_GET_PLAY_STATUS */ + { handle_get_play_status, false, 0 }, + /* HAL_EV_AVRCP_LIST_PLAYER_ATTRS */ + { handle_list_player_attrs, false, 0 }, + /* HAL_EV_AVRCP_LIST_PLAYER_VALUES */ + { handle_list_player_values, false, + sizeof(struct hal_ev_avrcp_list_player_values) }, + /* HAL_EV_AVRCP_GET_PLAYER_VALUES */ + { handle_get_player_values, true, + sizeof(struct hal_ev_avrcp_get_player_values) }, + /* HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT */ + { handle_get_player_attrs_text, true, + sizeof(struct hal_ev_avrcp_get_player_attrs_text) }, + /* HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT */ + { handle_get_player_values_text, true, + sizeof(struct hal_ev_avrcp_get_player_values_text) }, + /* HAL_EV_AVRCP_SET_PLAYER_VALUES */ + { handle_set_player_value, true, + sizeof(struct hal_ev_avrcp_set_player_values) }, + /* HAL_EV_AVRCP_GET_ELEMENT_ATTRS */ + { handle_get_element_attrs, true, + sizeof(struct hal_ev_avrcp_get_element_attrs) }, + /* HAL_EV_AVRCP_REGISTER_NOTIFICATION */ + { handle_register_notification, false, + sizeof(struct hal_ev_avrcp_register_notification) }, + /* HAL_EV_AVRCP_VOLUME_CHANGED */ + { handle_volume_changed, false, + sizeof(struct hal_ev_avrcp_volume_changed) }, + /* HAL_EV_AVRCP_PASSTHROUGH_CMD */ + { handle_passthrough_cmd, false, + sizeof(struct hal_ev_avrcp_passthrough_cmd) }, +}; + +static bt_status_t init(btrc_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + cbs = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_AVRCP, ev_handlers, + sizeof(ev_handlers) / sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_AVRCP; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_AVRCP); + } + + return ret; +} + +static bt_status_t get_play_status_rsp(btrc_play_status_t status, + uint32_t song_len, uint32_t song_pos) +{ + struct hal_cmd_avrcp_get_play_status cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.status = status; + cmd.duration = song_len; + cmd.position = song_pos; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_GET_PLAY_STATUS, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t list_player_app_attr_rsp(int num_attr, + btrc_player_attr_t *p_attrs) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_list_player_attrs *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (num_attr < 0) + return BT_STATUS_PARM_INVALID; + + len = sizeof(*cmd) + num_attr; + if (len > IPC_MTU) + return BT_STATUS_PARM_INVALID; + + cmd->number = num_attr; + memcpy(cmd->attrs, p_attrs, num_attr); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_LIST_PLAYER_ATTRS, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t list_player_app_value_rsp(int num_val, uint8_t *p_vals) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_list_player_values *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (num_val < 0) + return BT_STATUS_PARM_INVALID; + + len = sizeof(*cmd) + num_val; + + if (len > IPC_MTU) + return BT_STATUS_PARM_INVALID; + + cmd->number = num_val; + memcpy(cmd->values, p_vals, num_val); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_LIST_PLAYER_VALUES, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t get_player_app_value_rsp(btrc_player_settings_t *p_vals) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_get_player_attrs *cmd = (void *) buf; + size_t len, attrs_len; + int i; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!p_vals) + return BT_STATUS_PARM_INVALID; + + attrs_len = p_vals->num_attr * + sizeof(struct hal_avrcp_player_attr_value); + len = sizeof(*cmd) + attrs_len; + + if (len > IPC_MTU) + return BT_STATUS_PARM_INVALID; + + cmd->number = p_vals->num_attr; + + for (i = 0; i < p_vals->num_attr; i++) { + cmd->attrs[i].attr = p_vals->attr_ids[i]; + cmd->attrs[i].value = p_vals->attr_values[i]; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_ATTRS, + len, cmd, NULL, NULL, NULL); +} + +static int write_text(uint8_t *ptr, uint8_t id, uint8_t *text, size_t *len) +{ + struct hal_avrcp_player_setting_text *value = (void *) ptr; + size_t attr_len = sizeof(*value); + + if (attr_len + *len > IPC_MTU) + return 0; + + value->id = id; + value->len = strnlen((const char *) text, BTRC_MAX_ATTR_STR_LEN); + + *len += attr_len; + + if (value->len + *len > IPC_MTU) + value->len = IPC_MTU - *len; + + memcpy(value->text, text, value->len); + + *len += value->len; + + return attr_len + value->len; +} + +static uint8_t write_player_setting_text(uint8_t *ptr, uint8_t num_attr, + btrc_player_setting_text_t *p_attrs, + size_t *len) +{ + int i; + + for (i = 0; i < num_attr && *len < IPC_MTU; i++) { + int ret; + + ret = write_text(ptr, p_attrs[i].id, p_attrs[i].text, len); + if (ret == 0) + break; + + ptr += ret; + } + + return i; +} + +static bt_status_t get_player_app_attr_text_rsp(int num_attr, + btrc_player_setting_text_t *p_attrs) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_get_player_attrs_text *cmd = (void *) buf; + uint8_t *ptr; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (num_attr < 0 || num_attr > BTRC_MAX_APP_SETTINGS) + return BT_STATUS_PARM_INVALID; + + len = sizeof(*cmd); + ptr = (uint8_t *) &cmd->attrs[0]; + cmd->number = write_player_setting_text(ptr, num_attr, p_attrs, &len); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t get_player_app_value_text_rsp(int num_val, + btrc_player_setting_text_t *p_vals) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_get_player_values_text *cmd = (void *) buf; + uint8_t *ptr; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (num_val < 0) + return BT_STATUS_PARM_INVALID; + + len = sizeof(*cmd); + ptr = (uint8_t *) &cmd->values[0]; + cmd->number = write_player_setting_text(ptr, num_val, p_vals, &len); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT, + len, cmd, NULL, NULL, NULL); +} + +static uint8_t write_element_attr_text(uint8_t *ptr, uint8_t num_attr, + btrc_element_attr_val_t *p_attrs, + size_t *len) +{ + int i; + + for (i = 0; i < num_attr && *len < IPC_MTU; i++) { + int ret; + + ret = write_text(ptr, p_attrs[i].attr_id, p_attrs[i].text, len); + if (ret == 0) + break; + + ptr += ret; + } + + return i; +} + +static bt_status_t get_element_attr_rsp(uint8_t num_attr, + btrc_element_attr_val_t *p_attrs) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_get_element_attrs_text *cmd = (void *) buf; + size_t len; + uint8_t *ptr; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + len = sizeof(*cmd); + ptr = (uint8_t *) &cmd->values[0]; + cmd->number = write_element_attr_text(ptr, num_attr, p_attrs, &len); + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t set_player_app_value_rsp(btrc_status_t rsp_status) +{ + struct hal_cmd_avrcp_set_player_attrs_value cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.status = rsp_status; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t play_status_changed_rsp(btrc_notification_type_t type, + btrc_play_status_t *play_status) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + size_t len; + + cmd->event = BTRC_EVT_PLAY_STATUS_CHANGED; + cmd->type = type; + cmd->len = 1; + memcpy(cmd->data, play_status, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t track_change_rsp(btrc_notification_type_t type, + btrc_uid_t *track) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + size_t len; + + cmd->event = BTRC_EVT_TRACK_CHANGE; + cmd->type = type; + cmd->len = sizeof(*track); + memcpy(cmd->data, track, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t track_reached_end_rsp(btrc_notification_type_t type) +{ + struct hal_cmd_avrcp_register_notification cmd; + + cmd.event = BTRC_EVT_TRACK_REACHED_END; + cmd.type = type; + cmd.len = 0; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t track_reached_start_rsp(btrc_notification_type_t type) +{ + struct hal_cmd_avrcp_register_notification cmd; + + cmd.event = BTRC_EVT_TRACK_REACHED_START; + cmd.type = type; + cmd.len = 0; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t play_pos_changed_rsp(btrc_notification_type_t type, + uint32_t *song_pos) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + size_t len; + + cmd->event = BTRC_EVT_PLAY_POS_CHANGED; + cmd->type = type; + cmd->len = sizeof(*song_pos); + memcpy(cmd->data, song_pos, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t settings_changed_rsp(btrc_notification_type_t type, + btrc_player_settings_t *player_setting) +{ + char buf[IPC_MTU]; + struct hal_cmd_avrcp_register_notification *cmd = (void *) buf; + struct hal_avrcp_player_attr_value *attrs; + size_t len, param_len; + int i; + + param_len = player_setting->num_attr * sizeof(*attrs); + len = sizeof(*cmd) + param_len; + + if (len > IPC_MTU) + return BT_STATUS_PARM_INVALID; + + cmd->event = BTRC_EVT_APP_SETTINGS_CHANGED; + cmd->type = type; + cmd->len = param_len; + + attrs = (struct hal_avrcp_player_attr_value *) &cmd->data[0]; + for (i = 0; i < player_setting->num_attr; i++) { + attrs[i].attr = player_setting->attr_ids[i]; + attrs[i].value = player_setting->attr_values[i]; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, + HAL_OP_AVRCP_REGISTER_NOTIFICATION, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t register_notification_rsp(btrc_event_id_t event_id, + btrc_notification_type_t type, + btrc_register_notification_t *p_param) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + switch (event_id) { + case BTRC_EVT_PLAY_STATUS_CHANGED: + return play_status_changed_rsp(type, &p_param->play_status); + case BTRC_EVT_TRACK_CHANGE: + return track_change_rsp(type, &p_param->track); + case BTRC_EVT_TRACK_REACHED_END: + return track_reached_end_rsp(type); + case BTRC_EVT_TRACK_REACHED_START: + return track_reached_start_rsp(type); + case BTRC_EVT_PLAY_POS_CHANGED: + return play_pos_changed_rsp(type, &p_param->song_pos); + case BTRC_EVT_APP_SETTINGS_CHANGED: + return settings_changed_rsp(type, &p_param->player_setting); + default: + return BT_STATUS_PARM_INVALID; + } +} + +static bt_status_t set_volume(uint8_t volume) +{ + struct hal_cmd_avrcp_set_volume cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.value = volume; + + return hal_ipc_cmd(HAL_SERVICE_ID_AVRCP, HAL_OP_AVRCP_SET_VOLUME, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_AVRCP; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_AVRCP); +} + +static btrc_interface_t iface = { + .size = sizeof(iface), + .init = init, + .get_play_status_rsp = get_play_status_rsp, + .list_player_app_attr_rsp = list_player_app_attr_rsp, + .list_player_app_value_rsp = list_player_app_value_rsp, + .get_player_app_value_rsp = get_player_app_value_rsp, + .get_player_app_attr_text_rsp = get_player_app_attr_text_rsp, + .get_player_app_value_text_rsp = get_player_app_value_text_rsp, + .get_element_attr_rsp = get_element_attr_rsp, + .set_player_app_value_rsp = set_player_app_value_rsp, + .register_notification_rsp = register_notification_rsp, + .set_volume = set_volume, + .cleanup = cleanup +}; + +btrc_interface_t *bt_get_avrcp_interface(void) +{ + return &iface; +} diff -Nru bluez-4.101/android/hal-bluetooth.c bluez-5.23/android/hal-bluetooth.c --- bluez-4.101/android/hal-bluetooth.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-bluetooth.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,939 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" +#include "hal-utils.h" + +#define MODE_PROPERTY_NAME "persist.sys.bluetooth.mode" + +static const bt_callbacks_t *bt_hal_cbacks = NULL; + +#define enum_prop_to_hal(prop, hal_prop, type) do { \ + static type e; \ + prop.val = &e; \ + prop.len = sizeof(e); \ + e = *((uint8_t *) (hal_prop->val)); \ +} while (0) + +#define enum_prop_from_hal(prop, hal_len, hal_val, enum_type) do { \ + enum_type e; \ + if (prop->len != sizeof(e)) { \ + error("invalid HAL property %u (%u vs %zu), aborting ", \ + prop->type, prop->len, sizeof(e)); \ + exit(EXIT_FAILURE); \ + } \ + memcpy(&e, prop->val, sizeof(e)); \ + *((uint8_t *) hal_val) = e; /* enums are mapped to 1 byte */ \ + *hal_len = 1; \ +} while (0) + +static void handle_adapter_state_changed(void *buf, uint16_t len, int fd) +{ + struct hal_ev_adapter_state_changed *ev = buf; + + DBG("state: %s", bt_state_t2str(ev->state)); + + if (bt_hal_cbacks->adapter_state_changed_cb) + bt_hal_cbacks->adapter_state_changed_cb(ev->state); +} + +static void adapter_props_to_hal(bt_property_t *send_props, + struct hal_property *prop, + uint8_t num_props, uint16_t len) +{ + void *buf = prop; + uint8_t i; + + for (i = 0; i < num_props; i++) { + if (sizeof(*prop) + prop->len > len) { + error("invalid adapter properties(%zu > %u), aborting", + sizeof(*prop) + prop->len, len); + exit(EXIT_FAILURE); + } + + send_props[i].type = prop->type; + + switch (prop->type) { + case HAL_PROP_ADAPTER_TYPE: + enum_prop_to_hal(send_props[i], prop, + bt_device_type_t); + break; + case HAL_PROP_ADAPTER_SCAN_MODE: + enum_prop_to_hal(send_props[i], prop, + bt_scan_mode_t); + break; + case HAL_PROP_ADAPTER_SERVICE_REC: + default: + send_props[i].len = prop->len; + send_props[i].val = prop->val; + break; + } + + DBG("prop[%d]: %s", i, btproperty2str(&send_props[i])); + + len -= sizeof(*prop) + prop->len; + buf += sizeof(*prop) + prop->len; + prop = buf; + } + + if (!len) + return; + + error("invalid adapter properties (%u bytes left), aborting", len); + exit(EXIT_FAILURE); +} + +static void adapter_prop_from_hal(const bt_property_t *property, uint8_t *type, + uint16_t *len, void *val) +{ + /* type match IPC type */ + *type = property->type; + + switch (property->type) { + case HAL_PROP_ADAPTER_SCAN_MODE: + enum_prop_from_hal(property, len, val, bt_scan_mode_t); + break; + default: + *len = property->len; + memcpy(val, property->val, property->len); + break; + } +} + +static void device_props_to_hal(bt_property_t *send_props, + struct hal_property *prop, uint8_t num_props, + uint16_t len) +{ + void *buf = prop; + uint8_t i; + + for (i = 0; i < num_props; i++) { + if (sizeof(*prop) + prop->len > len) { + error("invalid device properties (%zu > %u), aborting", + sizeof(*prop) + prop->len, len); + exit(EXIT_FAILURE); + } + + send_props[i].type = prop->type; + + switch (prop->type) { + case HAL_PROP_DEVICE_TYPE: + enum_prop_to_hal(send_props[i], prop, + bt_device_type_t); + break; + case HAL_PROP_DEVICE_VERSION_INFO: + { + static bt_remote_version_t e; + const struct hal_prop_device_info *p; + + send_props[i].val = &e; + send_props[i].len = sizeof(e); + + p = (struct hal_prop_device_info *) prop->val; + + e.manufacturer = p->manufacturer; + e.sub_ver = p->sub_version; + e.version = p->version; + } + break; + case HAL_PROP_DEVICE_SERVICE_REC: + { + static bt_service_record_t e; + const struct hal_prop_device_service_rec *p; + + send_props[i].val = &e; + send_props[i].len = sizeof(e); + + p = (struct hal_prop_device_service_rec *) prop->val; + + memset(&e, 0, sizeof(e)); + memcpy(&e.channel, &p->channel, sizeof(e.channel)); + memcpy(e.uuid.uu, p->uuid, sizeof(e.uuid.uu)); + memcpy(e.name, p->name, p->name_len); + } + break; + default: + send_props[i].len = prop->len; + send_props[i].val = prop->val; + break; + } + + len -= sizeof(*prop) + prop->len; + buf += sizeof(*prop) + prop->len; + prop = buf; + + DBG("prop[%d]: %s", i, btproperty2str(&send_props[i])); + } + + if (!len) + return; + + error("invalid device properties (%u bytes left), aborting", len); + exit(EXIT_FAILURE); +} + +static void handle_adapter_props_changed(void *buf, uint16_t len, int fd) +{ + struct hal_ev_adapter_props_changed *ev = buf; + bt_property_t props[ev->num_props]; + + DBG(""); + + if (!bt_hal_cbacks->adapter_properties_cb) + return; + + len -= sizeof(*ev); + adapter_props_to_hal(props, ev->props, ev->num_props, len); + + bt_hal_cbacks->adapter_properties_cb(ev->status, ev->num_props, props); +} + +static void handle_bond_state_change(void *buf, uint16_t len, int fd) +{ + struct hal_ev_bond_state_changed *ev = buf; + bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; + + DBG("state %u", ev->state); + + if (bt_hal_cbacks->bond_state_changed_cb) + bt_hal_cbacks->bond_state_changed_cb(ev->status, addr, + ev->state); +} + +static void handle_pin_request(void *buf, uint16_t len, int fd) +{ + struct hal_ev_pin_request *ev = buf; + /* Those are declared as packed, so it's safe to assign pointers */ + bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; + bt_bdname_t *name = (bt_bdname_t *) ev->name; + + DBG(""); + + if (bt_hal_cbacks->pin_request_cb) + bt_hal_cbacks->pin_request_cb(addr, name, ev->class_of_dev); +} + +static void handle_ssp_request(void *buf, uint16_t len, int fd) +{ + struct hal_ev_ssp_request *ev = buf; + /* Those are declared as packed, so it's safe to assign pointers */ + bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; + bt_bdname_t *name = (bt_bdname_t *) ev->name; + + DBG(""); + + if (bt_hal_cbacks->ssp_request_cb) + bt_hal_cbacks->ssp_request_cb(addr, name, ev->class_of_dev, + ev->pairing_variant, + ev->passkey); +} + +void bt_thread_associate(void) +{ + if (bt_hal_cbacks->thread_evt_cb) + bt_hal_cbacks->thread_evt_cb(ASSOCIATE_JVM); +} + +void bt_thread_disassociate(void) +{ + if (bt_hal_cbacks->thread_evt_cb) + bt_hal_cbacks->thread_evt_cb(DISASSOCIATE_JVM); +} + +static bool interface_ready(void) +{ + return bt_hal_cbacks != NULL; +} + +static void handle_discovery_state_changed(void *buf, uint16_t len, int fd) +{ + struct hal_ev_discovery_state_changed *ev = buf; + + DBG(""); + + if (bt_hal_cbacks->discovery_state_changed_cb) + bt_hal_cbacks->discovery_state_changed_cb(ev->state); +} + +static void handle_device_found(void *buf, uint16_t len, int fd) +{ + struct hal_ev_device_found *ev = buf; + bt_property_t props[ev->num_props]; + + DBG(""); + + if (!bt_hal_cbacks->device_found_cb) + return; + + len -= sizeof(*ev); + device_props_to_hal(props, ev->props, ev->num_props, len); + + bt_hal_cbacks->device_found_cb(ev->num_props, props); +} + +static void handle_device_state_changed(void *buf, uint16_t len, int fd) +{ + struct hal_ev_remote_device_props *ev = buf; + bt_property_t props[ev->num_props]; + + DBG(""); + + if (!bt_hal_cbacks->remote_device_properties_cb) + return; + + len -= sizeof(*ev); + device_props_to_hal(props, ev->props, ev->num_props, len); + + bt_hal_cbacks->remote_device_properties_cb(ev->status, + (bt_bdaddr_t *)ev->bdaddr, + ev->num_props, props); +} + +static void handle_acl_state_changed(void *buf, uint16_t len, int fd) +{ + struct hal_ev_acl_state_changed *ev = buf; + bt_bdaddr_t *addr = (bt_bdaddr_t *) ev->bdaddr; + + DBG("state %u", ev->state); + + if (bt_hal_cbacks->acl_state_changed_cb) + bt_hal_cbacks->acl_state_changed_cb(ev->status, addr, + ev->state); +} + +static void handle_dut_mode_receive(void *buf, uint16_t len, int fd) +{ + struct hal_ev_dut_mode_receive *ev = buf; + + DBG(""); + + if (len != sizeof(*ev) + ev->len) { + error("invalid dut mode receive event (%u), aborting", len); + exit(EXIT_FAILURE); + } + + if (bt_hal_cbacks->dut_mode_recv_cb) + bt_hal_cbacks->dut_mode_recv_cb(ev->opcode, ev->data, ev->len); +} + +static void handle_le_test_mode(void *buf, uint16_t len, int fd) +{ + struct hal_ev_le_test_mode *ev = buf; + + DBG(""); + + if (bt_hal_cbacks->le_test_mode_cb) + bt_hal_cbacks->le_test_mode_cb(ev->status, ev->num_packets); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_ADAPTER_STATE_CHANGED */ + { handle_adapter_state_changed, false, + sizeof(struct hal_ev_adapter_state_changed) }, + /* HAL_EV_ADAPTER_PROPS_CHANGED */ + { handle_adapter_props_changed, true, + sizeof(struct hal_ev_adapter_props_changed) + + sizeof(struct hal_property) }, + /* HAL_EV_REMOTE_DEVICE_PROPS */ + { handle_device_state_changed, true, + sizeof(struct hal_ev_remote_device_props) + + sizeof(struct hal_property) }, + /* HAL_EV_DEVICE_FOUND */ + { handle_device_found, true, sizeof(struct hal_ev_device_found) + + sizeof(struct hal_property) }, + /* HAL_EV_DISCOVERY_STATE_CHANGED */ + { handle_discovery_state_changed, false, + sizeof(struct hal_ev_discovery_state_changed) }, + /* HAL_EV_PIN_REQUEST */ + { handle_pin_request, false, sizeof(struct hal_ev_pin_request) }, + /* HAL_EV_SSP_REQUEST */ + { handle_ssp_request, false, sizeof(struct hal_ev_ssp_request) }, + /* HAL_EV_BOND_STATE_CHANGED */ + { handle_bond_state_change, false, + sizeof(struct hal_ev_bond_state_changed) }, + /* HAL_EV_ACL_STATE_CHANGED */ + { handle_acl_state_changed, false, + sizeof(struct hal_ev_acl_state_changed) }, + /* HAL_EV_DUT_MODE_RECEIVE */ + { handle_dut_mode_receive, true, + sizeof(struct hal_ev_dut_mode_receive) }, + /* HAL_EV_LE_TEST_MODE */ + { handle_le_test_mode, false, sizeof(struct hal_ev_le_test_mode) }, +}; + +static uint8_t get_mode(void) +{ + char value[PROPERTY_VALUE_MAX]; + + if (property_get(MODE_PROPERTY_NAME, value, "") > 0 && + (!strcasecmp(value, "bredr"))) + return HAL_MODE_BREDR; + + if (property_get(MODE_PROPERTY_NAME, value, "") > 0 && + (!strcasecmp(value, "le"))) + return HAL_MODE_LE; + + return HAL_MODE_DEFAULT; +} + +static int init(bt_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int status; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + hal_ipc_register(HAL_SERVICE_ID_BLUETOOTH, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + if (!hal_ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH))) + return BT_STATUS_FAIL; + + bt_hal_cbacks = callbacks; + + /* Start Android Bluetooth daemon service */ + if (property_set("bluetooth.start", "daemon") < 0) { + error("Failed to set bluetooth.start=daemon"); + hal_ipc_cleanup(); + bt_hal_cbacks = NULL; + return BT_STATUS_FAIL; + } + + if (!hal_ipc_accept()) { + hal_ipc_cleanup(); + bt_hal_cbacks = NULL; + return BT_STATUS_FAIL; + } + + cmd.service_id = HAL_SERVICE_ID_BLUETOOTH; + cmd.mode = get_mode(); + + status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + if (status != BT_STATUS_SUCCESS) { + error("Failed to register 'bluetooth' service"); + goto fail; + } + + cmd.service_id = HAL_SERVICE_ID_SOCKET; + cmd.mode = HAL_MODE_DEFAULT; + + status = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + if (status != BT_STATUS_SUCCESS) { + error("Failed to register 'socket' service"); + goto fail; + } + + return status; + +fail: + hal_ipc_cleanup(); + bt_hal_cbacks = NULL; + + hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH); + + return status; +} + +static int enable(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_ENABLE, 0, NULL, + NULL, NULL, NULL); +} + +static int disable(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DISABLE, 0, NULL, + NULL, NULL, NULL); +} + +static void cleanup(void) +{ + DBG(""); + + if (!interface_ready()) + return; + + hal_ipc_cleanup(); + + bt_hal_cbacks = NULL; + + hal_ipc_unregister(HAL_SERVICE_ID_BLUETOOTH); +} + +static int get_adapter_properties(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROPS, + 0, NULL, NULL, NULL, NULL); +} + +static int get_adapter_property(bt_property_type_t type) +{ + struct hal_cmd_get_adapter_prop cmd; + + DBG("prop: %s (%d)", bt_property_type_t2str(type), type); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + /* type match IPC type */ + cmd.type = type; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_ADAPTER_PROP, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int set_adapter_property(const bt_property_t *property) +{ + char buf[IPC_MTU]; + struct hal_cmd_set_adapter_prop *cmd = (void *) buf; + size_t len; + + DBG("prop: %s", btproperty2str(property)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + adapter_prop_from_hal(property, &cmd->type, &cmd->len, cmd->val); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SET_ADAPTER_PROP, + len, cmd, NULL, NULL, NULL); +} + +static int get_remote_device_properties(bt_bdaddr_t *remote_addr) +{ + struct hal_cmd_get_remote_device_props cmd; + + DBG("bdaddr: %s", bdaddr2str(remote_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROPS, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int get_remote_device_property(bt_bdaddr_t *remote_addr, + bt_property_type_t type) +{ + struct hal_cmd_get_remote_device_prop cmd; + + DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr), + bt_property_type_t2str(type)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); + + /* type match IPC type */ + cmd.type = type; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROP, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int set_remote_device_property(bt_bdaddr_t *remote_addr, + const bt_property_t *property) +{ + char buf[IPC_MTU]; + struct hal_cmd_set_remote_device_prop *cmd = (void *) buf; + size_t len; + + DBG("bdaddr: %s prop: %s", bdaddr2str(remote_addr), + bt_property_type_t2str(property->type)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd->bdaddr, remote_addr, sizeof(cmd->bdaddr)); + + /* type match IPC type */ + cmd->type = property->type; + cmd->len = property->len; + memcpy(cmd->val, property->val, property->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_REMOTE_DEVICE_PROP, + len, cmd, NULL, NULL, NULL); +} + +static int get_remote_service_record(bt_bdaddr_t *remote_addr, bt_uuid_t *uuid) +{ + struct hal_cmd_get_remote_service_rec cmd; + + DBG("bdaddr: %s", bdaddr2str(remote_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); + memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICE_REC, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int get_remote_services(bt_bdaddr_t *remote_addr) +{ + struct hal_cmd_get_remote_services cmd; + + DBG("bdaddr: %s", bdaddr2str(remote_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, remote_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_GET_REMOTE_SERVICES, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int start_discovery(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY, 0, + NULL, NULL, NULL, NULL); +} + +static int cancel_discovery(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_DISCOVERY, 0, + NULL, NULL, NULL, NULL); +} + +static int create_bond(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_create_bond cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CREATE_BOND, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int cancel_bond(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_cancel_bond cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_CANCEL_BOND, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int remove_bond(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_remove_bond cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_REMOVE_BOND, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int pin_reply(const bt_bdaddr_t *bd_addr, uint8_t accept, + uint8_t pin_len, bt_pin_code_t *pin_code) +{ + struct hal_cmd_pin_reply cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + cmd.accept = accept; + cmd.pin_len = pin_len; + memcpy(cmd.pin_code, pin_code, sizeof(cmd.pin_code)); + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_PIN_REPLY, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int ssp_reply(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant, + uint8_t accept, uint32_t passkey) +{ + struct hal_cmd_ssp_reply cmd; + + DBG("bdaddr: %s", bdaddr2str(bd_addr)); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + /* type match IPC type */ + cmd.ssp_variant = variant; + cmd.accept = accept; + cmd.passkey = passkey; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_SSP_REPLY, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static const void *get_profile_interface(const char *profile_id) +{ + DBG("%s", profile_id); + + if (!interface_ready()) + return NULL; + + if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID)) + return bt_get_socket_interface(); + + if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID)) + return bt_get_hidhost_interface(); + + if (!strcmp(profile_id, BT_PROFILE_PAN_ID)) + return bt_get_pan_interface(); + + if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID)) + return bt_get_a2dp_interface(); + + if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID)) + return bt_get_avrcp_interface(); + + if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID)) + return bt_get_handsfree_interface(); + + if (!strcmp(profile_id, BT_PROFILE_GATT_ID)) + return bt_get_gatt_interface(); + + if (!strcmp(profile_id, BT_PROFILE_HEALTH_ID)) + return bt_get_health_interface(); + + return NULL; +} + +static int dut_mode_configure(uint8_t enable) +{ + struct hal_cmd_dut_mode_conf cmd; + + DBG("enable %u", enable); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.enable = enable; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_CONF, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int dut_mode_send(uint16_t opcode, uint8_t *buf, uint8_t buf_len) +{ + char cmd_buf[IPC_MTU]; + struct hal_cmd_dut_mode_send *cmd = (void *) cmd_buf; + size_t len; + + DBG("opcode %u len %u", opcode, buf_len); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->opcode = opcode; + cmd->len = buf_len; + memcpy(cmd->data, buf, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_DUT_MODE_SEND, + len, cmd, NULL, NULL, NULL); +} + +static int le_test_mode(uint16_t opcode, uint8_t *buf, uint8_t buf_len) +{ + char cmd_buf[IPC_MTU]; + struct hal_cmd_le_test_mode *cmd = (void *) cmd_buf; + size_t len; + + DBG("opcode %u len %u", opcode, buf_len); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->opcode = opcode; + cmd->len = buf_len; + memcpy(cmd->data, buf, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_OP_LE_TEST_MODE, + len, cmd, NULL, NULL, NULL); +} + +static int config_hci_snoop_log(uint8_t enable) +{ + const char *property; + + DBG("enable %u", enable); + + property = enable ? "bluetooth.start" : "bluetooth.stop"; + + if (property_set(property, "snoop") < 0) { + error("Failed to set %s=snoop", property); + return BT_STATUS_FAIL; + } + + return BT_STATUS_SUCCESS; +} + +static const bt_interface_t bluetooth_if = { + .size = sizeof(bt_interface_t), + .init = init, + .enable = enable, + .disable = disable, + .cleanup = cleanup, + .get_adapter_properties = get_adapter_properties, + .get_adapter_property = get_adapter_property, + .set_adapter_property = set_adapter_property, + .get_remote_device_properties = get_remote_device_properties, + .get_remote_device_property = get_remote_device_property, + .set_remote_device_property = set_remote_device_property, + .get_remote_service_record = get_remote_service_record, + .get_remote_services = get_remote_services, + .start_discovery = start_discovery, + .cancel_discovery = cancel_discovery, + .create_bond = create_bond, + .remove_bond = remove_bond, + .cancel_bond = cancel_bond, + .pin_reply = pin_reply, + .ssp_reply = ssp_reply, + .get_profile_interface = get_profile_interface, + .dut_mode_configure = dut_mode_configure, + .dut_mode_send = dut_mode_send, + .le_test_mode = le_test_mode, + .config_hci_snoop_log = config_hci_snoop_log, +}; + +static const bt_interface_t *get_bluetooth_interface(void) +{ + DBG(""); + + return &bluetooth_if; +} + +static int close_bluetooth(struct hw_device_t *device) +{ + DBG(""); + + cleanup(); + + free(device); + + return 0; +} + +static int open_bluetooth(const struct hw_module_t *module, char const *name, + struct hw_device_t **device) +{ + bluetooth_device_t *dev = malloc(sizeof(bluetooth_device_t)); + + DBG(""); + + memset(dev, 0, sizeof(bluetooth_device_t)); + dev->common.tag = HARDWARE_DEVICE_TAG; + dev->common.version = 0; + dev->common.module = (struct hw_module_t *) module; + dev->common.close = close_bluetooth; + dev->get_bluetooth_interface = get_bluetooth_interface; + + *device = (struct hw_device_t *) dev; + + return 0; +} + +static struct hw_module_methods_t bluetooth_module_methods = { + .open = open_bluetooth, +}; + +struct hw_module_t HAL_MODULE_INFO_SYM = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = BT_HARDWARE_MODULE_ID, + .name = "BlueZ Bluetooth stack", + .author = "Intel Corporation", + .methods = &bluetooth_module_methods +}; diff -Nru bluez-4.101/android/hal-gatt.c bluez-5.23/android/hal-gatt.c --- bluez-4.101/android/hal-gatt.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-gatt.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,1416 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" +#include "hal-utils.h" + +static const btgatt_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void gatt_id_from_hal(btgatt_gatt_id_t *to, + struct hal_gatt_gatt_id *from) +{ + memcpy(&to->uuid, from->uuid, sizeof(to->uuid)); + to->inst_id = from->inst_id; +} + +static void gatt_id_to_hal(struct hal_gatt_gatt_id *to, btgatt_gatt_id_t *from) +{ + memcpy(to->uuid, &from->uuid, sizeof(from->uuid)); + to->inst_id = from->inst_id; +} + +static void srvc_id_from_hal(btgatt_srvc_id_t *to, + struct hal_gatt_srvc_id *from) +{ + memcpy(&to->id.uuid, from->uuid, sizeof(to->id.uuid)); + to->id.inst_id = from->inst_id; + to->is_primary = from->is_primary; +} + +static void srvc_id_to_hal(struct hal_gatt_srvc_id *to, btgatt_srvc_id_t *from) +{ + memcpy(to->uuid, &from->id.uuid, sizeof(from->id.uuid)); + to->inst_id = from->id.inst_id; + to->is_primary = from->is_primary; +} + +/* Client Event Handlers */ + +static void handle_register_client(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_register_client *ev = buf; + + if (cbs->client->register_client_cb) + cbs->client->register_client_cb(ev->status, ev->client_if, + (bt_uuid_t *) ev->app_uuid); +} + +static void handle_scan_result(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_scan_result *ev = buf; + uint8_t ad[62]; + + if (len != sizeof(*ev) + ev->len ) { + error("gatt: invalid scan result event, aborting"); + exit(EXIT_FAILURE); + } + + /* Java assumes that passed data has 62 bytes */ + memset(ad, 0, sizeof(ad)); + memcpy(ad, ev->adv_data, ev->len > sizeof(ad) ? sizeof(ad) : ev->len); + + if (cbs->client->scan_result_cb) + cbs->client->scan_result_cb((bt_bdaddr_t *) ev->bda, ev->rssi, + ad); +} + +static void handle_connect(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_connect *ev = buf; + + if (cbs->client->open_cb) + cbs->client->open_cb(ev->conn_id, ev->status, ev->client_if, + (bt_bdaddr_t *) ev->bda); +} + +static void handle_disconnect(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_disconnect *ev = buf; + + if (cbs->client->close_cb) + cbs->client->close_cb(ev->conn_id, ev->status, ev->client_if, + (bt_bdaddr_t *) ev->bda); +} + +static void handle_search_complete(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_search_complete *ev = buf; + + if (cbs->client->search_complete_cb) + cbs->client->search_complete_cb(ev->conn_id, ev->status); +} + +static void handle_search_result(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_search_result *ev = buf; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + + if (cbs->client->search_result_cb) + cbs->client->search_result_cb(ev->conn_id, &srvc_id); +} + +static void handle_get_characteristic(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_get_characteristic *ev = buf; + btgatt_gatt_id_t char_id; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + gatt_id_from_hal(&char_id, &ev->char_id); + + if (cbs->client->get_characteristic_cb) + cbs->client->get_characteristic_cb(ev->conn_id, ev->status, + &srvc_id, &char_id, + ev->char_prop); +} + +static void handle_get_descriptor(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_get_descriptor *ev = buf; + btgatt_gatt_id_t descr_id; + btgatt_gatt_id_t char_id; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + gatt_id_from_hal(&char_id, &ev->char_id); + gatt_id_from_hal(&descr_id, &ev->descr_id); + + if (cbs->client->get_descriptor_cb) + cbs->client->get_descriptor_cb(ev->conn_id, ev->status, + &srvc_id, &char_id, &descr_id); +} + +static void handle_get_included_service(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_get_inc_service *ev = buf; + btgatt_srvc_id_t srvc_id; + btgatt_srvc_id_t incl_srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + srvc_id_from_hal(&incl_srvc_id, &ev->incl_srvc_id); + + if (cbs->client->get_included_service_cb) + cbs->client->get_included_service_cb(ev->conn_id, ev->status, + &srvc_id, + &incl_srvc_id); +} + +static void handle_register_for_notification(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_reg_for_notif *ev = buf; + btgatt_gatt_id_t char_id; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + gatt_id_from_hal(&char_id, &ev->char_id); + + if (cbs->client->register_for_notification_cb) + cbs->client->register_for_notification_cb(ev->conn_id, + ev->registered, + ev->status, + &srvc_id, + &char_id); +} + +static void handle_notify(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_notify *ev = buf; + btgatt_notify_params_t params; + + if (len != sizeof(*ev) + ev->len ) { + error("gatt: invalid notify event, aborting"); + exit(EXIT_FAILURE); + } + + memset(¶ms, 0, sizeof(params)); + memcpy(params.value, ev->value, ev->len); + memcpy(¶ms.bda, ev->bda, sizeof(params.bda)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->char_id); + + params.len = ev->len; + params.is_notify = ev->is_notify; + + if (cbs->client->notify_cb) + cbs->client->notify_cb(ev->conn_id, ¶ms); +} + +static void handle_read_characteristic(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_read_characteristic *ev = buf; + btgatt_read_params_t params; + + if (len != sizeof(*ev) + ev->data.len ) { + error("gatt: invalid read characteristic event, aborting"); + exit(EXIT_FAILURE); + } + + memset(¶ms, 0, sizeof(params)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); + gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); + + memcpy(¶ms.value.value, ev->data.value, ev->data.len); + + params.value_type = ev->data.value_type; + params.value.len = ev->data.len; + params.status = ev->data.status; + + if (cbs->client->read_characteristic_cb) + cbs->client->read_characteristic_cb(ev->conn_id, ev->status, + ¶ms); +} + +static void handle_write_characteristic(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_write_characteristic *ev = buf; + btgatt_write_params_t params; + + memset(¶ms, 0, sizeof(params)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); + gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); + + params.status = ev->data.status; + + if (cbs->client->write_characteristic_cb) + cbs->client->write_characteristic_cb(ev->conn_id, ev->status, + ¶ms); +} + +static void handle_read_descriptor(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_read_descriptor *ev = buf; + btgatt_read_params_t params; + + if (len != sizeof(*ev) + ev->data.len ) { + error("gatt: invalid read descriptor event, aborting"); + exit(EXIT_FAILURE); + } + + memset(¶ms, 0, sizeof(params)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); + gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); + + memcpy(¶ms.value.value, ev->data.value, ev->data.len); + + params.value_type = ev->data.value_type; + params.value.len = ev->data.len; + params.status = ev->data.status; + + if (cbs->client->read_descriptor_cb) + cbs->client->read_descriptor_cb(ev->conn_id, ev->status, + ¶ms); +} + +static void handle_write_descriptor(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_write_descriptor *ev = buf; + btgatt_write_params_t params; + + memset(¶ms, 0, sizeof(params)); + + srvc_id_from_hal(¶ms.srvc_id, &ev->data.srvc_id); + gatt_id_from_hal(¶ms.char_id, &ev->data.char_id); + gatt_id_from_hal(¶ms.descr_id, &ev->data.descr_id); + + params.status = ev->data.status; + + if (cbs->client->write_descriptor_cb) + cbs->client->write_descriptor_cb(ev->conn_id, ev->status, + ¶ms); +} + +static void handle_execute_write(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_exec_write *ev = buf; + + if (cbs->client->execute_write_cb) + cbs->client->execute_write_cb(ev->conn_id, ev->status); +} + +static void handle_read_remote_rssi(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_read_remote_rssi *ev = buf; + + if (cbs->client->read_remote_rssi_cb) + cbs->client->read_remote_rssi_cb(ev->client_if, + (bt_bdaddr_t *) ev->address, + ev->rssi, ev->status); +} + +static void handle_listen(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_client_listen *ev = buf; + + if (cbs->client->listen_cb) + cbs->client->listen_cb(ev->status, ev->server_if); +} + +/* Server Event Handlers */ + +static void handle_register_server(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_register *ev = buf; + + if (cbs->server->register_server_cb) + cbs->server->register_server_cb(ev->status, ev->server_if, + (bt_uuid_t *) &ev->uuid); +} + +static void handle_connection(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_connection *ev = buf; + + if (cbs->server->connection_cb) + cbs->server->connection_cb(ev->conn_id, ev->server_if, + ev->connected, + (bt_bdaddr_t *) &ev->bdaddr); +} + +static void handle_service_added(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_service_added *ev = buf; + btgatt_srvc_id_t srvc_id; + + srvc_id_from_hal(&srvc_id, &ev->srvc_id); + + if (cbs->server->service_added_cb) + cbs->server->service_added_cb(ev->status, ev->server_if, + &srvc_id, ev->srvc_handle); +} + +static void handle_included_service_added(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_inc_srvc_added *ev = buf; + + if (cbs->server->included_service_added_cb) + cbs->server->included_service_added_cb(ev->status, + ev->server_if, + ev->srvc_handle, + ev->incl_srvc_handle); +} + +static void handle_characteristic_added(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_characteristic_added *ev = buf; + + if (cbs->server->characteristic_added_cb) + cbs->server->characteristic_added_cb(ev->status, ev->server_if, + (bt_uuid_t *) &ev->uuid, + ev->srvc_handle, + ev->char_handle); +} + +static void handle_descriptor_added(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_descriptor_added *ev = buf; + + if (cbs->server->descriptor_added_cb) + cbs->server->descriptor_added_cb(ev->status, ev->server_if, + (bt_uuid_t *) &ev->uuid, + ev->srvc_handle, + ev->descr_handle); +} + +static void handle_service_started(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_service_started *ev = buf; + + if (cbs->server->service_started_cb) + cbs->server->service_started_cb(ev->status, ev->server_if, + ev->srvc_handle); +} + +static void handle_service_stopped(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_service_stopped *ev = buf; + + if (cbs->server->service_stopped_cb) + cbs->server->service_stopped_cb(ev->status, ev->server_if, + ev->srvc_handle); +} + +static void handle_service_deleted(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_service_deleted *ev = buf; + + if (cbs->server->service_deleted_cb) + cbs->server->service_deleted_cb(ev->status, ev->server_if, + ev->srvc_handle); +} + +static void handle_request_read(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_request_read *ev = buf; + + if (cbs->server->request_read_cb) + cbs->server->request_read_cb(ev->conn_id, ev->trans_id, + (bt_bdaddr_t *) &ev->bdaddr, + ev->attr_handle, ev->offset, + ev->is_long); +} + +static void handle_request_write(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_request_write *ev = buf; + + if (len != sizeof(*ev) + ev->length ) { + error("gatt: invalid request write event, aborting"); + exit(EXIT_FAILURE); + } + + if (cbs->server->request_write_cb) + cbs->server->request_write_cb(ev->conn_id, ev->trans_id, + (bt_bdaddr_t *) ev->bdaddr, + ev->attr_handle, ev->offset, + ev->length, ev->need_rsp, + ev->is_prep, ev->value); +} + +static void handle_request_exec_write(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_request_exec_write *ev = buf; + + if (cbs->server->request_exec_write_cb) + cbs->server->request_exec_write_cb(ev->conn_id, ev->trans_id, + (bt_bdaddr_t *) ev->bdaddr, + ev->exec_write); +} + +static void handle_response_confirmation(void *buf, uint16_t len, int fd) +{ + struct hal_ev_gatt_server_rsp_confirmation *ev = buf; + + if (cbs->server->response_confirmation_cb) + cbs->server->response_confirmation_cb(ev->status, ev->handle); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_GATT_CLIENT_REGISTER_CLIENT */ + { handle_register_client, false, + sizeof(struct hal_ev_gatt_client_register_client) }, + /* HAL_EV_GATT_CLIENT_SCAN_RESULT */ + { handle_scan_result, true, + sizeof(struct hal_ev_gatt_client_scan_result) }, + /* HAL_EV_GATT_CLIENT_CONNECT */ + { handle_connect, false, sizeof(struct hal_ev_gatt_client_connect) }, + /* HAL_EV_GATT_CLIENT_DISCONNECT */ + { handle_disconnect, false, + sizeof(struct hal_ev_gatt_client_disconnect) }, + /* HAL_EV_GATT_CLIENT_SEARCH_COMPLETE */ + { handle_search_complete, false, + sizeof(struct hal_ev_gatt_client_search_complete) }, + /* HAL_EV_GATT_CLIENT_SEARCH_RESULT */ + { handle_search_result, false, + sizeof(struct hal_ev_gatt_client_search_result) }, + /* HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC */ + { handle_get_characteristic, false, + sizeof(struct hal_ev_gatt_client_get_characteristic) }, + /* HAL_EV_GATT_CLIENT_GET_DESCRIPTOR */ + { handle_get_descriptor, false, + sizeof(struct hal_ev_gatt_client_get_descriptor) }, + /* HAL_EV_GATT_CLIENT_GET_INC_SERVICE */ + { handle_get_included_service, false, + sizeof(struct hal_ev_gatt_client_get_inc_service) }, + /* HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF */ + { handle_register_for_notification, false, + sizeof(struct hal_ev_gatt_client_reg_for_notif) }, + /* HAL_EV_GATT_CLIENT_NOTIFY */ + { handle_notify, true, sizeof(struct hal_ev_gatt_client_notify) }, + /* HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC */ + { handle_read_characteristic, true, + sizeof(struct hal_ev_gatt_client_read_characteristic) }, + /* HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC */ + { handle_write_characteristic, false, + sizeof(struct hal_ev_gatt_client_write_characteristic) }, + /* HAL_EV_GATT_CLIENT_READ_DESCRIPTOR */ + { handle_read_descriptor, true, + sizeof(struct hal_ev_gatt_client_read_descriptor) }, + /* HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR */ + { handle_write_descriptor, false, + sizeof(struct hal_ev_gatt_client_write_descriptor) }, + /* HAL_EV_GATT_CLIENT_EXEC_WRITE */ + { handle_execute_write, false, + sizeof(struct hal_ev_gatt_client_exec_write) }, + /* HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI */ + { handle_read_remote_rssi, false, + sizeof(struct hal_ev_gatt_client_read_remote_rssi) }, + /* HAL_EV_GATT_CLIENT_LISTEN */ + { handle_listen, false, sizeof(struct hal_ev_gatt_client_listen) }, + /* HAL_EV_GATT_SERVER_REGISTER */ + { handle_register_server, false, + sizeof(struct hal_ev_gatt_server_register) }, + /* HAL_EV_GATT_SERVER_CONNECTION */ + { handle_connection, false, + sizeof(struct hal_ev_gatt_server_connection) }, + /* HAL_EV_GATT_SERVER_SERVICE_ADDED */ + { handle_service_added, false, + sizeof(struct hal_ev_gatt_server_service_added) }, + /* HAL_EV_GATT_SERVER_INC_SRVC_ADDED */ + { handle_included_service_added, false, + sizeof(struct hal_ev_gatt_server_inc_srvc_added) }, + /* HAL_EV_GATT_SERVER_CHAR_ADDED */ + { handle_characteristic_added, false, + sizeof(struct hal_ev_gatt_server_characteristic_added) }, + /* HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED */ + { handle_descriptor_added, false, + sizeof(struct hal_ev_gatt_server_descriptor_added) }, + /* HAL_EV_GATT_SERVER_SERVICE_STARTED */ + { handle_service_started, false, + sizeof(struct hal_ev_gatt_server_service_started) }, + /* HAL_EV_GATT_SERVER_SERVICE_STOPPED */ + { handle_service_stopped, false, + sizeof(struct hal_ev_gatt_server_service_stopped) }, + /* HAL_EV_GATT_SERVER_SERVICE_DELETED */ + { handle_service_deleted, false, + sizeof(struct hal_ev_gatt_server_service_deleted) }, + /* HAL_EV_GATT_SERVER_REQUEST_READ */ + { handle_request_read, false, + sizeof(struct hal_ev_gatt_server_request_read) }, + /* HAL_EV_GATT_SERVER_REQUEST_WRITE */ + { handle_request_write, true, + sizeof(struct hal_ev_gatt_server_request_write) }, + /* HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE */ + { handle_request_exec_write, false, + sizeof(struct hal_ev_gatt_server_request_exec_write) }, + /* HAL_EV_GATT_SERVER_RSP_CONFIRMATION */ + { handle_response_confirmation, false, + sizeof(struct hal_ev_gatt_server_rsp_confirmation) }, +}; + +/* Client API */ + +static bt_status_t register_client(bt_uuid_t *uuid) +{ + struct hal_cmd_gatt_client_register cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.uuid, uuid, sizeof(*uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REGISTER, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t unregister_client(int client_if) +{ + struct hal_cmd_gatt_client_unregister cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_UNREGISTER, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t scan(int client_if, bool start) +{ + struct hal_cmd_gatt_client_scan cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + cmd.start = start; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t connect(int client_if, const bt_bdaddr_t *bd_addr, + bool is_direct) +{ + struct hal_cmd_gatt_client_connect cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + cmd.is_direct = is_direct; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t disconnect(int client_if, const bt_bdaddr_t *bd_addr, + int conn_id) +{ + struct hal_cmd_gatt_client_disconnect cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + cmd.conn_id = conn_id; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_DISCONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t listen(int client_if, bool start) +{ + struct hal_cmd_gatt_client_listen cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + cmd.start = start; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_LISTEN, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t refresh(int client_if, const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_gatt_client_refresh cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_REFRESH, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t search_service(int conn_id, bt_uuid_t *filter_uuid) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_search_service *cmd = (void *) buf; + size_t len = sizeof(*cmd); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->conn_id = conn_id; + + if (filter_uuid) { + memcpy(cmd->filter_uuid, filter_uuid, sizeof(*filter_uuid)); + len += sizeof(*filter_uuid); + cmd->filtered = 1; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_SEARCH_SERVICE, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t get_included_service(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_srvc_id_t *start_incl_srvc_id) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_get_included_service *cmd = (void *) buf; + size_t len = sizeof(*cmd); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + cmd->continuation = 0; + + if (start_incl_srvc_id) { + srvc_id_to_hal(&cmd->incl_srvc_id[0], start_incl_srvc_id); + len += sizeof(cmd->incl_srvc_id[0]); + cmd->continuation = 1; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t get_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *start_char_id) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_get_characteristic *cmd = (void *) buf; + size_t len = sizeof(*cmd); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + cmd->continuation = 0; + + if (start_char_id) { + gatt_id_to_hal(&cmd->char_id[0], start_char_id); + len += sizeof(cmd->char_id[0]); + cmd->continuation = 1; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t get_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *start_descr_id) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_get_descriptor *cmd = (void *) buf; + size_t len = sizeof(*cmd); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + gatt_id_to_hal(&cmd->char_id, char_id); + cmd->continuation = 0; + + if (start_descr_id) { + gatt_id_to_hal(&cmd->descr_id[0], start_descr_id); + len += sizeof(cmd->descr_id[0]); + cmd->continuation = 1; + } + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DESCRIPTOR, + len, cmd, NULL , NULL, NULL); +} + +static bt_status_t read_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + int auth_req) +{ + struct hal_cmd_gatt_client_read_characteristic cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.conn_id = conn_id; + cmd.auth_req = auth_req; + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + gatt_id_to_hal(&cmd.char_id, char_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t write_characteristic(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + int write_type, int len, int auth_req, + char *p_value) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_write_characteristic *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + len; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + cmd->write_type = write_type; + cmd->len = len; + cmd->auth_req = auth_req; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + gatt_id_to_hal(&cmd->char_id, char_id); + + memcpy(cmd->value, p_value, len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC, + cmd_len, cmd, NULL, NULL, NULL); +} + +static bt_status_t read_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, + int auth_req) +{ + struct hal_cmd_gatt_client_read_descriptor cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.conn_id = conn_id; + cmd.auth_req = auth_req; + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + gatt_id_to_hal(&cmd.char_id, char_id); + gatt_id_to_hal(&cmd.descr_id, descr_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_DESCRIPTOR, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t write_descriptor(int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, + int write_type, int len, int auth_req, + char *p_value) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_write_descriptor *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + len; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + cmd->write_type = write_type; + cmd->len = len; + cmd->auth_req = auth_req; + + srvc_id_to_hal(&cmd->srvc_id, srvc_id); + gatt_id_to_hal(&cmd->char_id, char_id); + gatt_id_to_hal(&cmd->descr_id, descr_id); + + memcpy(cmd->value, p_value, len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR, + cmd_len, cmd, NULL, NULL, NULL); +} + +static bt_status_t execute_write(int conn_id, int execute) +{ + struct hal_cmd_gatt_client_execute_write cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.conn_id = conn_id; + cmd.execute = execute; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_EXECUTE_WRITE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t register_for_notification(int client_if, + const bt_bdaddr_t *bd_addr, + btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id) +{ + struct hal_cmd_gatt_client_register_for_notification cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + gatt_id_to_hal(&cmd.char_id, char_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t deregister_for_notification(int client_if, + const bt_bdaddr_t *bd_addr, + btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id) +{ + struct hal_cmd_gatt_client_deregister_for_notification cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + gatt_id_to_hal(&cmd.char_id, char_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t read_remote_rssi(int client_if, const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_gatt_client_read_remote_rssi cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.client_if = client_if; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int get_device_type(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_gatt_client_get_device_type cmd; + uint8_t dev_type; + size_t resp_len = sizeof(dev_type); + bt_status_t status; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + status = hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE, + sizeof(cmd), &cmd, &resp_len, &dev_type, NULL); + + if (status != BT_STATUS_SUCCESS || resp_len != sizeof(dev_type)) + return 0; + + return dev_type; +} + +static bt_status_t set_adv_data_real(int server_if, bool set_scan_rsp, + bool include_name, bool include_txpower, + int min_interval, int max_interval, + int appearance, uint16_t manufacturer_len, + char *manufacturer_data, + uint16_t service_data_len, char *service_data, + uint16_t service_uuid_len, char *service_uuid) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_client_set_adv_data *cmd = (void *) buf; + size_t cmd_len; + uint8_t *data; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd_len = sizeof(*cmd) + manufacturer_len + service_data_len + + service_uuid_len; + + if (cmd_len > IPC_MTU) + return BT_STATUS_FAIL; + + cmd->server_if = server_if; + cmd->set_scan_rsp = set_scan_rsp; + cmd->include_name = include_name; + cmd->include_txpower = include_txpower; + cmd->min_interval = min_interval; + cmd->max_interval = max_interval; + cmd->appearance = appearance; + cmd->manufacturer_len = manufacturer_len; + cmd->service_data_len = service_data_len; + cmd->service_uuid_len = service_uuid_len; + + data = cmd->data; + + if (manufacturer_data && manufacturer_len) { + memcpy(data, manufacturer_data, manufacturer_len); + data += manufacturer_len; + } + + if (service_data && service_data_len) { + memcpy(data, service_data, service_data_len); + data += service_data_len; + } + + if (service_uuid && service_uuid_len) + memcpy(data, service_uuid, service_uuid_len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SET_ADV_DATA, + cmd_len, cmd, NULL, NULL, NULL); +} + +/* + * This is temporary solution and support for older Android versions might + * be removed at any time. + */ +#if ANDROID_VERSION < PLATFORM_VER(4,4,3) +static bt_status_t set_adv_data(int server_if, bool set_scan_rsp, + bool include_name, bool include_txpower, + int min_interval, int max_interval, + int appearance, uint16_t manufacturer_len, + char *manufacturer_data) +{ + return set_adv_data_real(server_if, set_scan_rsp, include_name, + include_txpower, min_interval, + max_interval, appearance, + manufacturer_len, manufacturer_data, + 0, NULL, 0, NULL); +} +#else +static bt_status_t set_adv_data(int server_if, bool set_scan_rsp, + bool include_name, bool include_txpower, + int min_interval, int max_interval, + int appearance, uint16_t manufacturer_len, + char *manufacturer_data, + uint16_t service_data_len, char *service_data, + uint16_t service_uuid_len, char *service_uuid) +{ + return set_adv_data_real(server_if, set_scan_rsp, include_name, + include_txpower, min_interval, + max_interval, appearance, + manufacturer_len, manufacturer_data, + service_data_len, service_data, + service_uuid_len, service_uuid); +} +#endif + +static bt_status_t test_command(int command, btgatt_test_params_t *params) +{ + struct hal_cmd_gatt_client_test_command cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.command = command; + + memcpy(cmd.bda1, params->bda1, sizeof(*params->bda1)); + memcpy(cmd.uuid1, params->uuid1, sizeof(*params->uuid1)); + + cmd.u1 = params->u1; + cmd.u2 = params->u2; + cmd.u3 = params->u3; + cmd.u4 = params->u4; + cmd.u5 = params->u5; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_TEST_COMMAND, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +/* Server API */ + +static bt_status_t register_server(bt_uuid_t *uuid) +{ + struct hal_cmd_gatt_server_register cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.uuid, uuid, sizeof(*uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_REGISTER, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t unregister_server(int server_if) +{ + struct hal_cmd_gatt_server_unregister cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_UNREGISTER, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t server_connect(int server_if, const bt_bdaddr_t *bd_addr, + bool is_direct) +{ + struct hal_cmd_gatt_server_connect cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.is_direct = is_direct; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t server_disconnect(int server_if, const bt_bdaddr_t *bd_addr, + int conn_id) +{ + struct hal_cmd_gatt_server_disconnect cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.conn_id = conn_id; + + memcpy(cmd.bdaddr, bd_addr, sizeof(*bd_addr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_DISCONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t add_service(int server_if, btgatt_srvc_id_t *srvc_id, + int num_handles) +{ + struct hal_cmd_gatt_server_add_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.num_handles = num_handles; + + srvc_id_to_hal(&cmd.srvc_id, srvc_id); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_ADD_SERVICE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t add_included_service(int server_if, int service_handle, + int included_handle) +{ + struct hal_cmd_gatt_server_add_inc_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + cmd.included_handle = included_handle; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_INC_SERVICE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t add_characteristic(int server_if, int service_handle, + bt_uuid_t *uuid, int properties, + int permissions) +{ + struct hal_cmd_gatt_server_add_characteristic cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + cmd.properties = properties; + cmd.permissions = permissions; + + memcpy(cmd.uuid, uuid, sizeof(*uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t add_descriptor(int server_if, int service_handle, + bt_uuid_t *uuid, int permissions) +{ + struct hal_cmd_gatt_server_add_descriptor cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + cmd.permissions = permissions; + + memcpy(cmd.uuid, uuid, sizeof(*uuid)); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_ADD_DESCRIPTOR, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t start_service(int server_if, int service_handle, + int transport) +{ + struct hal_cmd_gatt_server_start_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + cmd.transport = transport; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_START_SERVICE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t stop_service(int server_if, int service_handle) +{ + struct hal_cmd_gatt_server_stop_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_STOP_SERVICE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t delete_service(int server_if, int service_handle) +{ + struct hal_cmd_gatt_server_delete_service cmd; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.server_if = server_if; + cmd.service_handle = service_handle; + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_DELETE_SERVICE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t send_indication(int server_if, int attribute_handle, + int conn_id, int len, int confirm, + char *p_value) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_server_send_indication *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + len; + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->server_if = server_if; + cmd->attribute_handle = attribute_handle; + cmd->conn_id = conn_id; + cmd->len = len; + cmd->confirm = confirm; + + memcpy(cmd->value, p_value, len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_SEND_INDICATION, + cmd_len, cmd, NULL, NULL, NULL); +} + +static bt_status_t send_response(int conn_id, int trans_id, int status, + btgatt_response_t *response) +{ + char buf[IPC_MTU]; + struct hal_cmd_gatt_server_send_response *cmd = (void *) buf; + size_t cmd_len = sizeof(*cmd) + sizeof(*response); + + memset(buf, 0 , IPC_MTU); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->conn_id = conn_id; + cmd->trans_id = trans_id; + cmd->status = status; + cmd->handle = response->attr_value.handle; + cmd->offset = response->attr_value.offset; + cmd->auth_req = response->attr_value.auth_req; + cmd->len = response->attr_value.len; + + memcpy(cmd->data, response->attr_value.value, cmd->len); + + return hal_ipc_cmd(HAL_SERVICE_ID_GATT, + HAL_OP_GATT_SERVER_SEND_RESPONSE, + cmd_len, cmd, NULL, NULL, NULL); +} + +static bt_status_t init(const btgatt_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + cbs = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_GATT, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_GATT; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_GATT); + } + + return ret; +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_GATT; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_GATT); +} + +static btgatt_client_interface_t client_iface = { + .register_client = register_client, + .unregister_client = unregister_client, + .scan = scan, + .connect = connect, + .disconnect = disconnect, + .listen = listen, + .refresh = refresh, + .search_service = search_service, + .get_included_service = get_included_service, + .get_characteristic = get_characteristic, + .get_descriptor = get_descriptor, + .read_characteristic = read_characteristic, + .write_characteristic = write_characteristic, + .read_descriptor = read_descriptor, + .write_descriptor = write_descriptor, + .execute_write = execute_write, + .register_for_notification = register_for_notification, + .deregister_for_notification = deregister_for_notification, + .read_remote_rssi = read_remote_rssi, + .get_device_type = get_device_type, + .set_adv_data = set_adv_data, + .test_command = test_command, +}; + +static btgatt_server_interface_t server_iface = { + .register_server = register_server, + .unregister_server = unregister_server, + .connect = server_connect, + .disconnect = server_disconnect, + .add_service = add_service, + .add_included_service = add_included_service, + .add_characteristic = add_characteristic, + .add_descriptor = add_descriptor, + .start_service = start_service, + .stop_service = stop_service, + .delete_service = delete_service, + .send_indication = send_indication, + .send_response = send_response, +}; + +static btgatt_interface_t iface = { + .size = sizeof(iface), + .init = init, + .cleanup = cleanup, + .client = &client_iface, + .server = &server_iface, +}; + +btgatt_interface_t *bt_get_gatt_interface(void) +{ + return &iface; +} diff -Nru bluez-4.101/android/hal.h bluez-5.23/android/hal.h --- bluez-4.101/android/hal.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal.h 2014-03-25 20:53:41.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +btsock_interface_t *bt_get_socket_interface(void); +bthh_interface_t *bt_get_hidhost_interface(void); +btpan_interface_t *bt_get_pan_interface(void); +btav_interface_t *bt_get_a2dp_interface(void); +btrc_interface_t *bt_get_avrcp_interface(void); +bthf_interface_t *bt_get_handsfree_interface(void); +btgatt_interface_t *bt_get_gatt_interface(void); +bthl_interface_t *bt_get_health_interface(void); + +void bt_thread_associate(void); +void bt_thread_disassociate(void); diff -Nru bluez-4.101/android/hal-handsfree.c bluez-5.23/android/hal-handsfree.c --- bluez-4.101/android/hal-handsfree.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-handsfree.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" + +#define MODE_PROPERTY_NAME "persist.sys.bluetooth.handsfree" + +static const bthf_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void handle_conn_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_conn_state *ev = buf; + + if (cbs->connection_state_cb) + cbs->connection_state_cb(ev->state, + (bt_bdaddr_t *) (ev->bdaddr)); +} + +static void handle_audio_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_audio_state *ev = buf; + + if (cbs->audio_state_cb) + cbs->audio_state_cb(ev->state, (bt_bdaddr_t *) (ev->bdaddr)); +} + +static void handle_vr_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_vr_state *ev = buf; + + if (cbs->vr_cmd_cb) + cbs->vr_cmd_cb(ev->state); +} + +static void handle_answer(void *buf, uint16_t len, int fd) +{ + if (cbs->answer_call_cmd_cb) + cbs->answer_call_cmd_cb(); +} + +static void handle_hangup(void *buf, uint16_t len, int fd) +{ + if (cbs->hangup_call_cmd_cb) + cbs->hangup_call_cmd_cb(); +} + +static void handle_volume(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_volume *ev = buf; + + if (cbs->volume_cmd_cb) + cbs->volume_cmd_cb(ev->type, ev->volume); +} + +static void handle_dial(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_dial *ev = buf; + uint16_t num_len = ev->number_len; + char *number = NULL; + + if (len != sizeof(*ev) + num_len || + (num_len != 0 && ev->number[num_len - 1] != '\0')) { + error("invalid dial event, aborting"); + exit(EXIT_FAILURE); + } + + if (!cbs->dial_call_cmd_cb) + return; + + if (ev->number_len) + number = (char *) ev->number; + + cbs->dial_call_cmd_cb(number); +} + +static void handle_dtmf(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_dtmf *ev = buf; + + if (cbs->dtmf_cmd_cb) + cbs->dtmf_cmd_cb(ev->tone); +} + +static void handle_nrec(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_nrec *ev = buf; + + if (cbs->nrec_cmd_cb) + cbs->nrec_cmd_cb(ev->nrec); +} + +static void handle_chld(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_chld *ev = buf; + + if (cbs->chld_cmd_cb) + cbs->chld_cmd_cb(ev->chld); +} + +static void handle_cnum(void *buf, uint16_t len, int fd) +{ + if (cbs->cnum_cmd_cb) + cbs->cnum_cmd_cb(); +} + +static void handle_cind(void *buf, uint16_t len, int fd) +{ + if (cbs->cind_cmd_cb) + cbs->cind_cmd_cb(); +} + +static void handle_cops(void *buf, uint16_t len, int fd) +{ + if (cbs->cops_cmd_cb) + cbs->cops_cmd_cb(); +} + +static void handle_clcc(void *buf, uint16_t len, int fd) +{ + if (cbs->clcc_cmd_cb) + cbs->clcc_cmd_cb(); +} + +static void handle_unknown_at(void *buf, uint16_t len, int fd) +{ + struct hal_ev_handsfree_unknown_at *ev = buf; + + if (len != sizeof(*ev) + ev->len || + (ev->len != 0 && ev->buf[ev->len - 1] != '\0')) { + error("invalid unknown command event, aborting"); + exit(EXIT_FAILURE); + } + + if (cbs->unknown_at_cmd_cb) + cbs->unknown_at_cmd_cb((char *) ev->buf); +} + +static void handle_hsp_key_press(void *buf, uint16_t len, int fd) +{ + if (cbs->key_pressed_cmd_cb) + cbs->key_pressed_cmd_cb(); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_HANDSFREE_CONN_STATE */ + { handle_conn_state, false, + sizeof(struct hal_ev_handsfree_conn_state) }, + /* HAL_EV_HANDSFREE_AUDIO_STATE */ + { handle_audio_state, false, + sizeof(struct hal_ev_handsfree_audio_state) }, + /* HAL_EV_HANDSFREE_VR */ + { handle_vr_state, false, sizeof(struct hal_ev_handsfree_vr_state) }, + /*HAL_EV_HANDSFREE_ANSWER */ + { handle_answer, false, 0 }, + /*HAL_EV_HANDSFREE_HANGUP */ + { handle_hangup, false, 0 }, + /* HAL_EV_HANDSFREE_VOLUME */ + { handle_volume, false, sizeof(struct hal_ev_handsfree_volume) }, + /* HAL_EV_HANDSFREE_DIAL */ + { handle_dial, true, sizeof(struct hal_ev_handsfree_dial) }, + /* HAL_EV_HANDSFREE_DTMF */ + { handle_dtmf, false, sizeof(struct hal_ev_handsfree_dtmf) }, + /* HAL_EV_HANDSFREE_NREC */ + { handle_nrec, false, sizeof(struct hal_ev_handsfree_nrec) }, + /* HAL_EV_HANDSFREE_CHLD */ + { handle_chld, false, sizeof(struct hal_ev_handsfree_chld) }, + /* HAL_EV_HANDSFREE_CNUM */ + { handle_cnum, false, 0 }, + /* HAL_EV_HANDSFREE_CIND */ + { handle_cind, false, 0 }, + /* HAL_EV_HANDSFREE_COPS */ + { handle_cops, false, 0 }, + /* HAL_EV_HANDSFREE_CLCC */ + { handle_clcc, false, 0 }, + /* HAL_EV_HANDSFREE_UNKNOWN_AT */ + { handle_unknown_at, true, sizeof(struct hal_ev_handsfree_unknown_at) }, + /* HAL_EV_HANDSFREE_HSP_KEY_PRESS */ + { handle_hsp_key_press, false, 0 }, +}; + +static uint8_t get_mode(void) +{ + char value[PROPERTY_VALUE_MAX]; + + if (property_get(MODE_PROPERTY_NAME, value, "") > 0 && + (!strcasecmp(value, "hfp"))) + return HAL_MODE_HANDSFREE_HFP; + + if (property_get(MODE_PROPERTY_NAME, value, "") > 0 && + (!strcasecmp(value, "hfp_wbs"))) + return HAL_MODE_HANDSFREE_HFP_WBS; + + return HAL_MODE_HANDSFREE_HSP_ONLY; +} + +static bt_status_t init(bthf_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + cbs = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_HANDSFREE, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_HANDSFREE; + cmd.mode = get_mode(); + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE); + } + + return ret; +} + +static bt_status_t handsfree_connect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_handsfree_connect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_CONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t disconnect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_handsfree_disconnect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DISCONNECT, sizeof(cmd), &cmd, + NULL, NULL, NULL); +} + +static bt_status_t connect_audio(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_handsfree_connect_audio cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CONNECT_AUDIO, sizeof(cmd), + &cmd, NULL, NULL, NULL); +} + +static bt_status_t disconnect_audio(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_handsfree_disconnect_audio cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DISCONNECT_AUDIO, sizeof(cmd), + &cmd, NULL, NULL, NULL); +} + +static bt_status_t start_voice_recognition(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_START_VR, + 0, NULL, NULL, NULL, NULL); +} + +static bt_status_t stop_voice_recognition(void) +{ + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, HAL_OP_HANDSFREE_STOP_VR, + 0, NULL, NULL, NULL, NULL); +} + +static bt_status_t volume_control(bthf_volume_type_t type, int volume) +{ + struct hal_cmd_handsfree_volume_control cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.type = type; + cmd.volume = volume; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_VOLUME_CONTROL, sizeof(cmd), + &cmd, NULL, NULL, NULL); +} + +static bt_status_t device_status_notification(bthf_network_state_t state, + bthf_service_type_t type, + int signal, int battery) +{ + struct hal_cmd_handsfree_device_status_notif cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.state = state; + cmd.type = type; + cmd.signal = signal; + cmd.battery = battery; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t cops_response(const char *cops) +{ + char buf[IPC_MTU]; + struct hal_cmd_handsfree_cops_response *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!cops) + return BT_STATUS_PARM_INVALID; + + cmd->len = strlen(cops) + 1; + memcpy(cmd->buf, cops, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_COPS_RESPONSE, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t cind_response(int svc, int num_active, int num_held, + bthf_call_state_t state, int signal, + int roam, int batt_chg) +{ + struct hal_cmd_handsfree_cind_response cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.svc = svc; + cmd.num_active = num_active; + cmd.num_held = num_held; + cmd.state = state; + cmd.signal = signal; + cmd.roam = roam; + cmd.batt_chg = batt_chg; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CIND_RESPONSE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t formatted_at_response(const char *rsp) +{ + char buf[IPC_MTU]; + struct hal_cmd_handsfree_formatted_at_response *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!rsp) + return BT_STATUS_PARM_INVALID; + + cmd->len = strlen(rsp) + 1; + memcpy(cmd->buf, rsp, cmd->len); + + len = sizeof(*cmd) + cmd->len; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t at_response(bthf_at_response_t response, int error) +{ + struct hal_cmd_handsfree_at_response cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.response = response; + cmd.error = error; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_AT_RESPONSE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t clcc_response(int index, bthf_call_direction_t dir, + bthf_call_state_t state, + bthf_call_mode_t mode, + bthf_call_mpty_type_t mpty, + const char *number, + bthf_call_addrtype_t type) +{ + char buf[IPC_MTU]; + struct hal_cmd_handsfree_clcc_response *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->index = index; + cmd->dir = dir; + cmd->state = state; + cmd->mode = mode; + cmd->mpty = mpty; + cmd->type = type; + + if (number) { + cmd->number_len = strlen(number) + 1; + memcpy(cmd->number, number, cmd->number_len); + } else { + cmd->number_len = 0; + } + + len = sizeof(*cmd) + cmd->number_len; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CLCC_RESPONSE, + len, cmd, NULL, NULL, NULL); +} + +static bt_status_t phone_state_change(int num_active, int num_held, + bthf_call_state_t state, + const char *number, + bthf_call_addrtype_t type) +{ + char buf[IPC_MTU]; + struct hal_cmd_handsfree_phone_state_change *cmd = (void *) buf; + size_t len; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd->num_active = num_active; + cmd->num_held = num_held; + cmd->state = state; + cmd->type = type; + + if (number) { + cmd->number_len = strlen(number) + 1; + memcpy(cmd->number, number, cmd->number_len); + } else { + cmd->number_len = 0; + } + + len = sizeof(*cmd) + cmd->number_len; + + return hal_ipc_cmd(HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, + len, cmd, NULL, NULL, NULL); +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_HANDSFREE; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_HANDSFREE); +} + +static bthf_interface_t iface = { + .size = sizeof(iface), + .init = init, + .connect = handsfree_connect, + .disconnect = disconnect, + .connect_audio = connect_audio, + .disconnect_audio = disconnect_audio, + .start_voice_recognition = start_voice_recognition, + .stop_voice_recognition = stop_voice_recognition, + .volume_control = volume_control, + .device_status_notification = device_status_notification, + .cops_response = cops_response, + .cind_response = cind_response, + .formatted_at_response = formatted_at_response, + .at_response = at_response, + .clcc_response = clcc_response, + .phone_state_change = phone_state_change, + .cleanup = cleanup +}; + +bthf_interface_t *bt_get_handsfree_interface(void) +{ + return &iface; +} diff -Nru bluez-4.101/android/hal-health.c bluez-5.23/android/hal-health.c --- bluez-4.101/android/hal-health.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-health.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" + +static const bthl_callbacks_t *cbacks = NULL; + +static bool interface_ready(void) +{ + return cbacks != NULL; +} + +static void handle_app_registration_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_health_app_reg_state *ev = buf; + + if (cbacks->app_reg_state_cb) + cbacks->app_reg_state_cb(ev->id, ev->state); +} + +static void handle_channel_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_health_channel_state *ev = buf; + int flags; + + if (fd < 0) + goto end; + + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + error("health: fcntl GETFL error: %s", strerror(errno)); + return; + } + + /* Clean O_NONBLOCK fd flag as Android Java layer expects */ + if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) { + error("health: fcntl SETFL error: %s", strerror(errno)); + return; + } + +end: + if (cbacks->channel_state_cb) + cbacks->channel_state_cb(ev->app_id, (bt_bdaddr_t *) ev->bdaddr, + ev->mdep_index, ev->channel_id, + ev->channel_state, fd); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_HEALTH_APP_REG_STATE */ + { handle_app_registration_state, false, + sizeof(struct hal_ev_health_app_reg_state) }, + /* HAL_EV_HEALTH_CHANNEL_STATE */ + { handle_channel_state, false, + sizeof(struct hal_ev_health_channel_state) }, +}; + +static bt_status_t register_application(bthl_reg_param_t *reg, int *app_id) +{ + uint8_t buf[IPC_MTU]; + struct hal_cmd_health_reg_app *cmd = (void *) buf; + struct hal_rsp_health_reg_app rsp; + size_t rsp_len = sizeof(rsp); + bt_status_t status; + uint16_t off, len; + int i; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!reg || !app_id || !reg->application_name) + return BT_STATUS_PARM_INVALID; + + *app_id = -1; + memset(buf, 0, IPC_MTU); + + cmd->num_of_mdep = reg->number_of_mdeps; + + off = 0; + cmd->app_name_off = off; + len = strlen(reg->application_name) + 1; + memcpy(cmd->data, reg->application_name, len); + off += len; + + cmd->provider_name_off = off; + if (reg->provider_name) { + len = strlen(reg->provider_name) + 1; + memcpy(cmd->data + off, reg->provider_name, len); + off += len; + } + + cmd->service_name_off = off; + if (reg->srv_name) { + len = strlen(reg->srv_name) + 1; + memcpy(cmd->data + off, reg->srv_name, len); + off += len; + } + + cmd->service_descr_off = off; + if (reg->srv_desp) { + len = strlen(reg->srv_desp) + 1; + memcpy(cmd->data + off, reg->srv_desp, len); + off += len; + } + + cmd->len = off; + status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP, + sizeof(*cmd) + cmd->len, buf, + &rsp_len, &rsp, NULL); + if (status != BT_STATUS_SUCCESS) + return status; + + for (i = 0; i < reg->number_of_mdeps; i++) { + struct hal_cmd_health_mdep *mdep = (void *) buf; + + memset(buf, 0, IPC_MTU); + mdep->app_id = rsp.app_id; + mdep->role = reg->mdep_cfg[i].mdep_role; + mdep->data_type = reg->mdep_cfg[i].data_type; + mdep->channel_type = reg->mdep_cfg[i].channel_type; + + if (reg->mdep_cfg[i].mdep_description) { + mdep->descr_len = + strlen(reg->mdep_cfg[i].mdep_description) + 1; + memcpy(mdep->descr, reg->mdep_cfg[i].mdep_description, + mdep->descr_len); + } + + status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, + sizeof(*mdep) + mdep->descr_len, + buf, NULL, NULL, NULL); + + if (status != BT_STATUS_SUCCESS) + return status; + } + + *app_id = rsp.app_id; + + return status; +} + +static bt_status_t unregister_application(int app_id) +{ + struct hal_cmd_health_unreg_app cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.app_id = app_id; + + return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_UNREG_APP, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t connect_channel(int app_id, bt_bdaddr_t *bd_addr, + int mdep_cfg_index, int *channel_id) +{ + struct hal_cmd_health_connect_channel cmd; + struct hal_rsp_health_connect_channel rsp; + size_t len = sizeof(rsp); + bt_status_t status; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr || !channel_id) + return BT_STATUS_PARM_INVALID; + + *channel_id = -1; + cmd.app_id = app_id; + cmd.mdep_index = mdep_cfg_index; + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + status = hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_CONNECT_CHANNEL, + sizeof(cmd), &cmd, &len, &rsp, NULL); + + if (status == BT_STATUS_SUCCESS) + *channel_id = rsp.channel_id; + + return status; +} + +static bt_status_t destroy_channel(int channel_id) +{ + struct hal_cmd_health_destroy_channel cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.channel_id = channel_id; + + return hal_ipc_cmd(HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_DESTROY_CHANNEL, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t init(bthl_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + /* store reference to user callbacks */ + cbacks = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_HEALTH, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_HEALTH; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbacks = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_HEALTH); + } + + return ret; +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbacks = NULL; + + cmd.service_id = HAL_SERVICE_ID_HEALTH; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_HEALTH); +} + +static bthl_interface_t health_if = { + .size = sizeof(health_if), + .init = init, + .register_application = register_application, + .unregister_application = unregister_application, + .connect_channel = connect_channel, + .destroy_channel = destroy_channel, + .cleanup = cleanup +}; + +bthl_interface_t *bt_get_health_interface(void) +{ + return &health_if; +} diff -Nru bluez-4.101/android/hal-hidhost.c bluez-5.23/android/hal-hidhost.c --- bluez-4.101/android/hal-hidhost.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-hidhost.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,392 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "hal-ipc.h" + +static const bthh_callbacks_t *cbacks; + +static bool interface_ready(void) +{ + return cbacks != NULL; +} + +static void handle_conn_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_hidhost_conn_state *ev = buf; + + if (cbacks->connection_state_cb) + cbacks->connection_state_cb((bt_bdaddr_t *) ev->bdaddr, + ev->state); +} + +static void handle_info(void *buf, uint16_t len, int fd) +{ + struct hal_ev_hidhost_info *ev = buf; + bthh_hid_info_t info; + + info.attr_mask = ev->attr; + info.sub_class = ev->subclass; + info.app_id = ev->app_id; + info.vendor_id = ev->vendor; + info.product_id = ev->product; + info.version = ev->version; + info.ctry_code = ev->country; + info.dl_len = ev->descr_len; + memcpy(info.dsc_list, ev->descr, info.dl_len); + + if (cbacks->hid_info_cb) + cbacks->hid_info_cb((bt_bdaddr_t *) ev->bdaddr, info); +} + +static void handle_proto_mode(void *buf, uint16_t len, int fd) +{ + struct hal_ev_hidhost_proto_mode *ev = buf; + + if (cbacks->protocol_mode_cb) + cbacks->protocol_mode_cb((bt_bdaddr_t *) ev->bdaddr, + ev->status, ev->mode); +} + +static void handle_idle_time(void *buf, uint16_t len, int fd) +{ + struct hal_ev_hidhost_idle_time *ev = buf; + + if (cbacks->idle_time_cb) + cbacks->idle_time_cb((bt_bdaddr_t *) ev->bdaddr, ev->status, + ev->idle_rate); +} + +static void handle_get_report(void *buf, uint16_t len, int fd) +{ + struct hal_ev_hidhost_get_report *ev = buf; + + if (len != sizeof(*ev) + ev->len) { + error("invalid get report event, aborting"); + exit(EXIT_FAILURE); + } + + if (cbacks->get_report_cb) + cbacks->get_report_cb((bt_bdaddr_t *) ev->bdaddr, ev->status, + ev->data, ev->len); +} + +static void handle_virtual_unplug(void *buf, uint16_t len, int fd) +{ + struct hal_ev_hidhost_virtual_unplug *ev = buf; + + if (cbacks->virtual_unplug_cb) + cbacks->virtual_unplug_cb((bt_bdaddr_t *) ev->bdaddr, + ev->status); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_HIDHOST_CONN_STATE */ + { handle_conn_state, false, sizeof(struct hal_ev_hidhost_conn_state) }, + /* HAL_EV_HIDHOST_INFO */ + { handle_info, false, sizeof(struct hal_ev_hidhost_info) }, + /* HAL_EV_HIDHOST_PROTO_MODE */ + { handle_proto_mode, false, sizeof(struct hal_ev_hidhost_proto_mode) }, + /* HAL_EV_HIDHOST_IDLE_TIME */ + { handle_idle_time, false, sizeof(struct hal_ev_hidhost_idle_time) }, + /* HAL_EV_HIDHOST_GET_REPORT */ + { handle_get_report, true, sizeof(struct hal_ev_hidhost_get_report) }, + /* HAL_EV_HIDHOST_VIRTUAL_UNPLUG */ + { handle_virtual_unplug, false, + sizeof(struct hal_ev_hidhost_virtual_unplug) }, +}; + +static bt_status_t hidhost_connect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_hidhost_connect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t disconnect(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_hidhost_disconnect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t virtual_unplug(bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_hidhost_virtual_unplug cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_VIRTUAL_UNPLUG, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info) +{ + struct hal_cmd_hidhost_set_info cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + cmd.attr = hid_info.attr_mask; + cmd.subclass = hid_info.sub_class; + cmd.app_id = hid_info.app_id; + cmd.vendor = hid_info.vendor_id; + cmd.product = hid_info.product_id; + cmd.country = hid_info.ctry_code; + cmd.descr_len = hid_info.dl_len; + memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t get_protocol(bt_bdaddr_t *bd_addr, + bthh_protocol_mode_t protocol_mode) +{ + struct hal_cmd_hidhost_get_protocol cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + /* type match IPC type */ + cmd.mode = protocol_mode; + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_PROTOCOL, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t set_protocol(bt_bdaddr_t *bd_addr, + bthh_protocol_mode_t protocol_mode) +{ + struct hal_cmd_hidhost_set_protocol cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + /* type match IPC type */ + cmd.mode = protocol_mode; + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_PROTOCOL, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t get_report(bt_bdaddr_t *bd_addr, + bthh_report_type_t report_type, + uint8_t report_id, + int buffer_size) +{ + struct hal_cmd_hidhost_get_report cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + cmd.id = report_id; + cmd.buf_size = buffer_size; + + /* type match IPC type */ + cmd.type = report_type; + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t set_report(bt_bdaddr_t *bd_addr, + bthh_report_type_t report_type, + char *report) +{ + uint8_t buf[IPC_MTU]; + struct hal_cmd_hidhost_set_report *cmd = (void *) buf; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr || !report) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); + cmd->len = strlen(report); + memcpy(cmd->data, report, cmd->len); + + /* type match IPC type */ + cmd->type = report_type; + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, + sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL); +} + +static bt_status_t send_data(bt_bdaddr_t *bd_addr, char *data) +{ + uint8_t buf[IPC_MTU]; + struct hal_cmd_hidhost_send_data *cmd = (void *) buf; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + if (!bd_addr || !data) + return BT_STATUS_PARM_INVALID; + + memcpy(cmd->bdaddr, bd_addr, sizeof(cmd->bdaddr)); + cmd->len = strlen(data); + memcpy(cmd->data, data, cmd->len); + + return hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, + sizeof(*cmd) + cmd->len, buf, NULL, NULL, NULL); +} + +static bt_status_t init(bthh_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + /* store reference to user callbacks */ + cbacks = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_HIDHOST, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_HIDHOST; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbacks = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST); + } + + return ret; +} + +static void cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbacks = NULL; + + cmd.service_id = HAL_SERVICE_ID_HIDHOST; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_HIDHOST); +} + +static bthh_interface_t hidhost_if = { + .size = sizeof(hidhost_if), + .init = init, + .connect = hidhost_connect, + .disconnect = disconnect, + .virtual_unplug = virtual_unplug, + .set_info = set_info, + .get_protocol = get_protocol, + .set_protocol = set_protocol, + .get_report = get_report, + .set_report = set_report, + .send_data = send_data, + .cleanup = cleanup +}; + +bthh_interface_t *bt_get_hidhost_interface(void) +{ + return &hidhost_if; +} diff -Nru bluez-4.101/android/hal-ipc-api.txt bluez-5.23/android/hal-ipc-api.txt --- bluez-4.101/android/hal-ipc-api.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-ipc-api.txt 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,2080 @@ +Android HAL protocol for Bluetooth +================================== + +The Android HAL daemon for Bluetooth functionality implements the Unix socket +server protocol around /run/bluetooth/daemon (tentative location) or Linux +abstract sockets (tentative name). + +The daemon is single threaded and uses a mainloop for scheduling and general +operation. + +The protocol is SOCK_SEQPACKET based and follows a strict PDU specification +with a generic header and initial registration exchange. The communication +is driven from the HAL with command/response exchanges. The daemon will use +notification to signal events. The protocol is single PDU exchanged based, +meaning every command requires a response. Notification does not require +any confirmation. Not handling this PDU exchange leads to a disconnection of +the socket. + +Command/response and notification use separate sockets. First connected socket +is used for command/response, second for notification. All services are +multi-plexed over same pair of sockets. Separation is done to ease +implementation of simple HAL library with dedicated thread for handling +notification. + +This strict protocol requirement is done to match C based callbacks and +callout functions that are running in a thread inside the HAL and might +block. + + .--Android--. .--Android--. + | daemon | | HAL | + | | Command | | + | | <-------------------------- | | + | | | | + | | --------------------------> | | + | | Response | | + | | | | + | | | | + | | Notification | | + | | --------------------------> | | + | | | | + '-----------' '-----------' + +Every packet will follow the basic header to support simple multi-plexing +over the same socket. It will also support a basic control channel with service +id 0. + + 0 8 16 24 31 + +--------------+--------------+--------------+--------------+ + | Service ID | Opcode | Data Length | + +--------------+--------------+-----------------------------+ + | | + +The unique service ID is assigned by this specification for each HAL. + +As general rule of thumb, the opcode for command matches the opcode for a +response. Or the opcode 0x00 for an error is returned. + +Notification opcodes start from 0x80. + +All command/response opcodes have the least significant bit not set. And all +notifications have the least significant bit set. + +The HAL modules only have the job to map the callback and event functions +to the protocol. They do not need to do anything else. Below is an example +of a sample transaction for the Bluetooth Core HAL and enabling of an +adapter. + + HAL Daemon + ---------------------------------------------------- + + call enable() --> command 0x01 + return enable() <-- response 0x01 + + call adapter_state_changed() <-- notification 0x81 + return adapter_state_changed() + +When the Android hardware framework calls into the Bluetooth Core HAL +and executes the enable() callback, the HAL module sends the enable +command with opcode 0x01 to the daemon. As soon as the daemon responds, +the callback will return with the appropriate result. + +After the daemon switched on the adapter, it will send a notification +with opcode 0x81 to the HAL module. + +The Bluetooth Core HAL and Bluetooth Socket HAL are guaranteed to be +available from the daemon. All other HAL modules are optional. + +When the Bluetooth Core HAL init() function is called, it should open +the socket and register both "bluetooth" and "socket" service modules. It is +required to register "socket" service at the same time since the HAL module +does not have its own init() function. + +When new profiles are initiated, the get_profile_interface() callback +will load the profile and during init() of the profile, it should register the +specific service. + + Bluetooth main thread Daemon + ------------------------------------------------------- + + init() --> open command socket + --> open notification socket + --> register module "bluetooth" + --> register module "socket" + + get_profile_interface() --> return profile struct + --> continue on Handsfree thread + + + Handsfree thread Daemon + -------------------------------------------------------- + + init() --> register module handsfree + + +Core Service (ID 0) +=================== + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 Failed + + Opcode 0x01 - Register module command/response + + Command parameters: Service id (1 octet) + Mode (1 octet) + Response parameters: + + In case a command is sent for an undeclared service ID, it will + be rejected. Also there will be no notifications for undeclared + service ID. + + Valid Mode values: 0x00 = Default Mode + 0xXX = as defined by service + + In case of an error, the error response will be returned. + + Opcode 0x02 - Unregister module command/response + + Command parameters: Service id (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + +Bluetooth Core HAL (ID 1) +========================= + +Android HAL name: "bluetooth" (BT_HARDWARE_MODULE_ID) + + Service modes: 0x00 = Enable BR/EDR/LE if supported (default) + 0x01 = Enable BR/EDR only + 0x02 = Enable LE only + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Opcode 0x01 - Enable command/response + + Command parameters: + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x02 - Disable command/response + + Command parameters: + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x03 - Get Adapter Properties command/response + + Command parameters: + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x04 - Get Adapter Property command/response + + Command parameters: Property type (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x05 - Set Adapter Property command/response + + Command parameters: Property type (1 octet) + Property length (2 octets) + Property value (variable) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x06 - Get Remote Device Properties command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x07 - Get Remote Device Property command/response + + Command parameters: Remote address (6 octets) + Property type (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x08 - Set Remote Device Property command/response + + Command parameters: Remote address (6 octets) + Property type (1 octet) + Property length (2 octets) + Property value (variable) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x09 - Get Remote Service Record command/response + + Command parameters: Remote address (6 octets) + UUID (16 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0a - Get Remote Services command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0b - Start Discovery command/response + + Command parameters: + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0c - Cancel Discovery command/response + + Command parameters: + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0d - Create Bond command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0e - Remove Bond command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0f - Cancel Bond command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x10 - PIN Reply command/response + + Command parameters: Remote address (6 octets) + Accept (1 octet) + PIN length (1 octet) + PIN code (16 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x11 - SSP Reply command/response + + Command parameters: Remote address (6 octets) + SSP variant (1 octet) + Accept (1 octet) + Passkey (4 octets) + Response parameters: + + Valid SSP variant values: 0x00 = Passkey Confirmation + 0x01 = Passkey Entry + 0x02 = Consent (for Just Works) + 0x03 = Passkey Notification + + In case of an error, the error response will be returned. + + Opcode 0x12 - DUT Mode Configure command/response + + Command parameters: Enable (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x13 - DUT Mode Send command/response + + Command parameters: Opcode (2 octets) + Length (1 octet) + Data (variable) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x14 - LE Test Mode command/response + + Command parameters: Opcode (2 octets) + Length (1 octet) + Data (variable) + Response parameters: + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Adapter State Changed notification + + Notifications parameters: State (1 octet) + + Valid state values: 0x00 = Off + 0x01 = On + + Opcode 0x82 - Adapter Properties Changed notification + + Notification parameters: Status (1 octet) + Num properties (1 octet) + Type # (1 octet) + Length # (2 octets) + Value # (variable) + ... + + Opcode 0x83 - Remote Device Properties notification + + Notification parameters: Status (1 octet) + Remote address (6 octets) + Num properties (1 octet) + Type # (1 octet) + Length # (2 octets) + Value # (variable) + ... + + Opcode 0x84 - Device Found notification + + Notification parameters: Num properties (1 octet) + Type # (1 octet) + Length # (2 octets) + Value # (variable) + ... + + Opcode 0x85 - Discovery State Changed notification + + Notifications parameters: State (1 octet) + + Opcode 0x86 - PIN Request notification + + Notification parameters: Remote address (6 octets) + Remote name (249 octets) + Class of device (4 octets) + + Opcode 0x87 - SSP Request notification + + Notification parameters: Remote address (6 octets) + Remote name (249 octets) + Class of device (4 octets) + Pairing variant (1 octet) + Passkey (4 octets) + + Opcode 0x88 - Bond State Changed notification + + Notification parameters: Status (1 octet) + Remote address (6 octets) + Bond state (1 octet) + + Valid bond state values: 0x00 = None + 0x01 = Bonding + 0x02 = Bonded + + Opcode 0x89 - ACL State Changed notification + + Notification parameters: Status (1 octet) + Remote address (6 octets) + ACL state (1 octet) + + Opcode 0x8a - DUT Mode Receive notification + + Notification parameters: Opcode (2 octets) + Length (1 octet) + Data (variable) + + Opcode 0x8b - LE Test Mode notification + + Notification parameters: Status (1 octet) + Num packets (2 octets) + + +Bluetooth Socket HAL (ID 2) +=========================== + +Android HAL name:: "socket" (BT_PROFILE_SOCKETS_ID) + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Listen command/response + + Command parameters: Socket type (1 octet) + Service name (256 octets) + Service UUID (16 octets) + Channel (2 octets) + Socket flags (1 octet) + Response parameters: File descriptor (inline) + + Valid socket types: 0x01 = RFCOMM + 0x02 = SCO + 0x03 = L2CAP + + Valid socket flags: 0x01 = Encrypt + 0x02 = Auth + + In case of an error, the error response will be returned. + + Opcode 0x02 - Connect command/response + + Command parameters: Remote address (6 octets) + Socket type (1 octet) + Service UUID (16 octets) + Channel (2 octets) + Socket flags (1 octet) + Response parameters: File descriptor (inline) + + Valid socket types: 0x01 = RFCOMM + 0x02 = SCO + 0x03 = L2CAP + + Valid socket flags: 0x01 = Encrypt + 0x02 = Auth + + In case of an error, the error response will be returned. + + +Bluetooth HID Host HAL (ID 3) +============================ + +Android HAL name: "hidhost" (BT_PROFILE_HIDHOST_ID) + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Connect command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x02 - Disconnect command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x03 - Virtual Unplug command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x04 - Set Info command/response + + Command parameters: Remote address (6 octets) + Attribute mask (2 octets) + Subclass (1 octet) + Application ID (1 octet) + Vendor ID (2 octets) + Product ID (2 octets) + Version (2 octets) + Country code (1 octet) + Descriptor length (2 octet) + Descriptor value (884 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x05 - Get Protocol command/response + + Command parameters: Remote address (6 octets) + Protocol mode (1 octet) + Response parameters: + + Valid protocol modes: 0x00 = Report + 0x01 = Boot + 0xff = Unsupported + + In case of an error, the error response will be returned. + + Opcode 0x06 - Set Protocol command/response + + Command parameters: Remote address (6 octets) + Protocol mode (1 octet) + Response parameters: + + Valid protocol modes: 0x00 = Report + 0x01 = Boot + 0xff = Unsupported + + In case of an error, the error response will be returned. + + Opcode 0x07 - Get Report command/response + + Command parameters: Remote address (6 octets) + Report type (1 octet) + Report ID (1 octet) + Buffer size (2 octet) + Response parameters: + + Valid report types: 0x01 = Input + 0x02 = Output + 0x03 = Feature + + In case of an error, the error response will be returned. + + Opcode 0x08 - Set Report command/response + + Command parameters: Remote address (6 octets) + Report type (1 octet) + Report length (2 octets) + Report data (Report length) + + Response parameters: + + Valid report types: 0x01 = Input + 0x02 = Output + 0x03 = Feature + + In case of an error, the error response will be returned. + + Opcode 0x09 - Send Data command/response + + Command parameters: Remote address (6 octets) + Data length (2 octets) + Data (Data length) + + Response parameters: + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Connection State notification + + Notification parameters: Remote address (6 octets) + Connection State (1 octets) + + Valid connection states: 0x00 = Connected + 0x01 = Connecting + 0x02 = Disconnected + 0x03 = Disconnecting + 0x04 = Failed - Mouse from host + 0x05 = Failed - Keyboard from host + 0x06 = Failed - Too many devices + 0x07 = Failed - No HID driver + 0x08 = Failed - generic + 0x09 = Unknown + + Opcode 0x82 - HID Info notification + + Notification parameters: Remote address (6 octets) + Attribute mask (2 octets) + Subclass (1 octet) + Application ID (1 octet) + Vendor ID (2 octets) + Product ID (2 octets) + Version (2 octets) + Country code (1 octet) + Descriptor length (2 octet) + Descriptor value (884 octets) + + Opcode 0x83 - Protocol Mode notification + + Notification parameters: Remote address (6 octets) + Status (1 octet) + Protocol mode (1 octet) + + Valid protocol modes: 0x00 = Report + 0x01 = Boot + 0xff = Unsupported + + Opcode 0x84 - Idle Time notification + + Notification parameters: Remote address (6 octets) + Status (1 octet) + Idle time (2 octets) + + Opcode 0x85 - Get Report notification + + Notification parameters: Remote address (6 octets) + Status (1 octet) + Report length (2 octets) + Report data (variable) + + Opcode 0x86 - Virtual Unplug notification + + Notification parameters: Remote address (6 octets) + Status (1 octet) + + Valid status values: 0x00 = Ok + 0x01 = Handshake - Device not ready + 0x02 = Handshake - Invalid report ID + 0x03 = Handshake - Transaction not SPT + 0x04 = Handshake - Invalid parameter + 0x05 = Handshake - Generic error + 0x06 = General error + 0x07 = SDP error + 0x08 = Set protocol error + 0x09 = Device database full + 0x0a = Device type not supported + 0x0b = No resources + 0x0c = Authentication failed + 0x0d = HDL + + +Bluetooth PAN HAL (ID 4) +======================== + +Android HAL name: "pan" (BT_PROFILE_PAN_ID) + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Enable command/response + + Command parameters: Local role (1 octet) + Response parameters: + + Valid role values: 0x00 = None + 0x01 = NAP + 0x02 = PANU + + In case of an error, the error response will be returned. + + Opcode 0x02 - Get Local Role command/response + + Command parameters: + Response parameters: Local role (1 octet) + + Valid role values: 0x00 = None + 0x01 = NAP + 0x02 = PANU + + In case of an error, the error response will be returned. + + Opcode 0x03 - Connect command/response + + Command parameters: Remote address (6 octets) + Local role (1 octet) + Remote role (1 octet) + Response parameters: + + Valid role values: 0x01 = NAP + 0x02 = PANU + + In case of an error, the error response will be returned. + + Opcode 0x04 - Disconnect command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Control State notification + + Notification parameters: Control state (1 octet) + Status (1 octet) + Local role (1 octet) + Interface name (17 octet) + + Valid control states: 0x00 = Enabled + 0x01 = Disabled + + Valid role values: 0x00 = None + 0x01 = NAP + 0x02 = PANU + + Opcode 0x82 - Connection State notification + + Notification parameters: Connection state (1 octet) + Status (1 octet) + Remote address (6 octets) + Local role (1 octet) + Remote role (1 octet) + + Valid connection states: 0x00 = Connected + 0x01 = Connecting + 0x02 = Disconnected + 0x03 = Disconnecting + + Valid role values: 0x01 = NAP + 0x02 = PANU + + +Bluetooth Handsfree HAL (ID 5) +============================== + +Android HAL name: "handsfree" (BT_PROFILE_HANDSFREE_ID) + + Service modes: 0x00 = Headset Profile only mode (default) + 0x01 = Handsfree Profile (narrowband speech) + 0x02 = Handsfree Profile (narrowband and wideband speech) + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Connect command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x02 - Disconnect command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x03 - Connect Audio command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x04 - Disconnect Audio command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x05 - Start Voice Recognition command/response + + Command parameters: + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x06 - Stop Voice Recognition command/response + + Command parameters: + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x07 - Volume Control command/response + + Command parameters: Volume type (1 octet) + Volume (1 octet) + Response parameters: + + Valid volume types: 0x00 = Speaker + 0x01 = Microphone + + In case of an error, the error response will be returned. + + Opcode 0x08 - Device Status Notification command/response + + Command parameters: Network state (1 octet) + Service type (1 octet) + Signal strength (1 octet) + Battery level (1 octet) + Response parameters: + + Valid network states: 0x00 = Not available + 0x01 = Available + + Valid service types: 0x00 = Home network + 0x01 = Roaming network + + In case of an error, the error response will be returned. + + Opcode 0x09 - COPS Response command/response + + Command parameters: COPS command response (string) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0a - CIND Response command/response + + Command parameters: Service (1 octet) + Number of active calls (1 octet) + Number of held calls (1 octet) + Call setup state (1 octet) + Signal strength (1 octet) + Roaming indicator (1 octet) + Battery level (1 octet) + Response parameters: + + Valid call setup states: 0x00 = Active + 0x01 = Held + 0x02 = Dialing + 0x03 = Alerting + 0x04 = Incoming + 0x05 = Waiting + 0x06 = Idle + + In case of an error, the error response will be returned. + + Opcode 0x0b - Formatted AT Response command/response + + Command parameters: Pre-formatted AT response (string) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0c - AT Response command/response + + Command parameters: Response code (1 octet) + Error code (1 octet) + Response parameters: + + Valid response codes: 0x00 = ERROR + 0x01 = OK + + In case of an error, the error response will be returned. + + Opcode 0x0d - CLCC Response command/response + + Command parameters: Call index (1 octet) + Call direction (1 octet) + Call state (1 octet) + Call mode (1 octet) + Call multiparty type (1 octet) + Call number type (1 octet) + Call number (string) + Response parameters: + + Valid call directions: 0x00 = Outgoing + 0x01 = Incoming + + Valid call states: 0x00 = Active + 0x01 = Held + 0x02 = Dialing + 0x03 = Alerting + 0x04 = Incoming + 0x05 = Waiting + 0x06 = Idle + + Valid call modes: 0x00 = Voice + 0x01 = Data + 0x02 = Fax + + Valid multiparty types: 0x00 = Single call + 0x01 = Multiparty call + + Valid number types: 0x81 = Unknown + 0x91 = International + + In case of an error, the error response will be returned. + + Opcode 0x0e - Phone Status Change command/response + + Command parameters: Number of active calls (1 octet) + Number of held calls (1 octet) + Call setup state (1 octet) + Call number type (1 octet) + Call number (string) + Response parameters: + + Valid call setup states: 0x00 = Active + 0x01 = Held + 0x02 = Dialing + 0x03 = Alerting + 0x04 = Incoming + 0x05 = Waiting + 0x06 = Idle + + Valid number types: 0x81 = Unknown + 0x91 = International + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Connection State notification + + Notification parameters: Connection state (1 octet) + Remote address (6 octets) + + Valid connection states: 0x00 = Disconnected + 0x01 = Connecting + 0x02 = Connected + 0x03 = SLC connected + 0x04 = Disconnecting + + Opcode 0x82 - Audio State notification + + Notification parameters: Audio state (1 octet) + Remote address (6 octets) + + Valid audio states: 0x00 = Disconnected + 0x01 = Connecting + 0x02 = Connected + 0x03 = Disconnecting + + Opcode 0x83 - Voice Recognition Command notification + + Notification parameters: Voice recognition state (1 octet) + + Valid voice recognition states: 0x00 = Stopped + 0x01 = Started + + Opcode 0x84 - Answer Call Command notification + + Notification parameters: + + Opcode 0x85 - Hangup Call Command notification + + Notification parameters: + + Opcode 0x86 - Volume Command notification + + Notification parameters: Volume type (1 octet) + Volume (1 octet) + + Valid volume types: 0x00 = Speaker + 0x01 = Microphone + + Opcode 0x87 - Dial Call Command notification + + Notification parameters: Number (string) + + Opcode 0x88 - DTMF Command notification + + Notification parameters: Tone (1 octet) + + Opcode 0x89 - NREC Command notification + + Notification parameters: NREC types (1 octet) + + Valid NREC types: 0x00 = Stop + 0x01 = Start + + Opcode 0x8a - CHLD Command notification + + Notification parameters: NREC types (1 octet) + + Valid CHLD types: 0x00 = Release and hold + 0x01 = Release active and accept held + 0x02 = Hold active and accept held + 0x03 = Add held call to conference + + Opcode 0x8b - CNUM Command notification + + Notification parameters: + + Opcode 0x8c - CIND Command notification + + Notification parameters: + + Opcode 0x8d - COPS Command notification + + Notification parameters: + + Opcode 0x8e - CLCC Command notification + + Notification parameters: + + Opcode 0x8f - Unknown AT Command notification + + Notification parameters: AT command (string) + + Opcode 0x90 - Key Pressed Command notification + + Notification parameters: + + +Bluetooth Advanced Audio HAL (ID 6) +=================================== + +Android HAL name: "a2dp" (BT_PROFILE_ADVANCED_AUDIO_ID) + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Connect command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x02 - Disconnect command/response + + Command parameters: Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Connection State notification + + Notification parameters: Connection state (1 octet) + Remote address (6 octets) + + Valid connection states: 0x00 = Disconnected + 0x01 = Connecting + 0x02 = Connected + 0x03 = Disconnecting + + Opcode 0x82 - Audio State notification + + Notification parameters: Audio state (1 octet) + Remote address (6 octets) + + Valid connection states: 0x00 = Remote suspend + 0x01 = Stopped + 0x02 = Started + + +Bluetooth Health HAL (ID 7) +=========================== + +Android HAL name: "health" (BT_PROFILE_HEALTH_ID) + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Register Application command/response + + Command parameters: Number of MDEP (1 octet) + Application name offset (2 octets) + Provider name offset (2 octets) + Service name offset (2 octets) + Service description offset (2 octets) + Data length (2 octets) + Data (data length) + Response parameters: Application ID (2 octets) + + Strings are null terminated. + In case of an error, the error response will be returned. + + Opcode 0x02 - Register Application MDEP data command/response + + Command parameters: Application ID (2 octets) + MDEP Role (1 octet) + Data type (2 octets) + Channel type (1 octet) + MDEP description length (2 octets) + MDEP description (MDEP desciption length) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x03 - Unregister Application command/response + + Command parameters: Application ID (2 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x04 - Connect Channel command/response + + Command parameters: Application ID (2 octets) + Remote address (6 octets) + MDEP index (1 octet) + Response parameters: Channel ID (2 octets) + + In case of an error, the error response will be returned. + + Opcode 0x05 - Destroy Channel command/response + + Command parameters: Channel ID (2 octets) + Response parameters: + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Application Registration State notification + + Notification parameters: Application ID (2 octets) + Application state (1 octet) + + Valid application states: 0x00 = Registration success + 0x01 = Registration failed + 0x02 = Deregistration success + 0x03 = Deregistration failed + + Opcode 0x82 - Channel State notification + + Notification parameters: Application ID (2 octets) + Remote address (6 octets) + MDEP index (1 octet) + Channel ID (2 octets) + Channel state (1 octet) + File descriptor (inline) + + Valid channel states: 0x00 = Connecting + 0x01 = Connected + 0x02 = Disconnecting + 0x03 = Disconnected + 0x04 = Destroyed + + +Bluetooth Remote Control HAL (ID 8) +=================================== + +Android HAL name: "avrcp" (BT_PROFILE_AV_RC_ID) + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Get Play Status Response command/response + + Command parameters: Status (1 octet) + Duration (4 octets) + Position (4 octets) + + In case of an error, the error response will be returned. + + Valid status values: 0x00 = Stopped + 0x01 = Playing + 0x02 = Paused + 0x03 = Fwd seek + 0x04 = Rev seek + 0xff = Error + + Opcode 0x02 - List Player Attributes Response command/response + + Command parameters: Number of attributes (1 octet) + Attribute # (1 octet) + ... + + In case of an error, the error response will be returned. + + Valid attributes: 0x01 = Equalizer + 0x02 = Repead + 0x03 = Shuffle + 0x04 = Scan + + Opcode 0x03 - List Player Values Response command/response + + Command parameters: Number of values (1 octet) + Value # (1 octet) + ... + + In case of an error, the error response will be returned. + + Opcode 0x04 - Get Player Values Response command/response + + Command parameters: Number of attributes (1 octet) + Attribute # (1 octet) + Value # (1 octet) + ... + + In case of an error, the error response will be returned. + + Valid attributes: Same as in List Player Attributes + + Opcode 0x05 - Get Player Attributes Text Response command/response + + Command parameters: Number of attributes (1 octet) + Attribute # (1 octet) + Attribute # text length (1 octet) + Attribute # text (variable) + ... + + In case of an error, the error response will be returned. + + Valid attributes: Same as in List Player Attributes + + Opcode 0x06 - Get Player Values Text Response command/response + + Command parameters: Number of values (1 octet) + Value # (1 octet) + Value # text length (1 octet) + Value # text (variable) + ... + + In case of an error, the error response will be returned. + + Opcode 0x07 - Get Element Attributes Text Response command/response + + Command parameters: Number of elements (1 octet) + Element # (1 octet) + Element # text length (1 octet) + Element # text (variable) + ... + + In case of an error, the error response will be returned. + + Valid elements: 0x01 = Title + 0x02 = Artist + 0x03 = Album + 0x04 = Track Number + 0x05 = Number of Tracks + 0x06 = Genre + 0x06 = Duration + + Opcode 0x08 - Set Player Attributes Value Response command/response + + Command parameters: Status (1 octet) + + In case of an error, the error response will be returned. + + Valid status values: Same as in Get Play Status Response + + Opcode 0x09 - Register Notification Response command/response + + Command parameters: Event (1 octet) + Type (1 octet) + Data length (1 octet) + Data (variable) + + In case of an error, the error response will be returned. + + Valid event values: 0x01 = Status Changed + 0x02 = Track Changed + 0x03 = Track Reached End + 0x04 = Track Reached Start + 0x05 = Position Changed + 0x08 = Setting Changed + + Valid type values : 0x00 = Interim + 0x01 = Changed + + Opcode 0x0a - Set Volume command/response + + Command parameters: Value (1 octet) + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Remote Features notification + + Notification parameters: Remote address (6 octets) + Features (1 octet) + + Valid features values : 0x00 = None + 0x01 = Metadata + 0x02 = Absolute Volume + 0x03 = Browse + + Opcode 0x82 - Get Play Status notification + + Notification parameters: + + Opcode 0x83 - List Player Attributes notification + + Notification parameters: + + Opcode 0x84 - List Player Values notification + + Notification parameters: Attribute (1 octet) + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x85 - Get Player Values notification + + Notification parameters: Number of attributes (1 octet) + Attribute # (1 octet) + ... + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x86 - Get Player Attributes Text notification + + Notification parameters: Number of attributes (1 octet) + Attribute # (1 octet) + ... + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x87 - Get Player Values Text notification + + Notification parameters: Attribute (1 octet) + Number of values (1 octet) + Value # (1 octet) + ... + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x88 - Set Player Values notification + + Notification parameters: Number of attributes (1 octet) + Attribute # (1 octet) + Value # (1 octet) + ... + + Valid attribute values: Same as in List Player Attributes + + Opcode 0x89 - Get Element Attributes notification + + Notification parameters: Number of attributes (1 octet) + Attribute # (1 octet) + ... + + Valid attribute values: Same as in Get Element Attribute + + Opcode 0x8a - Register Notification notification + + Notification parameters: Event (1 octet) + Parameter (4 octets) + + Valid event values: Same as in Register Notification + + Opcode 0x8b - Volume Changed notification + + Notification parameters: Volume (1 octet) + Type (1 octet) + + Valid type values: Same as in Register Notification + + Opcode 0x8c - Passthrough Command notification + + Notification parameters: ID (1 octet) + State (1 octet) + + +Bluetooth GATT HAL (ID 9) +========================= + +Android HAL name: "gatt" (BT_PROFILE_GATT_ID) + +Structures: + + GATT Service ID: UUID (16 octets) + Instance ID (1 octet) + Is Primary (1 octet) + + GATT Included Service ID: UUID (16 octets) + Instance ID (1 octet) + Is Primary (1 octet) + + GATT Characteristic ID: UUID (16 octets) + Instance ID (1 octet) + + GATT Descriptor ID: UUID (16 octets) + Instance ID (1 octet) + +Commands and responses: + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Valid status values: 0x01 = Fail + 0x02 = Not ready + 0x03 = No memory + 0x04 = Busy + 0x05 = Done (already completed) + 0x06 = Unsupported + 0x07 = Parameter invalid + 0x08 = Unhandled + 0x09 = Authentication failure + 0x0a = Remote device down + + Opcode 0x01 - Register Client command/response + + Command parameters: Service UUID (16 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x02 - Unregister Client command/response + + Command parameters: Client Interface (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x03 - Scan command/response + + Command parameters: Client Interface (4 octets) + Start (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x04 - Connect Device command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + Is Direct (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x05 - Disconnect Device command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + Connection ID (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x06 - Listen command/response + + Command parameters: Client Interface (4 octets) + Start (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x07 - Refresh command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x08 - Search Service command/response + + Command parameters: Connection ID (4 octets) + Filtered (1 octet) + Filter UUID (16 octets) + Response parameters: + + Filter UUID shall only be present when Filtered is non-zero. + + In case of an error, the error response will be returned. + + Opcode 0x09 - Get Included Service command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + Continuation (1 octet) + GATT Included Service ID (18 octets) + ... + Response parameters: + + GATT Included Service ID shall only be present when Continuation is non-zero. + + In case of an error, the error response will be returned. + + Opcode 0x0a - Get Characteristic command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + Continuation (1 octet) + GATT Characteristic ID (17 octets) + ... + Response parameters: + + GATT Characteristic ID shall only be present when Continuation is non-zero. + + In case of an error, the error response will be returned. + + Opcode 0x0b - Get Descriptor command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Continuation (1 octet) + GATT Descriptor ID (17 octets) + ... + Response parameters: + + GATT Descriptor ID shall only be present when Continuation is non-zero. + + In case of an error, the error response will be returned. + + Opcode 0x0c - Read Characteristic command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Authorization (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0d - Write Characteristic command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Write Type (4 octets) + Length (4 octets) + Authorization Req. (4 octets) + Value (variable) + Response parameters: + + Valid Write Type: 0x01 = No response + 0x02 = Default + 0x03 = Prepare + 0x04 = Signed + + In case of an error, the error response will be returned. + + Opcode 0x0e - Read Descriptor command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Descriptor ID (17 octets) + Authorization Req. (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x0f - Write Descriptor command/response + + Command parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Descriptor ID (17 octets) + Write Type (4 octets) + Length (4 octets) + Authorization Req. (4 octets) + Value (variable) + Response parameters: + + Valid Write Type: 0x01 = No response + 0x02 = Default + 0x03 = Prepare + 0x04 = Signed + + In case of an error, the error response will be returned. + + Opcode 0x10 - Execute Write command/response + + Command parameters: Connection ID (4 octets) + Execute (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x11 - Register For Notification command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x12 - Deregister For Notification command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x13 - Read Remote RSSI command/response + + Command parameters: Client Interface (4 octets) + Remote address (6 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x14 - Get Device Type command/response + + Command parameters: Remote address (6 octets) + Response parameters: Device Type + + Valid Device Type: 0x01 = BREDR + 0x02 = BLE + 0x03 = DUAL + + In case of an error, the error response will be returned. + + Opcode 0x15 - Set Advertising data command/response + + Command parameters: Server Interface (4 octets) + Set Scan Resp. (1 octet) + Include Name (1 octet) + Include TX Power (1 octet) + Min. Interval (4 octets) + Max. Interval (4 octets) + Appearance (4 octets) + Manufacturer Len. (2 octets) + Manufacturer Data (variable) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x16 - Test Command command/response + + Command parameters: Command (4 octets) + Address (6 octets) + UUID (16 octets) + U1 (2 octets) + U2 (2 octets) + U3 (2 octets) + U4 (2 octets) + U5 (2 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x17 - Register Server command/response + + Command parameters: UUID (16 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x18 - Unregister Server command/response + + Command parameters: Server (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x19 - Connect Peripheral command/response + + Command parameters: Server (4 octets) + Remote address (6 octes) + Is Direct (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1a - Disconnect Peripheral command/response + + Command parameters: Server (4 octets) + Remote address (6 octes) + Connection ID (1 octet) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1b - Add Service command/response + + Command parameters: Server (4 octets) + GATT Service ID (18 octets) + Number of Handles (4 octet) + Response parameters: + + Valid GATT Service ID: UUID (16 octets) + Instance ID (1 octet) + Is Primary (1 octet) + + In case of an error, the error response will be returned. + + Opcode 0x1c - Add Included Service command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + Included handle (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1d - Add Characteristic command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + UUID (16 octets) + Properties (4 octets) + Permissions (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1e - Add Descriptor command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + UUID (16 octets) + Permissions (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x1f - Start Service command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + Transport (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x20 - Stop Service command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x21 - Delete Service command/response + + Command parameters: Server (4 octets) + Service handle (4 octets) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x22 - Send Indication command/response + + Command parameters: Server (4 octets) + Attribute handle (4 octets) + Connection ID (4 octets) + Length (4 octets) + Confirmation (4 octets) + Value (variable) + Response parameters: + + In case of an error, the error response will be returned. + + Opcode 0x23 - Send Response command/response + + Command parameters: Connection ID (4 octets) + Transaction ID (4 octets) + Handle (2 octets) + Offset (2 octets) + Auth Request (1 octect) + Status (4 octets) + GATT Response (4 octets) + Response parameters: + + Valid GATT Response: GATT Value (607 octets) + Handle (2 octets) + + Valid GATT Value: Value (600 octets) + Handle (2 octets) + Offset (2 octets) + Length (2 octets) + Authentication Request (1 octet) + + In case of an error, the error response will be returned. + +Notifications: + + Opcode 0x81 - Register Client notification + + Notification parameters: Status (4 octets) + Client Interface (4 octets) + UUID (16 octets) + + Opcode 0x82 - Scan Result notification + + Notification parameters: Address (6 octets) + RSSI (4 octets) + Length (2 octets) + Data (variable) + + Opcode 0x83 - Connect Device notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + Client Interface (4 octets) + Address (6 octets) + + Opcode 0x84 - Disconnect Device notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + Client Interface (4 octets) + Address (6 octets) + + Opcode 0x85 - Search Complete notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + + Opcode 0x86 - Search Result notification + + Notification parameters: Connection ID (4 octets) + GATT Service ID (18 octets) + + Opcode 0x87 - Get Characteristic notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Char Prop. (4 octets) + + Opcode 0x88 - Get Descriptor notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Descriptor ID (17 octets) + + Opcode 0x89 - Get Included Service notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Service ID (18 octets) + GATT Included Service ID (18 octets) + + Opcode 0x8a - Register For Notification notification + + Notification parameters: Connection ID (4 octets) + Registered (4 octets) + Status (4 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + + Opcode 0x8b - Notify notification + + Notification parameters: Connection ID (4 octets) + Address (6 octets) + GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + Is Notify (1 octet) + Length (2 octets) + Value (variable) + + Opcode 0x8c - Read Characteristic notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Read Parameters (variable) + + Valid GATT Read Parameters: GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Descriptor ID (17 octets) + Value Type (4 octets) + Status (1 octet) + Length (2 octets) + Value (variable) + + Opcode 0x8d - Write Characteristic notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Write Parameters (53 octets) + + Valid GATT Write Parameters: GATT Service ID (18 octets) + GATT Characteristic ID (17 octets) + GATT Description ID (17 octets) + Status (1 octet) + + Opcode 0x8e - Read Descriptor notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Read Parameters (variable) + + Valid GATT Read Parameters: As described in Read Characteristic + + Opcode 0x8f - Write Descriptor notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + GATT Write Parameters (53 octets) + + Valid GATT Write Parameters: As described in Write Characteristic + + Opcode 0x90 - Execute Write notification + + Notification parameters: Connection ID (4 octets) + Status (4 octets) + + Opcode 0x91 - Read Remote RSSI notification + + Notification parameters: Client (4 octets) + Address (6 octets) + RSSI (4 octets) + Status (4 octets) + + Opcode 0x92 - Listen notification + + Notification parameters: Status (4 octets) + Server Interface (4 octets) + + Opcode 0x93 - Register Server notification + + Notification parameters: Status (4 octets) + Server (4 octets) + UUID (16 octets) + + Opcode 0x94 - Connection notification + + Notification parameters: Connection ID (4 octets) + Server (4 octets) + Connected (4 octets) + Address (6 octets) + + Opcode 0x95 - Service Added notification + + Notification parameters: Status (4 octets) + Server (4 octets) + GATT Service ID (18 octets) + Service Handle (4 octets) + + Opcode 0x96 - Included Service Added notification + + Notification patemeters: Status (4 octets) + Server (4 octets) + Service Handle (4 octets) + Included Service Handle (4 octets) + + Opcode 0x97 - Characteristic Added notification + + Notification parameters: Status (4 octets) + Server (4 octets) + UUID (16 octets) + Service Handle (4 octets) + Characteristic Handle (4 octets) + + Opcode 0x98 - Descriptor Added notification + + Notification parameters: Status (4 octets) + Server (4 octets) + UUID (6 octets) + Service Handle (4 octets) + Descriptor Handle (4 octets) + + Opcode 0x99 - Service Started notification + + Notification parameters: Status (4 octets) + Server (4 octets) + Service Handle (4 octets) + + Opcode 0x9a - Service Stopped notification + + Notification parameters: Status (4 octets) + Server (4 octets) + Service Handle (4 octets) + + Opcode 0x9b - Service Deleted notification + + Notification parameters: Status (4 octets) + Server (4 octets) + Service Handle (4 octets) + + Opcode 0x9c - Request Read notification + + Notification parameters: Connection ID (4 octets) + Trans ID (4 octets) + Address (6 octets) + Attribute Handle (4 octets) + Offset (4 octets) + Is Long (1 octet) + + Opcode 0x9d - Request Write notification + + Notification parameters: Connection ID (4 octets) + Trans ID (4 octets) + Address (6 octets) + Attribute Handle (4 octets) + Offset (4 octets) + Length (4 octets) + Need Response (4 octets) + Is Prepare (1 octet) + Value (variable) + + Opcode 0x9e - Request Execute Write notification + + Notification parameters: Connection ID (4 octets) + Trans ID (4 octets) + Address (6 octets) + Execute Write (4 octets) + + Opcode 0x9f - Response Confirmation notification + + Notification parameters: Status (4 octets) + Handle (4 octets) diff -Nru bluez-4.101/android/hal-ipc.c bluez-5.23/android/hal-ipc.c --- bluez-4.101/android/hal-ipc.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-ipc.c 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hal.h" +#include "hal-msg.h" +#include "hal-log.h" +#include "ipc-common.h" +#include "hal-ipc.h" + +#define CONNECT_TIMEOUT (10 * 1000) + +static int listen_sk = -1; +static int cmd_sk = -1; +static int notif_sk = -1; + +static pthread_mutex_t cmd_sk_mutex = PTHREAD_MUTEX_INITIALIZER; + +static pthread_t notif_th = 0; + +struct service_handler { + const struct hal_ipc_handler *handler; + uint8_t size; +}; + +static struct service_handler services[HAL_SERVICE_ID_MAX + 1]; + +void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers, + uint8_t size) +{ + services[service].handler = handlers; + services[service].size = size; +} + +void hal_ipc_unregister(uint8_t service) +{ + services[service].handler = NULL; + services[service].size = 0; +} + +static bool handle_msg(void *buf, ssize_t len, int fd) +{ + struct ipc_hdr *msg = buf; + const struct hal_ipc_handler *handler; + uint8_t opcode; + + if (len < (ssize_t) sizeof(*msg)) { + error("IPC: message too small (%zd bytes)", len); + return false; + } + + if (len != (ssize_t) (sizeof(*msg) + msg->len)) { + error("IPC: message malformed (%zd bytes)", len); + return false; + } + + /* if service is valid */ + if (msg->service_id > HAL_SERVICE_ID_MAX) { + error("IPC: unknown service (0x%x)", msg->service_id); + return false; + } + + /* if service is registered */ + if (!services[msg->service_id].handler) { + error("IPC: unregistered service (0x%x)", msg->service_id); + return false; + } + + /* if opcode fit valid range */ + if (msg->opcode < HAL_MINIMUM_EVENT) { + error("IPC: invalid opcode for service 0x%x (0x%x)", + msg->service_id, msg->opcode); + return false; + } + + /* + * opcode is used as table offset and must be adjusted as events start + * with HAL_MINIMUM_EVENT offset + */ + opcode = msg->opcode - HAL_MINIMUM_EVENT; + + /* if opcode is valid */ + if (opcode >= services[msg->service_id].size) { + error("IPC: invalid opcode for service 0x%x (0x%x)", + msg->service_id, msg->opcode); + return false; + } + + handler = &services[msg->service_id].handler[opcode]; + + /* if payload size is valid */ + if ((handler->var_len && handler->data_len > msg->len) || + (!handler->var_len && handler->data_len != msg->len)) { + error("IPC: message size invalid for service 0x%x opcode 0x%x " + "(%u bytes)", + msg->service_id, msg->opcode, msg->len); + return false; + } + + handler->handler(msg->payload, msg->len, fd); + + return true; +} + +static void *notification_handler(void *data) +{ + struct msghdr msg; + struct iovec iv; + struct cmsghdr *cmsg; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + char buf[IPC_MTU]; + ssize_t ret; + int fd; + + bt_thread_associate(); + + while (true) { + memset(&msg, 0, sizeof(msg)); + memset(buf, 0, sizeof(buf)); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + + iv.iov_base = buf; + iv.iov_len = sizeof(buf); + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + ret = recvmsg(notif_sk, &msg, 0); + if (ret < 0) { + error("Receiving notifications failed: %s", + strerror(errno)); + goto failed; + } + + /* socket was shutdown */ + if (ret == 0) { + pthread_mutex_lock(&cmd_sk_mutex); + if (cmd_sk == -1) { + pthread_mutex_unlock(&cmd_sk_mutex); + break; + } + pthread_mutex_unlock(&cmd_sk_mutex); + + error("Notification socket closed"); + goto failed; + } + + fd = -1; + + /* Receive auxiliary data in msg */ + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); + break; + } + } + + if (!handle_msg(buf, ret, fd)) + goto failed; + } + + close(notif_sk); + notif_sk = -1; + + bt_thread_disassociate(); + + DBG("exit"); + + return NULL; + +failed: + exit(EXIT_FAILURE); +} + +static int accept_connection(int sk) +{ + int err; + struct pollfd pfd; + int new_sk; + + memset(&pfd, 0 , sizeof(pfd)); + pfd.fd = sk; + pfd.events = POLLIN; + + err = poll(&pfd, 1, CONNECT_TIMEOUT); + if (err < 0) { + err = errno; + error("Failed to poll: %d (%s)", err, strerror(err)); + return -1; + } + + if (err == 0) { + error("bluetoothd connect timeout"); + return -1; + } + + new_sk = accept(sk, NULL, NULL); + if (new_sk < 0) { + err = errno; + error("Failed to accept socket: %d (%s)", err, strerror(err)); + return -1; + } + + return new_sk; +} + +bool hal_ipc_accept(void) +{ + int err; + + cmd_sk = accept_connection(listen_sk); + if (cmd_sk < 0) + return false; + + notif_sk = accept_connection(listen_sk); + if (notif_sk < 0) { + close(cmd_sk); + cmd_sk = -1; + return false; + } + + err = pthread_create(¬if_th, NULL, notification_handler, NULL); + if (err) { + notif_th = 0; + error("Failed to start notification thread: %d (%s)", err, + strerror(err)); + close(cmd_sk); + cmd_sk = -1; + close(notif_sk); + notif_sk = -1; + return false; + } + + info("IPC connected"); + + return true; +} + +bool hal_ipc_init(const char *path, size_t size) +{ + struct sockaddr_un addr; + int sk; + int err; + + sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + err = errno; + error("Failed to create socket: %d (%s)", err, + strerror(err)); + return false; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, path, size); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + error("Failed to bind socket: %d (%s)", err, strerror(err)); + close(sk); + return false; + } + + if (listen(sk, 2) < 0) { + err = errno; + error("Failed to listen on socket: %d (%s)", err, + strerror(err)); + close(sk); + return false; + } + + listen_sk = sk; + + return true; +} + +void hal_ipc_cleanup(void) +{ + close(listen_sk); + listen_sk = -1; + + pthread_mutex_lock(&cmd_sk_mutex); + if (cmd_sk >= 0) { + close(cmd_sk); + cmd_sk = -1; + } + pthread_mutex_unlock(&cmd_sk_mutex); + + if (notif_sk < 0) + return; + + shutdown(notif_sk, SHUT_RD); + + pthread_join(notif_th, NULL); + notif_th = 0; +} + +int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, + size_t *rsp_len, void *rsp, int *fd) +{ + ssize_t ret; + struct msghdr msg; + struct iovec iv[2]; + struct ipc_hdr cmd; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct ipc_status s; + size_t s_len = sizeof(s); + + if (cmd_sk < 0) { + error("Invalid cmd socket passed to hal_ipc_cmd"); + goto failed; + } + + if (!rsp || !rsp_len) { + memset(&s, 0, s_len); + rsp_len = &s_len; + rsp = &s; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.service_id = service_id; + cmd.opcode = opcode; + cmd.len = len; + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = param; + iv[1].iov_len = len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + pthread_mutex_lock(&cmd_sk_mutex); + + ret = sendmsg(cmd_sk, &msg, 0); + if (ret < 0) { + error("Sending command failed:%s", strerror(errno)); + pthread_mutex_unlock(&cmd_sk_mutex); + goto failed; + } + + /* socket was shutdown */ + if (ret == 0) { + error("Command socket closed"); + pthread_mutex_unlock(&cmd_sk_mutex); + goto failed; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = rsp; + iv[1].iov_len = *rsp_len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + if (fd) { + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + } + + ret = recvmsg(cmd_sk, &msg, 0); + + pthread_mutex_unlock(&cmd_sk_mutex); + + if (ret < 0) { + error("Receiving command response failed: %s", strerror(errno)); + goto failed; + } + + + if (ret < (ssize_t) sizeof(cmd)) { + error("Too small response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.service_id != service_id) { + error("Invalid service id (0x%x vs 0x%x)", + cmd.service_id, service_id); + goto failed; + } + + if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { + error("Malformed response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.opcode != opcode && cmd.opcode != HAL_OP_STATUS) { + error("Invalid opcode received (0x%x vs 0x%x)", + cmd.opcode, opcode); + goto failed; + } + + if (cmd.opcode == HAL_OP_STATUS) { + struct ipc_status *s = rsp; + + if (sizeof(*s) != cmd.len) { + error("Invalid status length"); + goto failed; + } + + if (s->code == HAL_STATUS_SUCCESS) { + error("Invalid success status response"); + goto failed; + } + + return s->code; + } + + /* Receive auxiliary data in msg */ + if (fd) { + struct cmsghdr *cmsg; + + *fd = -1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); + break; + } + } + } + + *rsp_len = cmd.len; + + return BT_STATUS_SUCCESS; + +failed: + exit(EXIT_FAILURE); +} diff -Nru bluez-4.101/android/hal-ipc.h bluez-5.23/android/hal-ipc.h --- bluez-4.101/android/hal-ipc.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-ipc.h 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +struct hal_ipc_handler { + void (*handler) (void *buf, uint16_t len, int fd); + bool var_len; + size_t data_len; +}; + +bool hal_ipc_init(const char *path, size_t size); +bool hal_ipc_accept(void); +void hal_ipc_cleanup(void); + +int hal_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, void *param, + size_t *rsp_len, void *rsp, int *fd); + +void hal_ipc_register(uint8_t service, const struct hal_ipc_handler *handlers, + uint8_t size); +void hal_ipc_unregister(uint8_t service); diff -Nru bluez-4.101/android/hal-log.h bluez-5.23/android/hal-log.h --- bluez-4.101/android/hal-log.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-log.h 2013-12-27 17:16:56.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#define LOG_TAG "BlueZ" + +#ifdef __BIONIC__ +#include +#else +#include +#define LOG_INFO " I" +#define LOG_WARN " W" +#define LOG_ERROR " E" +#define LOG_DEBUG " D" +#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg) +#endif + +#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg) +#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg) +#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg) +#define DBG(fmt, arg...) ALOG(LOG_DEBUG, LOG_TAG, "%s:%s() "fmt, __FILE__, \ + __func__, ##arg) diff -Nru bluez-4.101/android/hal-msg.h bluez-5.23/android/hal-msg.h --- bluez-4.101/android/hal-msg.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-msg.h 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,1621 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +static const char BLUEZ_HAL_SK_PATH[] = "\0bluez_hal_socket"; + +#define HAL_MINIMUM_EVENT 0x81 + +#define HAL_SERVICE_ID_CORE 0 +#define HAL_SERVICE_ID_BLUETOOTH 1 +#define HAL_SERVICE_ID_SOCKET 2 +#define HAL_SERVICE_ID_HIDHOST 3 +#define HAL_SERVICE_ID_PAN 4 +#define HAL_SERVICE_ID_HANDSFREE 5 +#define HAL_SERVICE_ID_A2DP 6 +#define HAL_SERVICE_ID_HEALTH 7 +#define HAL_SERVICE_ID_AVRCP 8 +#define HAL_SERVICE_ID_GATT 9 + +#define HAL_SERVICE_ID_MAX HAL_SERVICE_ID_GATT + +/* Core Service */ + +#define HAL_STATUS_SUCCESS IPC_STATUS_SUCCESS +#define HAL_STATUS_FAILED 0x01 +#define HAL_STATUS_NOT_READY 0x02 +#define HAL_STATUS_NOMEM 0x03 +#define HAL_STATUS_BUSY 0x04 +#define HAL_STATUS_DONE 0x05 +#define HAL_STATUS_UNSUPPORTED 0x06 +#define HAL_STATUS_INVALID 0x07 +#define HAL_STATUS_UNHANDLED 0x08 +#define HAL_STATUS_AUTH_FAILURE 0x09 +#define HAL_STATUS_REMOTE_DEVICE_DOWN 0x0a + +#define HAL_OP_STATUS IPC_OP_STATUS + +#define HAL_MODE_DEFAULT 0x00 +#define HAL_MODE_BREDR 0x01 +#define HAL_MODE_LE 0x02 + +#define HAL_OP_REGISTER_MODULE 0x01 +struct hal_cmd_register_module { + uint8_t service_id; + uint8_t mode; +} __attribute__((packed)); + +#define HAL_OP_UNREGISTER_MODULE 0x02 +struct hal_cmd_unregister_module { + uint8_t service_id; +} __attribute__((packed)); + +/* Bluetooth Core HAL API */ + +#define HAL_OP_ENABLE 0x01 + +#define HAL_OP_DISABLE 0x02 + +#define HAL_OP_GET_ADAPTER_PROPS 0x03 + +#define HAL_OP_GET_ADAPTER_PROP 0x04 +struct hal_cmd_get_adapter_prop { + uint8_t type; +} __attribute__((packed)); + +#define HAL_MAX_NAME_LENGTH 249 + +#define HAL_PROP_ADAPTER_NAME 0x01 +#define HAL_PROP_ADAPTER_ADDR 0x02 +#define HAL_PROP_ADAPTER_UUIDS 0x03 +#define HAL_PROP_ADAPTER_CLASS 0x04 +#define HAL_PROP_ADAPTER_TYPE 0x05 +#define HAL_PROP_ADAPTER_SERVICE_REC 0x06 +#define HAL_PROP_ADAPTER_SCAN_MODE 0x07 +#define HAL_PROP_ADAPTER_BONDED_DEVICES 0x08 +#define HAL_PROP_ADAPTER_DISC_TIMEOUT 0x09 + +#define HAL_PROP_DEVICE_NAME 0x01 +#define HAL_PROP_DEVICE_ADDR 0x02 +#define HAL_PROP_DEVICE_UUIDS 0x03 +#define HAL_PROP_DEVICE_CLASS 0x04 +#define HAL_PROP_DEVICE_TYPE 0x05 +#define HAL_PROP_DEVICE_SERVICE_REC 0x06 +struct hal_prop_device_service_rec { + uint8_t uuid[16]; + uint16_t channel; + uint8_t name_len; + uint8_t name[]; +} __attribute__((packed)); + +#define HAL_PROP_DEVICE_FRIENDLY_NAME 0x0a +#define HAL_PROP_DEVICE_RSSI 0x0b +#define HAL_PROP_DEVICE_VERSION_INFO 0x0c +struct hal_prop_device_info { + uint8_t version; + uint16_t sub_version; + uint16_t manufacturer; +} __attribute__((packed)); + +#define HAL_PROP_DEVICE_TIMESTAMP 0xFF + +#define HAL_ADAPTER_SCAN_MODE_NONE 0x00 +#define HAL_ADAPTER_SCAN_MODE_CONN 0x01 +#define HAL_ADAPTER_SCAN_MODE_CONN_DISC 0x02 + +#define HAL_TYPE_BREDR 0x01 +#define HAL_TYPE_LE 0x02 +#define HAL_TYPE_DUAL 0x03 + +#define HAL_OP_SET_ADAPTER_PROP 0x05 +struct hal_cmd_set_adapter_prop { + uint8_t type; + uint16_t len; + uint8_t val[0]; +} __attribute__((packed)); + +#define HAL_OP_GET_REMOTE_DEVICE_PROPS 0x06 +struct hal_cmd_get_remote_device_props { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_GET_REMOTE_DEVICE_PROP 0x07 +struct hal_cmd_get_remote_device_prop { + uint8_t bdaddr[6]; + uint8_t type; +} __attribute__((packed)); + +#define HAL_OP_SET_REMOTE_DEVICE_PROP 0x08 +struct hal_cmd_set_remote_device_prop { + uint8_t bdaddr[6]; + uint8_t type; + uint16_t len; + uint8_t val[0]; +} __attribute__((packed)); + +#define HAL_OP_GET_REMOTE_SERVICE_REC 0x09 +struct hal_cmd_get_remote_service_rec { + uint8_t bdaddr[6]; + uint8_t uuid[16]; +} __attribute__((packed)); + +#define HAL_OP_GET_REMOTE_SERVICES 0x0a +struct hal_cmd_get_remote_services { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_START_DISCOVERY 0x0b + +#define HAL_OP_CANCEL_DISCOVERY 0x0c + +#define HAL_OP_CREATE_BOND 0x0d +struct hal_cmd_create_bond { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_REMOVE_BOND 0x0e +struct hal_cmd_remove_bond { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_CANCEL_BOND 0x0f +struct hal_cmd_cancel_bond { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_PIN_REPLY 0x10 +struct hal_cmd_pin_reply { + uint8_t bdaddr[6]; + uint8_t accept; + uint8_t pin_len; + uint8_t pin_code[16]; +} __attribute__((packed)); + +#define HAL_SSP_VARIANT_CONFIRM 0x00 +#define HAL_SSP_VARIANT_ENTRY 0x01 +#define HAL_SSP_VARIANT_CONSENT 0x02 +#define HAL_SSP_VARIANT_NOTIF 0x03 + +#define HAL_OP_SSP_REPLY 0x11 +struct hal_cmd_ssp_reply { + uint8_t bdaddr[6]; + uint8_t ssp_variant; + uint8_t accept; + uint32_t passkey; +} __attribute__((packed)); + +#define HAL_OP_DUT_MODE_CONF 0x12 +struct hal_cmd_dut_mode_conf { + uint8_t enable; +} __attribute__((packed)); + +#define HAL_OP_DUT_MODE_SEND 0x13 +struct hal_cmd_dut_mode_send { + uint16_t opcode; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_OP_LE_TEST_MODE 0x14 +struct hal_cmd_le_test_mode { + uint16_t opcode; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +/* Bluetooth Socket HAL api */ + +#define HAL_SOCK_RFCOMM 0x01 +#define HAL_SOCK_SCO 0x02 +#define HAL_SOCK_L2CAP 0x03 + +#define HAL_SOCK_FLAG_ENCRYPT 0x01 +#define HAL_SOCK_FLAG_AUTH 0x02 + +#define HAL_OP_SOCKET_LISTEN 0x01 +struct hal_cmd_socket_listen { + uint8_t type; + uint8_t name[256]; + uint8_t uuid[16]; + int32_t channel; + uint8_t flags; +} __attribute__((packed)); + +#define HAL_OP_SOCKET_CONNECT 0x02 +struct hal_cmd_socket_connect { + uint8_t bdaddr[6]; + uint8_t type; + uint8_t uuid[16]; + int32_t channel; + uint8_t flags; +} __attribute__((packed)); + +/* Bluetooth HID Host HAL API */ + +#define HAL_OP_HIDHOST_CONNECT 0x01 +struct hal_cmd_hidhost_connect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_DISCONNECT 0x02 +struct hal_cmd_hidhost_disconnect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_VIRTUAL_UNPLUG 0x03 +struct hal_cmd_hidhost_virtual_unplug { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_SET_INFO 0x04 +struct hal_cmd_hidhost_set_info { + uint8_t bdaddr[6]; + uint8_t attr; + uint8_t subclass; + uint8_t app_id; + uint16_t vendor; + uint16_t product; + uint16_t country; + uint16_t descr_len; + uint8_t descr[0]; +} __attribute__((packed)); + +#define HAL_HIDHOST_REPORT_PROTOCOL 0x00 +#define HAL_HIDHOST_BOOT_PROTOCOL 0x01 +#define HAL_HIDHOST_UNSUPPORTED_PROTOCOL 0xff + +#define HAL_OP_HIDHOST_GET_PROTOCOL 0x05 +struct hal_cmd_hidhost_get_protocol { + uint8_t bdaddr[6]; + uint8_t mode; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_SET_PROTOCOL 0x06 +struct hal_cmd_hidhost_set_protocol { + uint8_t bdaddr[6]; + uint8_t mode; +} __attribute__((packed)); + +#define HAL_HIDHOST_INPUT_REPORT 0x01 +#define HAL_HIDHOST_OUTPUT_REPORT 0x02 +#define HAL_HIDHOST_FEATURE_REPORT 0x03 + +#define HAL_OP_HIDHOST_GET_REPORT 0x07 +struct hal_cmd_hidhost_get_report { + uint8_t bdaddr[6]; + uint8_t type; + uint8_t id; + uint16_t buf_size; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_SET_REPORT 0x08 +struct hal_cmd_hidhost_set_report { + uint8_t bdaddr[6]; + uint8_t type; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_OP_HIDHOST_SEND_DATA 0x09 +struct hal_cmd_hidhost_send_data { + uint8_t bdaddr[6]; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +/* a2dp HAL API */ + +#define HAL_OP_A2DP_CONNECT 0x01 +struct hal_cmd_a2dp_connect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_A2DP_DISCONNECT 0x02 +struct hal_cmd_a2dp_disconnect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +/* PAN HAL API */ + +/* PAN Roles */ +#define HAL_PAN_ROLE_NONE 0x00 +#define HAL_PAN_ROLE_NAP 0x01 +#define HAL_PAN_ROLE_PANU 0x02 + +/* PAN Control states */ +#define HAL_PAN_CTRL_ENABLED 0x00 +#define HAL_PAN_CTRL_DISABLED 0x01 + +/* PAN Connection states */ +#define HAL_PAN_STATE_CONNECTED 0x00 +#define HAL_PAN_STATE_CONNECTING 0x01 +#define HAL_PAN_STATE_DISCONNECTED 0x02 +#define HAL_PAN_STATE_DISCONNECTING 0x03 + +/* PAN status values */ +#define HAL_PAN_STATUS_FAIL 0x01 +#define HAL_PAN_STATUS_NOT_READY 0x02 +#define HAL_PAN_STATUS_NO_MEMORY 0x03 +#define HAL_PAN_STATUS_BUSY 0x04 +#define HAL_PAN_STATUS_DONE 0x05 +#define HAL_PAN_STATUS_UNSUPORTED 0x06 +#define HAL_PAN_STATUS_INVAL 0x07 +#define HAL_PAN_STATUS_UNHANDLED 0x08 +#define HAL_PAN_STATUS_AUTH_FAILED 0x09 +#define HAL_PAN_STATUS_DEVICE_DOWN 0x0A + +#define HAL_OP_PAN_ENABLE 0x01 +struct hal_cmd_pan_enable { + uint8_t local_role; +} __attribute__((packed)); + +#define HAL_OP_PAN_GET_ROLE 0x02 +struct hal_rsp_pan_get_role { + uint8_t local_role; +} __attribute__((packed)); + +#define HAL_OP_PAN_CONNECT 0x03 +struct hal_cmd_pan_connect { + uint8_t bdaddr[6]; + uint8_t local_role; + uint8_t remote_role; +} __attribute__((packed)); + +#define HAL_OP_PAN_DISCONNECT 0x04 +struct hal_cmd_pan_disconnect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_HEALTH_MDEP_ROLE_SOURCE 0x00 +#define HAL_HEALTH_MDEP_ROLE_SINK 0x01 + +#define HAL_HEALTH_CHANNEL_TYPE_RELIABLE 0x00 +#define HAL_HEALTH_CHANNEL_TYPE_STREAMING 0x01 +#define HAL_HEALTH_CHANNEL_TYPE_ANY 0x02 + +#define HAL_OP_HEALTH_REG_APP 0x01 +struct hal_cmd_health_reg_app { + uint8_t num_of_mdep; + uint16_t app_name_off; + uint16_t provider_name_off; + uint16_t service_name_off; + uint16_t service_descr_off; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +struct hal_rsp_health_reg_app { + uint16_t app_id; +} __attribute__((packed)); + +#define HAL_OP_HEALTH_MDEP 0x02 +struct hal_cmd_health_mdep { + uint16_t app_id; + uint8_t role; + uint16_t data_type; + uint8_t channel_type; + uint16_t descr_len; + uint8_t descr[0]; +} __attribute__((packed)); + +#define HAL_OP_HEALTH_UNREG_APP 0x03 +struct hal_cmd_health_unreg_app { + uint16_t app_id; +} __attribute__((packed)); + +#define HAL_OP_HEALTH_CONNECT_CHANNEL 0x04 +struct hal_cmd_health_connect_channel { + uint16_t app_id; + uint8_t bdaddr[6]; + uint8_t mdep_index; +} __attribute__((packed)); + +struct hal_rsp_health_connect_channel { + uint16_t channel_id; +} __attribute__((packed)); + +#define HAL_OP_HEALTH_DESTROY_CHANNEL 0x05 +struct hal_cmd_health_destroy_channel { + uint16_t channel_id; +} __attribute__((packed)); + +/* Handsfree HAL API */ + +#define HAL_MODE_HANDSFREE_HSP_ONLY HAL_MODE_DEFAULT +#define HAL_MODE_HANDSFREE_HFP 0x01 +#define HAL_MODE_HANDSFREE_HFP_WBS 0x02 + +#define HAL_OP_HANDSFREE_CONNECT 0x01 +struct hal_cmd_handsfree_connect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_DISCONNECT 0x02 +struct hal_cmd_handsfree_disconnect { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_CONNECT_AUDIO 0x03 +struct hal_cmd_handsfree_connect_audio { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_DISCONNECT_AUDIO 0x04 +struct hal_cmd_handsfree_disconnect_audio { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_START_VR 0x05 + +#define HAL_OP_HANDSFREE_STOP_VR 0x06 + +#define HAL_HANDSFREE_VOLUME_TYPE_SPEAKER 0x00 +#define HAL_HANDSFREE_VOLUME_TYPE_MIC 0x01 + +#define HAL_OP_HANDSFREE_VOLUME_CONTROL 0x07 +struct hal_cmd_handsfree_volume_control { + uint8_t type; + uint8_t volume; +} __attribute__((packed)); + +#define HAL_HANDSFREE_NETWORK_STATE_NOT_AVAILABLE 0x00 +#define HAL_HANDSFREE_NETWORK_STATE_AVAILABLE 0x01 + +#define HAL_HANDSFREE_SERVICE_TYPE_HOME 0x00 +#define HAL_HANDSFREE_SERVICE_TYPE_ROAMING 0x01 + +#define HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF 0x08 +struct hal_cmd_handsfree_device_status_notif { + uint8_t state; + uint8_t type; + uint8_t signal; + uint8_t battery; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_COPS_RESPONSE 0x09 +struct hal_cmd_handsfree_cops_response { + uint16_t len; + uint8_t buf[0]; +} __attribute__((packed)); + +#define HAL_HANDSFREE_CALL_STATE_ACTIVE 0x00 +#define HAL_HANDSFREE_CALL_STATE_HELD 0x01 +#define HAL_HANDSFREE_CALL_STATE_DIALING 0x02 +#define HAL_HANDSFREE_CALL_STATE_ALERTING 0x03 +#define HAL_HANDSFREE_CALL_STATE_INCOMING 0x04 +#define HAL_HANDSFREE_CALL_STATE_WAITING 0x05 +#define HAL_HANDSFREE_CALL_STATE_IDLE 0x06 + +#define HAL_OP_HANDSFREE_CIND_RESPONSE 0x0A +struct hal_cmd_handsfree_cind_response { + uint8_t svc; + uint8_t num_active; + uint8_t num_held; + uint8_t state; + uint8_t signal; + uint8_t roam; + uint8_t batt_chg; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE 0x0B +struct hal_cmd_handsfree_formatted_at_response { + uint16_t len; + uint8_t buf[0]; +} __attribute__((packed)); + +#define HAL_HANDSFREE_AT_RESPONSE_ERROR 0x00 +#define HAL_HANDSFREE_AT_RESPONSE_OK 0x01 + +#define HAL_OP_HANDSFREE_AT_RESPONSE 0x0C +struct hal_cmd_handsfree_at_response { + uint8_t response; + uint8_t error; +} __attribute__((packed)); + +#define HAL_HANDSFREE_CALL_DIRECTION_OUTGOING 0x00 +#define HAL_HANDSFREE_CALL_DIRECTION_INCOMING 0x01 + +#define HAL_HANDSFREE_CALL_TYPE_VOICE 0x00 +#define HAL_HANDSFREE_CALL_TYPE_DATA 0x01 +#define HAL_HANDSFREE_CALL_TYPE_FAX 0x02 + +#define HAL_HANDSFREE_CALL_MPTY_TYPE_SINGLE 0x00 +#define HAL_HANDSFREE_CALL_MPTY_TYPE_MULTI 0x01 + +#define HAL_HANDSFREE_CALL_ADDRTYPE_UNKNOWN 0x81 +#define HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL 0x91 + +#define HAL_OP_HANDSFREE_CLCC_RESPONSE 0x0D +struct hal_cmd_handsfree_clcc_response { + uint8_t index; + uint8_t dir; + uint8_t state; + uint8_t mode; + uint8_t mpty; + uint8_t type; + uint16_t number_len; + uint8_t number[0]; +} __attribute__((packed)); + +#define HAL_OP_HANDSFREE_PHONE_STATE_CHANGE 0x0E +struct hal_cmd_handsfree_phone_state_change { + uint8_t num_active; + uint8_t num_held; + uint8_t state; + uint8_t type; + uint16_t number_len; + uint8_t number[0]; +} __attribute__((packed)); + +/* GATT HAL API */ + +#define HAL_OP_GATT_CLIENT_REGISTER 0x01 +struct hal_cmd_gatt_client_register { + uint8_t uuid[16]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_UNREGISTER 0x02 +struct hal_cmd_gatt_client_unregister { + int32_t client_if; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_SCAN 0x03 +struct hal_cmd_gatt_client_scan { + int32_t client_if; + uint8_t start; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_CONNECT 0x04 +struct hal_cmd_gatt_client_connect { + int32_t client_if; + uint8_t bdaddr[6]; + uint8_t is_direct; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_DISCONNECT 0x05 +struct hal_cmd_gatt_client_disconnect { + int32_t client_if; + uint8_t bdaddr[6]; + int32_t conn_id; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_LISTEN 0x06 +struct hal_cmd_gatt_client_listen { + int32_t client_if; + uint8_t start; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_REFRESH 0x07 +struct hal_cmd_gatt_client_refresh { + int32_t client_if; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_SEARCH_SERVICE 0x08 +struct hal_cmd_gatt_client_search_service { + int32_t conn_id; + uint8_t filtered; + uint8_t filter_uuid[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_GET_INCLUDED_SERVICE 0x09 +struct hal_gatt_srvc_id { + uint8_t uuid[16]; + uint8_t inst_id; + uint8_t is_primary; +} __attribute__((packed)); + +struct hal_cmd_gatt_client_get_included_service { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + uint8_t continuation; + struct hal_gatt_srvc_id incl_srvc_id[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_GET_CHARACTERISTIC 0x0a +struct hal_gatt_gatt_id { + uint8_t uuid[16]; + uint8_t inst_id; +} __attribute__((packed)); + +struct hal_cmd_gatt_client_get_characteristic { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + uint8_t continuation; + struct hal_gatt_gatt_id char_id[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_GET_DESCRIPTOR 0x0b +struct hal_cmd_gatt_client_get_descriptor { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + uint8_t continuation; + struct hal_gatt_gatt_id descr_id[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_READ_CHARACTERISTIC 0x0c +struct hal_cmd_gatt_client_read_characteristic { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + int32_t auth_req; +} __attribute__((packed)); + +#define GATT_WRITE_TYPE_NO_RESPONSE 0x01 +#define GATT_WRITE_TYPE_DEFAULT 0x02 +#define GATT_WRITE_TYPE_PREPARE 0x03 +#define GATT_WRITE_TYPE_SIGNED 0x04 + +#define HAL_OP_GATT_CLIENT_WRITE_CHARACTERISTIC 0x0d +struct hal_cmd_gatt_client_write_characteristic { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + int32_t write_type; + int32_t len; + int32_t auth_req; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_READ_DESCRIPTOR 0x0e +struct hal_cmd_gatt_client_read_descriptor { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; + int32_t auth_req; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_WRITE_DESCRIPTOR 0x0f +struct hal_cmd_gatt_client_write_descriptor { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; + int32_t write_type; + int32_t len; + int32_t auth_req; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_EXECUTE_WRITE 0x10 +struct hal_cmd_gatt_client_execute_write { + int32_t conn_id; + int32_t execute; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_REGISTER_FOR_NOTIFICATION 0x11 +struct hal_cmd_gatt_client_register_for_notification { + int32_t client_if; + uint8_t bdaddr[6]; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_DEREGISTER_FOR_NOTIFICATION 0x12 +struct hal_cmd_gatt_client_deregister_for_notification { + int32_t client_if; + uint8_t bdaddr[6]; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_READ_REMOTE_RSSI 0x13 +struct hal_cmd_gatt_client_read_remote_rssi { + int32_t client_if; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_GET_DEVICE_TYPE 0x14 +struct hal_cmd_gatt_client_get_device_type { + uint8_t bdaddr[6]; +} __attribute__((packed)); + +struct hal_rsp_gatt_client_get_device_type { + uint8_t type; +} __attribute__((packed)); + +#define HAL_OP_GATT_CLIENT_SET_ADV_DATA 0x015 +struct hal_cmd_gatt_client_set_adv_data { + int32_t server_if; + uint8_t set_scan_rsp; + uint8_t include_name; + uint8_t include_txpower; + int32_t min_interval; + int32_t max_interval; + int32_t appearance; + uint16_t manufacturer_len; + uint16_t service_data_len; + uint16_t service_uuid_len; + uint8_t data[0]; +} __attribute__((packed)); + +#define GATT_CLIENT_TEST_CMD_ENABLE 0x01 +#define GATT_CLIENT_TEST_CMD_CONNECT 0x02 +#define GATT_CLIENT_TEST_CMD_DISCONNECT 0x03 +#define GATT_CLIENT_TEST_CMD_DISCOVER 0x04 +#define GATT_CLIENT_TEST_CMD_READ 0xe0 +#define GATT_CLIENT_TEST_CMD_WRITE 0xe1 +#define GATT_CLIENT_TEST_CMD_INCREASE_SECURITY 0xe2 +#define GATT_CLIENT_TEST_CMD_PAIRING_CONFIG 0xf0 + +#define HAL_OP_GATT_CLIENT_TEST_COMMAND 0x16 +struct hal_cmd_gatt_client_test_command { + int32_t command; + uint8_t bda1[6]; + uint8_t uuid1[16]; + uint16_t u1; + uint16_t u2; + uint16_t u3; + uint16_t u4; + uint16_t u5; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_REGISTER 0x17 +struct hal_cmd_gatt_server_register { + uint8_t uuid[16]; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_UNREGISTER 0x18 +struct hal_cmd_gatt_server_unregister { + int32_t server_if; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_CONNECT 0x19 +struct hal_cmd_gatt_server_connect { + int32_t server_if; + uint8_t bdaddr[6]; + uint8_t is_direct; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_DISCONNECT 0x1a +struct hal_cmd_gatt_server_disconnect { + int32_t server_if; + uint8_t bdaddr[6]; + int32_t conn_id; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_ADD_SERVICE 0x1b +struct hal_cmd_gatt_server_add_service { + int32_t server_if; + struct hal_gatt_srvc_id srvc_id; + int32_t num_handles; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_ADD_INC_SERVICE 0x1c +struct hal_cmd_gatt_server_add_inc_service { + int32_t server_if; + int32_t service_handle; + int32_t included_handle; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_ADD_CHARACTERISTIC 0x1d +struct hal_cmd_gatt_server_add_characteristic { + int32_t server_if; + int32_t service_handle; + uint8_t uuid[16]; + int32_t properties; + int32_t permissions; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_ADD_DESCRIPTOR 0x1e +struct hal_cmd_gatt_server_add_descriptor { + int32_t server_if; + int32_t service_handle; + uint8_t uuid[16]; + int32_t permissions; +} __attribute__((packed)); + +#define GATT_SERVER_TRANSPORT_LE 0x00 +#define GATT_SERVER_TRANSPORT_BREDR 0x01 +#define GATT_SERVER_TRANSPORT_LE_BREDR 0x02 + +#define HAL_OP_GATT_SERVER_START_SERVICE 0x1f +struct hal_cmd_gatt_server_start_service { + int32_t server_if; + int32_t service_handle; + int32_t transport; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_STOP_SERVICE 0x20 +struct hal_cmd_gatt_server_stop_service { + int32_t server_if; + int32_t service_handle; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_DELETE_SERVICE 0x21 +struct hal_cmd_gatt_server_delete_service { + int32_t server_if; + int32_t service_handle; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_SEND_INDICATION 0x22 +struct hal_cmd_gatt_server_send_indication { + int32_t server_if; + int32_t attribute_handle; + int32_t conn_id; + int32_t len; + int32_t confirm; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_OP_GATT_SERVER_SEND_RESPONSE 0x23 +struct hal_cmd_gatt_server_send_response { + int32_t conn_id; + int32_t trans_id; + uint16_t handle; + uint16_t offset; + uint8_t auth_req; + int32_t status; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +/* Notifications and confirmations */ + +#define HAL_POWER_OFF 0x00 +#define HAL_POWER_ON 0x01 + +#define HAL_EV_ADAPTER_STATE_CHANGED 0x81 +struct hal_ev_adapter_state_changed { + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_ADAPTER_PROPS_CHANGED 0x82 +struct hal_property { + uint8_t type; + uint16_t len; + uint8_t val[0]; +} __attribute__((packed)); +struct hal_ev_adapter_props_changed { + uint8_t status; + uint8_t num_props; + struct hal_property props[0]; +} __attribute__((packed)); + +#define HAL_EV_REMOTE_DEVICE_PROPS 0x83 +struct hal_ev_remote_device_props { + uint8_t status; + uint8_t bdaddr[6]; + uint8_t num_props; + struct hal_property props[0]; +} __attribute__((packed)); + +#define HAL_EV_DEVICE_FOUND 0x84 +struct hal_ev_device_found { + uint8_t num_props; + struct hal_property props[0]; +} __attribute__((packed)); + +#define HAL_DISCOVERY_STATE_STOPPED 0x00 +#define HAL_DISCOVERY_STATE_STARTED 0x01 + +#define HAL_EV_DISCOVERY_STATE_CHANGED 0x85 +struct hal_ev_discovery_state_changed { + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_PIN_REQUEST 0x86 +struct hal_ev_pin_request { + uint8_t bdaddr[6]; + uint8_t name[249]; + uint32_t class_of_dev; +} __attribute__((packed)); + +#define HAL_EV_SSP_REQUEST 0x87 +struct hal_ev_ssp_request { + uint8_t bdaddr[6]; + uint8_t name[249]; + uint32_t class_of_dev; + uint8_t pairing_variant; + uint32_t passkey; +} __attribute__((packed)); + +#define HAL_BOND_STATE_NONE 0 +#define HAL_BOND_STATE_BONDING 1 +#define HAL_BOND_STATE_BONDED 2 + +#define HAL_EV_BOND_STATE_CHANGED 0x88 +struct hal_ev_bond_state_changed { + uint8_t status; + uint8_t bdaddr[6]; + uint8_t state; +} __attribute__((packed)); + +#define HAL_ACL_STATE_CONNECTED 0x00 +#define HAL_ACL_STATE_DISCONNECTED 0x01 + +#define HAL_EV_ACL_STATE_CHANGED 0x89 +struct hal_ev_acl_state_changed { + uint8_t status; + uint8_t bdaddr[6]; + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_DUT_MODE_RECEIVE 0x8a +struct hal_ev_dut_mode_receive { + uint16_t opcode; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_EV_LE_TEST_MODE 0x8b +struct hal_ev_le_test_mode { + uint8_t status; + uint16_t num_packets; +} __attribute__((packed)); + +#define HAL_HIDHOST_STATE_CONNECTED 0x00 +#define HAL_HIDHOST_STATE_CONNECTING 0x01 +#define HAL_HIDHOST_STATE_DISCONNECTED 0x02 +#define HAL_HIDHOST_STATE_DISCONNECTING 0x03 +#define HAL_HIDHOST_STATE_NO_HID 0x07 +#define HAL_HIDHOST_STATE_FAILED 0x08 +#define HAL_HIDHOST_STATE_UNKNOWN 0x09 + +#define HAL_EV_HIDHOST_CONN_STATE 0x81 +struct hal_ev_hidhost_conn_state { + uint8_t bdaddr[6]; + uint8_t state; +} __attribute__((packed)); + +#define HAL_HIDHOST_STATUS_OK 0x00 +#define HAL_HIDHOST_GENERAL_ERROR 0x06 + +#define HAL_EV_HIDHOST_INFO 0x82 +struct hal_ev_hidhost_info { + uint8_t bdaddr[6]; + uint8_t attr; + uint8_t subclass; + uint8_t app_id; + uint16_t vendor; + uint16_t product; + uint16_t version; + uint8_t country; + uint16_t descr_len; + uint8_t descr[884]; +} __attribute__((packed)); + +#define HAL_EV_HIDHOST_PROTO_MODE 0x83 +struct hal_ev_hidhost_proto_mode { + uint8_t bdaddr[6]; + uint8_t status; + uint8_t mode; +} __attribute__((packed)); + +#define HAL_EV_HIDHOST_IDLE_TIME 0x84 +struct hal_ev_hidhost_idle_time { + uint8_t bdaddr[6]; + uint8_t status; + uint32_t idle_rate; +} __attribute__((packed)); + +#define HAL_EV_HIDHOST_GET_REPORT 0x85 +struct hal_ev_hidhost_get_report { + uint8_t bdaddr[6]; + uint8_t status; + uint16_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_EV_HIDHOST_VIRTUAL_UNPLUG 0x86 +struct hal_ev_hidhost_virtual_unplug { + uint8_t bdaddr[6]; + uint8_t status; +} __attribute__((packed)); + +#define HAL_EV_PAN_CTRL_STATE 0x81 +struct hal_ev_pan_ctrl_state { + uint8_t state; + uint8_t status; + uint8_t local_role; + uint8_t name[17]; +} __attribute__((packed)); + +#define HAL_EV_PAN_CONN_STATE 0x82 +struct hal_ev_pan_conn_state { + uint8_t state; + uint8_t status; + uint8_t bdaddr[6]; + uint8_t local_role; + uint8_t remote_role; +} __attribute__((packed)); + +#define HAL_HEALTH_APP_REG_SUCCESS 0x00 +#define HAL_HEALTH_APP_REG_FAILED 0x01 +#define HAL_HEALTH_APP_DEREG_SUCCESS 0x02 +#define HAL_HEALTH_APP_DEREG_FAILED 0x03 + +#define HAL_HEALTH_CHANNEL_CONNECTING 0x00 +#define HAL_HEALTH_CHANNEL_CONNECTED 0x01 +#define HAL_HEALTH_CHANNEL_DISCONNECTING 0x02 +#define HAL_HEALTH_CHANNEL_DISCONNECTED 0x03 +#define HAL_HEALTH_CHANNEL_DESTROYED 0x04 + +#define HAL_EV_HEALTH_APP_REG_STATE 0x81 +struct hal_ev_health_app_reg_state { + uint16_t id; + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_HEALTH_CHANNEL_STATE 0x82 +struct hal_ev_health_channel_state { + uint16_t app_id; + uint8_t bdaddr[6]; + uint8_t mdep_index; + uint16_t channel_id; + uint8_t channel_state; +} __attribute__((packed)); + +#define HAL_A2DP_STATE_DISCONNECTED 0x00 +#define HAL_A2DP_STATE_CONNECTING 0x01 +#define HAL_A2DP_STATE_CONNECTED 0x02 +#define HAL_A2DP_STATE_DISCONNECTING 0x03 + +#define HAL_EV_A2DP_CONN_STATE 0x81 +struct hal_ev_a2dp_conn_state { + uint8_t state; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_AUDIO_SUSPEND 0x00 +#define HAL_AUDIO_STOPPED 0x01 +#define HAL_AUDIO_STARTED 0x02 + +#define HAL_EV_A2DP_AUDIO_STATE 0x82 +struct hal_ev_a2dp_audio_state { + uint8_t state; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED 0x00 +#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTING 0x01 +#define HAL_EV_HANDSFREE_CONN_STATE_CONNECTED 0x02 +#define HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED 0x03 +#define HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING 0x04 + +#define HAL_EV_HANDSFREE_CONN_STATE 0x81 +struct hal_ev_handsfree_conn_state { + uint8_t state; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED 0x00 +#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING 0x01 +#define HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED 0x02 +#define HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING 0x03 + +#define HAL_EV_HANDSFREE_AUDIO_STATE 0x82 +struct hal_ev_handsfree_audio_state { + uint8_t state; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_HANDSFREE_VR_STOPPED 0x00 +#define HAL_HANDSFREE_VR_STARTED 0x01 + +#define HAL_EV_HANDSFREE_VR 0x83 +struct hal_ev_handsfree_vr_state { + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_ANSWER 0x84 + +#define HAL_EV_HANDSFREE_HANGUP 0x85 + +#define HAL_EV_HANDSFREE_VOLUME 0x86 +struct hal_ev_handsfree_volume { + uint8_t type; + uint8_t volume; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_DIAL 0x87 +struct hal_ev_handsfree_dial { + uint16_t number_len; + uint8_t number[0]; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_DTMF 0x88 +struct hal_ev_handsfree_dtmf { + uint8_t tone; +} __attribute__((packed)); + +#define HAL_HANDSFREE_NREC_STOP 0x00 +#define HAL_HANDSFREE_NREC_START 0x01 + +#define HAL_EV_HANDSFREE_NREC 0x89 +struct hal_ev_handsfree_nrec { + uint8_t nrec; +} __attribute__((packed)); + +#define HAL_HANDSFREE_CHLD_TYPE_RELEASEHELD 0x00 +#define HAL_HANDSFREE_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD 0x01 +#define HAL_HANDSFREE_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD 0x02 +#define HAL_HANDSFREE_CHLD_TYPE_ADDHELDTOCONF 0x03 + +#define HAL_EV_HANDSFREE_CHLD 0x8A +struct hal_ev_handsfree_chld { + uint8_t chld; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_CNUM 0x8B + +#define HAL_EV_HANDSFREE_CIND 0x8C + +#define HAL_EV_HANDSFREE_COPS 0x8D + +#define HAL_EV_HANDSFREE_CLCC 0x8E + +#define HAL_EV_HANDSFREE_UNKNOWN_AT 0x8F +struct hal_ev_handsfree_unknown_at { + uint16_t len; + uint8_t buf[0]; +} __attribute__((packed)); + +#define HAL_EV_HANDSFREE_HSP_KEY_PRESS 0x90 + +/* AVRCP HAL API */ + +#define HAL_AVRCP_PLAY_STATUS_STOPPED 0x00 +#define HAL_AVRCP_PLAY_STATUS_PLAYING 0x01 +#define HAL_AVRCP_PLAY_STATUS_PAUSED 0x02 +#define HAL_AVRCP_PLAY_STATUS_FWD_SEEK 0x03 +#define HAL_AVRCP_PLAY_STATUS_REV_SEEK 0x04 +#define HAL_AVRCP_PLAY_STATUS_ERROR 0xff + +#define HAL_OP_AVRCP_GET_PLAY_STATUS 0x01 +struct hal_cmd_avrcp_get_play_status { + uint8_t status; + uint32_t duration; + uint32_t position; +} __attribute__((packed)); + +#define HAL_AVRCP_PLAYER_ATTR_EQUALIZER 0x01 +#define HAL_AVRCP_PLAYER_ATTR_REPEAT 0x02 +#define HAL_AVRCP_PLAYER_ATTR_SHUFFLE 0x03 +#define HAL_AVRCP_PLAYER_ATTR_SCAN 0x04 + +#define HAL_OP_AVRCP_LIST_PLAYER_ATTRS 0x02 +struct hal_cmd_avrcp_list_player_attrs { + uint8_t number; + uint8_t attrs[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_LIST_PLAYER_VALUES 0x03 +struct hal_cmd_avrcp_list_player_values { + uint8_t number; + uint8_t values[0]; +} __attribute__((packed)); + +struct hal_avrcp_player_attr_value { + uint8_t attr; + uint8_t value; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_GET_PLAYER_ATTRS 0x04 +struct hal_cmd_avrcp_get_player_attrs { + uint8_t number; + struct hal_avrcp_player_attr_value attrs[0]; +} __attribute__((packed)); + +struct hal_avrcp_player_setting_text { + uint8_t id; + uint8_t len; + uint8_t text[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_GET_PLAYER_ATTRS_TEXT 0x05 +struct hal_cmd_avrcp_get_player_attrs_text { + uint8_t number; + struct hal_avrcp_player_setting_text attrs[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_GET_PLAYER_VALUES_TEXT 0x06 +struct hal_cmd_avrcp_get_player_values_text { + uint8_t number; + struct hal_avrcp_player_setting_text values[0]; +} __attribute__((packed)); + +#define HAL_AVRCP_MEDIA_ATTR_TITLE 0x01 +#define HAL_AVRCP_MEDIA_ATTR_ARTIST 0x02 +#define HAL_AVRCP_MEDIA_ATTR_ALBUM 0x03 +#define HAL_AVRCP_MEDIA_ATTR_TRACK_NUM 0x04 +#define HAL_AVRCP_MEDIA_ATTR_NUM_TRACKS 0x05 +#define HAL_AVRCP_MEDIA_ATTR_GENRE 0x06 +#define HAL_AVRCP_MEDIA_ATTR_DURATION 0x07 + +#define HAL_OP_AVRCP_GET_ELEMENT_ATTRS_TEXT 0x07 +struct hal_cmd_avrcp_get_element_attrs_text { + uint8_t number; + struct hal_avrcp_player_setting_text values[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_SET_PLAYER_ATTRS_VALUE 0x08 +struct hal_cmd_avrcp_set_player_attrs_value { + uint8_t status; +} __attribute__((packed)); + +#define HAL_AVRCP_EVENT_STATUS_CHANGED 0x01 +#define HAL_AVRCP_EVENT_TRACK_CHANGED 0x02 +#define HAL_AVRCP_EVENT_TRACK_REACHED_END 0x03 +#define HAL_AVRCP_EVENT_TRACK_REACHED_START 0x04 +#define HAL_AVRCP_EVENT_POSITION_CHANGED 0x05 +#define HAL_AVRCP_EVENT_SETTING_CHANGED 0x08 + +#define HAL_AVRCP_EVENT_TYPE_INTERIM 0x00 +#define HAL_AVRCP_EVENT_TYPE_CHANGED 0x01 + +#define HAL_OP_AVRCP_REGISTER_NOTIFICATION 0x09 +struct hal_cmd_avrcp_register_notification { + uint8_t event; + uint8_t type; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +#define HAL_OP_AVRCP_SET_VOLUME 0x0a +struct hal_cmd_avrcp_set_volume { + uint8_t value; +} __attribute__((packed)); + +#define HAL_AVRCP_FEATURE_NONE 0x00 +#define HAL_AVRCP_FEATURE_METADATA 0x01 +#define HAL_AVRCP_FEATURE_ABSOLUTE_VOLUME 0x02 +#define HAL_AVRCP_FEATURE_BROWSE 0x04 + +#define HAL_EV_AVRCP_REMOTE_FEATURES 0x81 +struct hal_ev_avrcp_remote_features { + uint8_t bdaddr[6]; + uint8_t features; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_PLAY_STATUS 0x82 +#define HAL_EV_AVRCP_LIST_PLAYER_ATTRS 0x83 + +#define HAL_EV_AVRCP_LIST_PLAYER_VALUES 0x84 +struct hal_ev_avrcp_list_player_values { + uint8_t attr; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_PLAYER_VALUES 0x85 +struct hal_ev_avrcp_get_player_values { + uint8_t number; + uint8_t attrs[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_PLAYER_ATTRS_TEXT 0x86 +struct hal_ev_avrcp_get_player_attrs_text { + uint8_t number; + uint8_t attrs[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_PLAYER_VALUES_TEXT 0x87 +struct hal_ev_avrcp_get_player_values_text { + uint8_t attr; + uint8_t number; + uint8_t values[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_SET_PLAYER_VALUES 0x88 +struct hal_ev_avrcp_set_player_values { + uint8_t number; + struct hal_avrcp_player_attr_value attrs[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_GET_ELEMENT_ATTRS 0x89 +struct hal_ev_avrcp_get_element_attrs { + uint8_t number; + uint8_t attrs[0]; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_REGISTER_NOTIFICATION 0x8a +struct hal_ev_avrcp_register_notification { + uint8_t event; + uint32_t param; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_VOLUME_CHANGED 0x8b +struct hal_ev_avrcp_volume_changed { + uint8_t volume; + uint8_t type; +} __attribute__((packed)); + +#define HAL_EV_AVRCP_PASSTHROUGH_CMD 0x8c +struct hal_ev_avrcp_passthrough_cmd { + uint8_t id; + uint8_t state; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_REGISTER_CLIENT 0x81 +struct hal_ev_gatt_client_register_client { + int32_t status; + int32_t client_if; + uint8_t app_uuid[16]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_SCAN_RESULT 0x82 +struct hal_ev_gatt_client_scan_result { + uint8_t bda[6]; + int32_t rssi; + uint16_t len; + uint8_t adv_data[0]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_CONNECT 0x83 +struct hal_ev_gatt_client_connect { + int32_t conn_id; + int32_t status; + int32_t client_if; + uint8_t bda[6]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_DISCONNECT 0x84 +struct hal_ev_gatt_client_disconnect { + int32_t conn_id; + int32_t status; + int32_t client_if; + uint8_t bda[6]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_SEARCH_COMPLETE 0x85 +struct hal_ev_gatt_client_search_complete { + int32_t conn_id; + int32_t status; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_SEARCH_RESULT 0x86 +struct hal_ev_gatt_client_search_result { + int32_t conn_id; + struct hal_gatt_srvc_id srvc_id; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_GET_CHARACTERISTIC 0x87 +struct hal_ev_gatt_client_get_characteristic { + int32_t conn_id; + int32_t status; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + int32_t char_prop; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_GET_DESCRIPTOR 0x88 +struct hal_ev_gatt_client_get_descriptor { + int32_t conn_id; + int32_t status; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_GET_INC_SERVICE 0X89 +struct hal_ev_gatt_client_get_inc_service { + int32_t conn_id; + int32_t status; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_srvc_id incl_srvc_id; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_REGISTER_FOR_NOTIF 0x8a +struct hal_ev_gatt_client_reg_for_notif { + int32_t conn_id; + int32_t registered; + int32_t status; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_NOTIFY 0x8b +struct hal_ev_gatt_client_notify { + int32_t conn_id; + uint8_t bda[6]; + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + uint8_t is_notify; + uint16_t len; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_READ_CHARACTERISTIC 0x8c +struct hal_gatt_read_params { + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; + uint8_t status; + uint16_t value_type; + uint16_t len; + uint8_t value[0]; +} __attribute__((packed)); + +struct hal_ev_gatt_client_read_characteristic { + int32_t conn_id; + int32_t status; + struct hal_gatt_read_params data; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_WRITE_CHARACTERISTIC 0x8d +struct hal_gatt_write_params { + struct hal_gatt_srvc_id srvc_id; + struct hal_gatt_gatt_id char_id; + struct hal_gatt_gatt_id descr_id; + uint8_t status; +} __attribute__((packed)); + +struct hal_ev_gatt_client_write_characteristic { + int32_t conn_id; + int32_t status; + struct hal_gatt_write_params data; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_READ_DESCRIPTOR 0x8e +struct hal_ev_gatt_client_read_descriptor { + int32_t conn_id; + int32_t status; + struct hal_gatt_read_params data; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_WRITE_DESCRIPTOR 0x8f +struct hal_ev_gatt_client_write_descriptor { + int32_t conn_id; + int32_t status; + struct hal_gatt_write_params data; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_EXEC_WRITE 0x90 +struct hal_ev_gatt_client_exec_write { + int32_t conn_id; + int32_t status; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_READ_REMOTE_RSSI 0x91 +struct hal_ev_gatt_client_read_remote_rssi { + int32_t client_if; + uint8_t address[6]; + int32_t rssi; + int32_t status; +} __attribute__((packed)); + +#define HAL_EV_GATT_CLIENT_LISTEN 0x92 +struct hal_ev_gatt_client_listen { + int32_t status; + int32_t server_if; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_REGISTER 0x93 +struct hal_ev_gatt_server_register { + int32_t status; + int32_t server_if; + uint8_t uuid[16]; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_CONNECTION 0x94 +struct hal_ev_gatt_server_connection { + int32_t conn_id; + int32_t server_if; + int32_t connected; + uint8_t bdaddr[6]; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_SERVICE_ADDED 0x95 +struct hal_ev_gatt_server_service_added { + int32_t status; + int32_t server_if; + struct hal_gatt_srvc_id srvc_id; + int32_t srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_INC_SRVC_ADDED 0x96 +struct hal_ev_gatt_server_inc_srvc_added { + int32_t status; + int32_t server_if; + int32_t srvc_handle; + int32_t incl_srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_CHAR_ADDED 0x97 +struct hal_ev_gatt_server_characteristic_added { + int32_t status; + int32_t server_if; + uint8_t uuid[16]; + int32_t srvc_handle; + int32_t char_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_DESCRIPTOR_ADDED 0x98 +struct hal_ev_gatt_server_descriptor_added { + int32_t status; + int32_t server_if; + uint8_t uuid[16]; + int32_t srvc_handle; + int32_t descr_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_SERVICE_STARTED 0x99 +struct hal_ev_gatt_server_service_started { + int32_t status; + int32_t server_if; + int32_t srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_SERVICE_STOPPED 0x9a +struct hal_ev_gatt_server_service_stopped { + int32_t status; + int32_t server_if; + int32_t srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_SERVICE_DELETED 0x9b +struct hal_ev_gatt_server_service_deleted { + int32_t status; + int32_t server_if; + int32_t srvc_handle; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_REQUEST_READ 0x9c +struct hal_ev_gatt_server_request_read { + int32_t conn_id; + int32_t trans_id; + uint8_t bdaddr[6]; + int32_t attr_handle; + int32_t offset; + uint8_t is_long; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_REQUEST_WRITE 0x9d +struct hal_ev_gatt_server_request_write { + int32_t conn_id; + int32_t trans_id; + uint8_t bdaddr[6]; + int32_t attr_handle; + int32_t offset; + int32_t length; + uint8_t need_rsp; + uint8_t is_prep; + uint8_t value[0]; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE 0x9e +struct hal_ev_gatt_server_request_exec_write { + int32_t conn_id; + int32_t trans_id; + uint8_t bdaddr[6]; + int32_t exec_write; +} __attribute__((packed)); + +#define HAL_EV_GATT_SERVER_RSP_CONFIRMATION 0x9f +struct hal_ev_gatt_server_rsp_confirmation { + int32_t status; + int32_t handle; +} __attribute__((packed)); + +#define HAL_GATT_PERMISSION_READ 0x0001 +#define HAL_GATT_PERMISSION_READ_ENCRYPTED 0x0002 +#define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM 0x0004 +#define HAL_GATT_PERMISSION_WRITE 0x0010 +#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED 0x0020 +#define HAL_GATT_PERMISSION_WRITE_ENCRYPTED_MITM 0x0040 +#define HAL_GATT_PERMISSION_WRITE_SIGNED 0x0080 +#define HAL_GATT_PERMISSION_WRITE_SIGNED_MITM 0x0100 + +#define HAL_GATT_AUTHENTICATION_NONE 0 +#define HAL_GATT_AUTHENTICATION_NO_MITM 1 +#define HAL_GATT_AUTHENTICATION_MITM 2 diff -Nru bluez-4.101/android/hal-pan.c bluez-5.23/android/hal-pan.c --- bluez-4.101/android/hal-pan.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-pan.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "hal-log.h" +#include "hal.h" +#include "hal-msg.h" +#include "hal-ipc.h" + +static const btpan_callbacks_t *cbs = NULL; + +static bool interface_ready(void) +{ + return cbs != NULL; +} + +static void handle_conn_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_pan_conn_state *ev = buf; + + if (cbs->connection_state_cb) + cbs->connection_state_cb(ev->state, ev->status, + (bt_bdaddr_t *) ev->bdaddr, + ev->local_role, ev->remote_role); +} + +static void handle_ctrl_state(void *buf, uint16_t len, int fd) +{ + struct hal_ev_pan_ctrl_state *ev = buf; + + /* + * FIXME: Callback declared in bt_pan.h is 'typedef void + * (*btpan_control_state_callback)(btpan_control_state_t state, + * bt_status_t error, int local_role, const char* ifname); + * But PanService.Java defined it wrong way. + * private void onControlStateChanged(int local_role, int state, + * int error, String ifname). + * First and third parameters are misplaced, so sending data according + * to PanService.Java, fix this if issue fixed in PanService.Java. + */ + if (cbs->control_state_cb) + cbs->control_state_cb(ev->local_role, ev->state, ev->status, + (char *)ev->name); +} + +/* + * handlers will be called from notification thread context, + * index in table equals to 'opcode - HAL_MINIMUM_EVENT' + */ +static const struct hal_ipc_handler ev_handlers[] = { + /* HAL_EV_PAN_CTRL_STATE */ + { handle_ctrl_state, false, sizeof(struct hal_ev_pan_ctrl_state) }, + /* HAL_EV_PAN_CONN_STATE */ + { handle_conn_state, false, sizeof(struct hal_ev_pan_conn_state) }, +}; + +static bt_status_t pan_enable(int local_role) +{ + struct hal_cmd_pan_enable cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + cmd.local_role = local_role; + + return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static int pan_get_local_role(void) +{ + struct hal_rsp_pan_get_role rsp; + size_t len = sizeof(rsp); + bt_status_t status; + + DBG(""); + + if (!interface_ready()) + return BTPAN_ROLE_NONE; + + status = hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, 0, NULL, + &len, &rsp, NULL); + if (status != BT_STATUS_SUCCESS) + return BTPAN_ROLE_NONE; + + return rsp.local_role; +} + +static bt_status_t pan_connect(const bt_bdaddr_t *bd_addr, int local_role, + int remote_role) +{ + struct hal_cmd_pan_connect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + cmd.local_role = local_role; + cmd.remote_role = remote_role; + + return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t pan_disconnect(const bt_bdaddr_t *bd_addr) +{ + struct hal_cmd_pan_disconnect cmd; + + DBG(""); + + if (!interface_ready()) + return BT_STATUS_NOT_READY; + + memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, + sizeof(cmd), &cmd, NULL, NULL, NULL); +} + +static bt_status_t pan_init(const btpan_callbacks_t *callbacks) +{ + struct hal_cmd_register_module cmd; + int ret; + + DBG(""); + + if (interface_ready()) + return BT_STATUS_DONE; + + cbs = callbacks; + + hal_ipc_register(HAL_SERVICE_ID_PAN, ev_handlers, + sizeof(ev_handlers)/sizeof(ev_handlers[0])); + + cmd.service_id = HAL_SERVICE_ID_PAN; + cmd.mode = HAL_MODE_DEFAULT; + + ret = hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + if (ret != BT_STATUS_SUCCESS) { + cbs = NULL; + hal_ipc_unregister(HAL_SERVICE_ID_PAN); + } + + return ret; +} + +static void pan_cleanup(void) +{ + struct hal_cmd_unregister_module cmd; + + DBG(""); + + if (!interface_ready()) + return; + + cbs = NULL; + + cmd.service_id = HAL_SERVICE_ID_PAN; + + hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + sizeof(cmd), &cmd, NULL, NULL, NULL); + + hal_ipc_unregister(HAL_SERVICE_ID_PAN); +} + +static btpan_interface_t pan_if = { + .size = sizeof(pan_if), + .init = pan_init, + .enable = pan_enable, + .get_local_role = pan_get_local_role, + .connect = pan_connect, + .disconnect = pan_disconnect, + .cleanup = pan_cleanup +}; + +btpan_interface_t *bt_get_pan_interface(void) +{ + return &pan_if; +} diff -Nru bluez-4.101/android/hal-sco.c bluez-5.23/android/hal-sco.c --- bluez-4.101/android/hal-sco.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-sco.c 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,1374 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../src/shared/util.h" +#include "sco-msg.h" +#include "ipc-common.h" +#include "hal-log.h" + +#define AUDIO_STREAM_DEFAULT_RATE 44100 +#define AUDIO_STREAM_SCO_RATE 8000 +#define AUDIO_STREAM_DEFAULT_FORMAT AUDIO_FORMAT_PCM_16_BIT + +#define OUT_BUFFER_SIZE 2560 +#define OUT_STREAM_FRAMES 2560 +#define IN_STREAM_FRAMES 5292 + +#define SOCKET_POLL_TIMEOUT_MS 500 + +static int listen_sk = -1; +static int ipc_sk = -1; + +static int sco_fd = -1; +static uint16_t sco_mtu = 0; +static pthread_mutex_t sco_mutex = PTHREAD_MUTEX_INITIALIZER; + +static pthread_t ipc_th = 0; +static pthread_mutex_t sk_mutex = PTHREAD_MUTEX_INITIALIZER; + +static struct sco_stream_in *sco_stream_in = NULL; +static struct sco_stream_out *sco_stream_out = NULL; + +struct sco_audio_config { + uint32_t rate; + uint32_t channels; + uint32_t frame_num; + audio_format_t format; +}; + +struct sco_stream_out { + struct audio_stream_out stream; + + struct sco_audio_config cfg; + + uint8_t *downmix_buf; + uint8_t *cache; + size_t cache_len; + + size_t samples; + struct timespec start; + + struct resampler_itfe *resampler; + int16_t *resample_buf; + uint32_t resample_frame_num; +}; + +static void sco_close_socket(void) +{ + DBG("sco fd %d", sco_fd); + + if (sco_fd < 0) + return; + + shutdown(sco_fd, SHUT_RDWR); + close(sco_fd); + sco_fd = -1; +} + +struct sco_stream_in { + struct audio_stream_in stream; + + struct sco_audio_config cfg; + + struct resampler_itfe *resampler; + int16_t *resample_buf; + uint32_t resample_frame_num; +}; + +struct sco_dev { + struct audio_hw_device dev; + struct sco_stream_out *out; + struct sco_stream_in *in; +}; + +/* + * return the minimum frame numbers from resampling between BT stack's rate + * and audio flinger's. For output stream, 'output' shall be true, otherwise + * false for input streams at audio flinger side. + */ +static size_t get_resample_frame_num(uint32_t sco_rate, uint32_t rate, + size_t frame_num, bool output) +{ + size_t resample_frames_num = frame_num * sco_rate / rate + output; + + DBG("resampler: sco_rate %d frame_num %zd rate %d resample frames %zd", + sco_rate, frame_num, rate, resample_frames_num); + + return resample_frames_num; +} + +/* SCO IPC functions */ + +static int sco_ipc_cmd(uint8_t service_id, uint8_t opcode, uint16_t len, + void *param, size_t *rsp_len, void *rsp, int *fd) +{ + ssize_t ret; + struct msghdr msg; + struct iovec iv[2]; + struct ipc_hdr cmd; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct ipc_status s; + size_t s_len = sizeof(s); + + pthread_mutex_lock(&sk_mutex); + + if (ipc_sk < 0) { + error("sco: Invalid cmd socket passed to sco_ipc_cmd"); + goto failed; + } + + if (!rsp || !rsp_len) { + memset(&s, 0, s_len); + rsp_len = &s_len; + rsp = &s; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.service_id = service_id; + cmd.opcode = opcode; + cmd.len = len; + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = param; + iv[1].iov_len = len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + ret = sendmsg(ipc_sk, &msg, 0); + if (ret < 0) { + error("sco: Sending command failed:%s", strerror(errno)); + goto failed; + } + + /* socket was shutdown */ + if (ret == 0) { + error("sco: Command socket closed"); + goto failed; + } + + memset(&msg, 0, sizeof(msg)); + memset(&cmd, 0, sizeof(cmd)); + + iv[0].iov_base = &cmd; + iv[0].iov_len = sizeof(cmd); + + iv[1].iov_base = rsp; + iv[1].iov_len = *rsp_len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + if (fd) { + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + } + + ret = recvmsg(ipc_sk, &msg, 0); + if (ret < 0) { + error("sco: Receiving command response failed:%s", + strerror(errno)); + goto failed; + } + + if (ret < (ssize_t) sizeof(cmd)) { + error("sco: Too small response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.service_id != service_id) { + error("sco: Invalid service id (%u vs %u)", cmd.service_id, + service_id); + goto failed; + } + + if (ret != (ssize_t) (sizeof(cmd) + cmd.len)) { + error("sco: Malformed response received(%zd bytes)", ret); + goto failed; + } + + if (cmd.opcode != opcode && cmd.opcode != SCO_OP_STATUS) { + error("sco: Invalid opcode received (%u vs %u)", + cmd.opcode, opcode); + goto failed; + } + + if (cmd.opcode == SCO_OP_STATUS) { + struct ipc_status *s = rsp; + + if (sizeof(*s) != cmd.len) { + error("sco: Invalid status length"); + goto failed; + } + + if (s->code == SCO_STATUS_SUCCESS) { + error("sco: Invalid success status response"); + goto failed; + } + + pthread_mutex_unlock(&sk_mutex); + + return s->code; + } + + pthread_mutex_unlock(&sk_mutex); + + /* Receive auxiliary data in msg */ + if (fd) { + struct cmsghdr *cmsg; + + *fd = -1; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET + && cmsg->cmsg_type == SCM_RIGHTS) { + memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); + break; + } + } + + if (*fd < 0) + goto failed; + } + + *rsp_len = cmd.len; + + return SCO_STATUS_SUCCESS; + +failed: + /* Some serious issue happen on IPC - recover */ + shutdown(ipc_sk, SHUT_RDWR); + pthread_mutex_unlock(&sk_mutex); + + return SCO_STATUS_FAILED; +} + +static int ipc_get_sco_fd(void) +{ + int ret = SCO_STATUS_SUCCESS; + + pthread_mutex_lock(&sco_mutex); + + if (sco_fd < 0) { + struct sco_rsp_get_fd rsp; + size_t rsp_len = sizeof(rsp); + + DBG("Getting SCO fd"); + + ret = sco_ipc_cmd(SCO_SERVICE_ID, SCO_OP_GET_FD, 0, NULL, + &rsp_len, &rsp, &sco_fd); + + /* Sometimes mtu returned is wrong */ + sco_mtu = /* rsp.mtu */ 48; + } + + pthread_mutex_unlock(&sco_mutex); + + return ret; +} + +/* Audio stream functions */ + +static void downmix_to_mono(struct sco_stream_out *out, const uint8_t *buffer, + size_t frame_num) +{ + const int16_t *input = (const void *) buffer; + int16_t *output = (void *) out->downmix_buf; + size_t i; + + for (i = 0; i < frame_num; i++) { + int16_t l = le16_to_cpu(get_unaligned(&input[i * 2])); + int16_t r = le16_to_cpu(get_unaligned(&input[i * 2 + 1])); + + put_unaligned(cpu_to_le16((l + r) >> 1), &output[i]); + } +} + +static uint64_t timespec_diff_us(struct timespec *a, struct timespec *b) +{ + struct timespec res; + + res.tv_sec = a->tv_sec - b->tv_sec; + res.tv_nsec = a->tv_nsec - b->tv_nsec; + + if (res.tv_nsec < 0) { + res.tv_sec--; + res.tv_nsec += 1000000000ll; /* 1sec */ + } + + return res.tv_sec * 1000000ll + res.tv_nsec / 1000ll; +} + +static bool write_data(struct sco_stream_out *out, const uint8_t *buffer, + size_t bytes) +{ + struct pollfd pfd; + size_t len, written = 0; + int ret; + uint8_t *p; + uint64_t audio_sent_us, audio_passed_us; + + pfd.fd = sco_fd; + pfd.events = POLLOUT | POLLHUP | POLLNVAL; + + while (bytes > written) { + struct timespec now; + + /* poll for sending */ + if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) { + DBG("timeout fd %d", sco_fd); + return false; + } + + if (pfd.revents & (POLLHUP | POLLNVAL)) { + error("error fd %d, events 0x%x", sco_fd, pfd.revents); + return false; + } + + len = bytes - written > sco_mtu ? sco_mtu : bytes - written; + + clock_gettime(CLOCK_REALTIME, &now); + /* Mark start of the stream */ + if (!out->samples) + memcpy(&out->start, &now, sizeof(out->start)); + + audio_sent_us = out->samples * 1000000ll / AUDIO_STREAM_SCO_RATE; + audio_passed_us = timespec_diff_us(&now, &out->start); + if ((int) (audio_sent_us - audio_passed_us) > 1500) { + struct timespec timeout = {0, + (audio_sent_us - + audio_passed_us) * 1000}; + DBG("Sleeping for %d ms", + (int) (audio_sent_us - audio_passed_us)); + nanosleep(&timeout, NULL); + } else if ((int)(audio_passed_us - audio_sent_us) > 50000) { + DBG("\n\nResync\n\n"); + out->samples = 0; + memcpy(&out->start, &now, sizeof(out->start)); + } + + if (out->cache_len) { + DBG("First packet cache_len %zd", out->cache_len); + memcpy(out->cache + out->cache_len, buffer, + sco_mtu - out->cache_len); + p = out->cache; + } else { + if (bytes - written >= sco_mtu) + p = (void *) buffer + written; + else { + memcpy(out->cache, buffer + written, + bytes - written); + out->cache_len = bytes - written; + DBG("Last packet, cache %zd bytes", + bytes - written); + written += bytes - written; + continue; + } + } + + ret = write(sco_fd, p, len); + if (ret > 0) { + if (out->cache_len) { + written = sco_mtu - out->cache_len; + out->cache_len = 0; + } else + written += ret; + + out->samples += ret / 2; + + DBG("written %d samples %zd total %zd bytes", + ret, out->samples, written); + continue; + } + + if (errno == EAGAIN) { + ret = errno; + warn("write failed (%d)", ret); + continue; + } + + if (errno != EINTR) { + ret = errno; + error("write failed (%d) fd %d bytes %zd", ret, sco_fd, + bytes); + return false; + } + } + + DBG("written %zd bytes", bytes); + + return true; +} + +static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, + size_t bytes) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + size_t frame_num = bytes / audio_stream_frame_size(&out->stream.common); + size_t output_frame_num = frame_num; + void *send_buf = out->downmix_buf; + size_t total; + + DBG("write to fd %d bytes %zu", sco_fd, bytes); + + if (ipc_get_sco_fd() != SCO_STATUS_SUCCESS) + return -1; + + if (!out->downmix_buf) { + error("sco: downmix buffer not initialized"); + return -1; + } + + downmix_to_mono(out, buffer, frame_num); + + if (out->resampler) { + int ret; + + /* limit resampler's output within what resample buf can hold */ + output_frame_num = out->resample_frame_num; + + ret = out->resampler->resample_from_input(out->resampler, + send_buf, + &frame_num, + out->resample_buf, + &output_frame_num); + if (ret) { + error("Failed to resample frames: %zd input %zd (%s)", + frame_num, output_frame_num, strerror(ret)); + return -1; + } + + send_buf = out->resample_buf; + + DBG("Resampled: frame_num %zd, output_frame_num %zd", + frame_num, output_frame_num); + } + + total = output_frame_num * sizeof(int16_t) * 1; + + DBG("total %zd", total); + + if (!write_data(out, send_buf, total)) + return -1; + + return bytes; +} + +static uint32_t out_get_sample_rate(const struct audio_stream *stream) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + + DBG("rate %u", out->cfg.rate); + + return out->cfg.rate; +} + +static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + DBG("rate %u", rate); + + return 0; +} + +static size_t out_get_buffer_size(const struct audio_stream *stream) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + size_t size = audio_stream_frame_size(&out->stream.common) * + out->cfg.frame_num; + + /* buffer size without resampling */ + if (out->cfg.rate == AUDIO_STREAM_SCO_RATE) + size = 576 * 2; + + DBG("buf size %zd", size); + + return size; +} + +static uint32_t out_get_channels(const struct audio_stream *stream) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + + DBG("channels num: %u", popcount(out->cfg.channels)); + + return out->cfg.channels; +} + +static audio_format_t out_get_format(const struct audio_stream *stream) +{ + struct sco_stream_out *out = (struct sco_stream_out *) stream; + + DBG("format: %u", out->cfg.format); + + return out->cfg.format; +} + +static int out_set_format(struct audio_stream *stream, audio_format_t format) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_standby(struct audio_stream *stream) +{ + DBG(""); + + return 0; +} + +static int out_dump(const struct audio_stream *stream, int fd) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + DBG("%s", kvpairs); + + return 0; +} + +static char *out_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + DBG(""); + + return strdup(""); +} + +static uint32_t out_get_latency(const struct audio_stream_out *stream) +{ + DBG(""); + + return 0; +} + +static int out_set_volume(struct audio_stream_out *stream, float left, + float right) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_get_render_position(const struct audio_stream_out *stream, + uint32_t *dsp_frames) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_add_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + + return -ENOSYS; +} + +static int out_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + + return -ENOSYS; +} + +static int sco_open_output_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out) +{ + struct sco_dev *adev = (struct sco_dev *) dev; + struct sco_stream_out *out; + int chan_num, ret; + size_t resample_size; + + DBG("config %p device flags 0x%02x", config, devices); + + if (sco_stream_out) { + DBG("stream_out already open"); + return -EIO; + } + + if (ipc_get_sco_fd() != SCO_STATUS_SUCCESS) + DBG("SCO is not connected yet; get fd on write()"); + + out = calloc(1, sizeof(struct sco_stream_out)); + if (!out) + return -ENOMEM; + + DBG("stream %p sco fd %d mtu %u", out, sco_fd, sco_mtu); + + out->stream.common.get_sample_rate = out_get_sample_rate; + out->stream.common.set_sample_rate = out_set_sample_rate; + out->stream.common.get_buffer_size = out_get_buffer_size; + out->stream.common.get_channels = out_get_channels; + out->stream.common.get_format = out_get_format; + out->stream.common.set_format = out_set_format; + out->stream.common.standby = out_standby; + out->stream.common.dump = out_dump; + out->stream.common.set_parameters = out_set_parameters; + out->stream.common.get_parameters = out_get_parameters; + out->stream.common.add_audio_effect = out_add_audio_effect; + out->stream.common.remove_audio_effect = out_remove_audio_effect; + out->stream.get_latency = out_get_latency; + out->stream.set_volume = out_set_volume; + out->stream.write = out_write; + out->stream.get_render_position = out_get_render_position; + + if (config) { + DBG("config: rate %u chan mask %x format %d offload %p", + config->sample_rate, config->channel_mask, + config->format, &config->offload_info); + + out->cfg.format = config->format; + out->cfg.channels = config->channel_mask; + out->cfg.rate = config->sample_rate; + } else { + out->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT; + out->cfg.channels = AUDIO_CHANNEL_OUT_STEREO; + out->cfg.rate = AUDIO_STREAM_DEFAULT_RATE; + } + + out->cfg.frame_num = OUT_STREAM_FRAMES; + + out->downmix_buf = malloc(out_get_buffer_size(&out->stream.common)); + if (!out->downmix_buf) { + free(out); + return -ENOMEM; + } + + out->cache = malloc(sco_mtu); + if (!out->cache) { + free(out->downmix_buf); + free(out); + return -ENOMEM; + } + + if (out->cfg.rate == AUDIO_STREAM_SCO_RATE) + goto skip_resampler; + + /* Channel numbers for resampler */ + chan_num = 1; + + ret = create_resampler(out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num, + RESAMPLER_QUALITY_DEFAULT, NULL, + &out->resampler); + if (ret) { + error("Failed to create resampler (%s)", strerror(-ret)); + goto failed; + } + + out->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE, + out->cfg.rate, + out->cfg.frame_num, 1); + + if (!out->resample_frame_num) { + error("frame num is too small to resample, discard it"); + goto failed; + } + + resample_size = sizeof(int16_t) * chan_num * out->resample_frame_num; + + out->resample_buf = malloc(resample_size); + if (!out->resample_buf) { + error("failed to allocate resample buffer for %u frames", + out->resample_frame_num); + goto failed; + } + + DBG("Resampler: input %d output %d chan %d frames %u size %zd", + out->cfg.rate, AUDIO_STREAM_SCO_RATE, chan_num, + out->resample_frame_num, resample_size); +skip_resampler: + *stream_out = &out->stream; + adev->out = out; + sco_stream_out = out; + + return 0; +failed: + if (out->resampler) + release_resampler(out->resampler); + + free(out->cache); + free(out->downmix_buf); + free(out); + *stream_out = NULL; + adev->out = NULL; + sco_stream_out = NULL; + + return ret; +} + +static void sco_close_output_stream(struct audio_hw_device *dev, + struct audio_stream_out *stream_out) +{ + struct sco_dev *sco_dev = (struct sco_dev *) dev; + struct sco_stream_out *out = (struct sco_stream_out *) stream_out; + + DBG("dev %p stream %p fd %d", dev, out, sco_fd); + + if (out->resampler) { + release_resampler(out->resampler); + free(out->resample_buf); + } + + free(out->cache); + free(out->downmix_buf); + free(out); + sco_dev->out = NULL; + + pthread_mutex_lock(&sco_mutex); + + sco_stream_out = NULL; + + if (!sco_stream_in) + sco_close_socket(); + + pthread_mutex_unlock(&sco_mutex); +} + +static int sco_set_parameters(struct audio_hw_device *dev, + const char *kvpairs) +{ + DBG("%s", kvpairs); + + return 0; +} + +static char *sco_get_parameters(const struct audio_hw_device *dev, + const char *keys) +{ + DBG(""); + + return strdup(""); +} + +static int sco_init_check(const struct audio_hw_device *dev) +{ + DBG(""); + + return 0; +} + +static int sco_set_voice_volume(struct audio_hw_device *dev, float volume) +{ + DBG("%f", volume); + + return 0; +} + +static int sco_set_master_volume(struct audio_hw_device *dev, float volume) +{ + DBG("%f", volume); + + return 0; +} + +static int sco_set_mode(struct audio_hw_device *dev, int mode) +{ + DBG(""); + + return -ENOSYS; +} + +static int sco_set_mic_mute(struct audio_hw_device *dev, bool state) +{ + DBG(""); + + return -ENOSYS; +} + +static int sco_get_mic_mute(const struct audio_hw_device *dev, bool *state) +{ + DBG(""); + + return -ENOSYS; +} + +static size_t sco_get_input_buffer_size(const struct audio_hw_device *dev, + const struct audio_config *config) +{ + DBG(""); + + return -ENOSYS; +} + +static uint32_t in_get_sample_rate(const struct audio_stream *stream) +{ + struct sco_stream_in *in = (struct sco_stream_in *) stream; + + DBG("rate %u", in->cfg.rate); + + return in->cfg.rate; +} + +static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) +{ + DBG("rate %u", rate); + + return 0; +} + +static size_t in_get_buffer_size(const struct audio_stream *stream) +{ + struct sco_stream_in *in = (struct sco_stream_in *) stream; + size_t size = audio_stream_frame_size(&in->stream.common) * + in->cfg.frame_num; + + /* buffer size without resampling */ + if (in->cfg.rate == AUDIO_STREAM_SCO_RATE) + size = 576; + + DBG("buf size %zd", size); + + return size; +} + +static uint32_t in_get_channels(const struct audio_stream *stream) +{ + struct sco_stream_in *in = (struct sco_stream_in *) stream; + + DBG("channels num: %u", popcount(in->cfg.channels)); + + return in->cfg.channels; +} + +static audio_format_t in_get_format(const struct audio_stream *stream) +{ + struct sco_stream_in *in = (struct sco_stream_in *) stream; + + DBG("format: %u", in->cfg.format); + + return in->cfg.format; +} + +static int in_set_format(struct audio_stream *stream, audio_format_t format) +{ + DBG(""); + + return -ENOSYS; +} + +static int in_standby(struct audio_stream *stream) +{ + DBG(""); + + return 0; +} + +static int in_dump(const struct audio_stream *stream, int fd) +{ + DBG(""); + + return -ENOSYS; +} + +static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) +{ + DBG("%s", kvpairs); + + return 0; +} + +static char *in_get_parameters(const struct audio_stream *stream, + const char *keys) +{ + DBG(""); + + return strdup(""); +} + +static int in_add_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + + return -ENOSYS; +} + +static int in_remove_audio_effect(const struct audio_stream *stream, + effect_handle_t effect) +{ + DBG(""); + + return -ENOSYS; +} + +static int in_set_gain(struct audio_stream_in *stream, float gain) +{ + DBG(""); + + return -ENOSYS; +} + +static bool read_data(struct sco_stream_in *in, char *buffer, size_t bytes) +{ + struct pollfd pfd; + size_t len, read_bytes = 0; + + pfd.fd = sco_fd; + pfd.events = POLLIN | POLLHUP | POLLNVAL; + + while (bytes > read_bytes) { + int ret; + + /* poll for reading */ + if (poll(&pfd, 1, SOCKET_POLL_TIMEOUT_MS) == 0) { + DBG("timeout fd %d", sco_fd); + return false; + } + + if (pfd.revents & (POLLHUP | POLLNVAL)) { + error("error fd %d, events 0x%x", sco_fd, pfd.revents); + return false; + } + + len = bytes - read_bytes > sco_mtu ? sco_mtu : + bytes - read_bytes; + + ret = read(sco_fd, buffer + read_bytes, len); + if (ret > 0) { + read_bytes += ret; + DBG("read %d total %zd", ret, read_bytes); + continue; + } + + if (errno == EAGAIN) { + ret = errno; + warn("read failed (%d)", ret); + continue; + } + + if (errno != EINTR) { + ret = errno; + error("read failed (%d) fd %d bytes %zd", ret, sco_fd, + bytes); + return false; + } + } + + DBG("read %zd bytes", read_bytes); + + return true; +} + +static ssize_t in_read(struct audio_stream_in *stream, void *buffer, + size_t bytes) +{ + struct sco_stream_in *in = (struct sco_stream_in *) stream; + size_t frame_size = audio_stream_frame_size(&stream->common); + size_t frame_num = bytes / frame_size; + size_t input_frame_num = frame_num; + void *read_buf = buffer; + size_t total = bytes; + int ret; + + DBG("Read from fd %d bytes %zu", sco_fd, bytes); + + if (ipc_get_sco_fd() != SCO_STATUS_SUCCESS) + return -1; + + if (!in->resampler && in->cfg.rate != AUDIO_STREAM_SCO_RATE) { + error("Cannot find resampler"); + return -1; + } + + if (in->resampler) { + input_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE, + in->cfg.rate, + frame_num, 0); + if (input_frame_num > in->resample_frame_num) { + DBG("resize input frames from %zd to %d", + input_frame_num, in->resample_frame_num); + input_frame_num = in->resample_frame_num; + } + + read_buf = in->resample_buf; + + total = input_frame_num * sizeof(int16_t) * 1; + } + + if(!read_data(in, read_buf, total)) + return -1; + + if (in->resampler) { + ret = in->resampler->resample_from_input(in->resampler, + in->resample_buf, + &input_frame_num, + (int16_t *) buffer, + &frame_num); + if (ret) { + error("Failed to resample frames: %zd input %zd (%s)", + frame_num, input_frame_num, + strerror(ret)); + return -1; + } + + DBG("resampler: remain %zd output %zd frames", input_frame_num, + frame_num); + } + + return bytes; +} + +static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) +{ + DBG(""); + + return -ENOSYS; +} + +static int sco_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in) +{ + struct sco_dev *sco_dev = (struct sco_dev *) dev; + struct sco_stream_in *in; + int chan_num, ret; + size_t resample_size; + + DBG("config %p device flags 0x%02x", config, devices); + + if (sco_stream_in) { + DBG("stream_in already open"); + ret = -EIO; + goto failed2; + } + + in = calloc(1, sizeof(struct sco_stream_in)); + if (!in) + return -ENOMEM; + + DBG("stream %p sco fd %d mtu %u", in, sco_fd, sco_mtu); + + in->stream.common.get_sample_rate = in_get_sample_rate; + in->stream.common.set_sample_rate = in_set_sample_rate; + in->stream.common.get_buffer_size = in_get_buffer_size; + in->stream.common.get_channels = in_get_channels; + in->stream.common.get_format = in_get_format; + in->stream.common.set_format = in_set_format; + in->stream.common.standby = in_standby; + in->stream.common.dump = in_dump; + in->stream.common.set_parameters = in_set_parameters; + in->stream.common.get_parameters = in_get_parameters; + in->stream.common.add_audio_effect = in_add_audio_effect; + in->stream.common.remove_audio_effect = in_remove_audio_effect; + in->stream.set_gain = in_set_gain; + in->stream.read = in_read; + in->stream.get_input_frames_lost = in_get_input_frames_lost; + + if (config) { + DBG("config: rate %u chan mask %x format %d offload %p", + config->sample_rate, config->channel_mask, + config->format, &config->offload_info); + + in->cfg.format = config->format; + in->cfg.channels = config->channel_mask; + in->cfg.rate = config->sample_rate; + } else { + in->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT; + in->cfg.channels = AUDIO_CHANNEL_OUT_MONO; + in->cfg.rate = AUDIO_STREAM_DEFAULT_RATE; + } + + in->cfg.frame_num = IN_STREAM_FRAMES; + + if (in->cfg.rate == AUDIO_STREAM_SCO_RATE) + goto skip_resampler; + + /* Channel numbers for resampler */ + chan_num = 1; + + ret = create_resampler(AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num, + RESAMPLER_QUALITY_DEFAULT, NULL, + &in->resampler); + if (ret) { + error("Failed to create resampler (%s)", strerror(-ret)); + goto failed; + } + + in->resample_frame_num = get_resample_frame_num(AUDIO_STREAM_SCO_RATE, + in->cfg.rate, + in->cfg.frame_num, 0); + + resample_size = sizeof(int16_t) * chan_num * in->resample_frame_num; + + in->resample_buf = malloc(resample_size); + if (!in->resample_buf) { + error("failed to allocate resample buffer for %d frames", + in->resample_frame_num); + goto failed; + } + + DBG("Resampler: input %d output %d chan %d frames %u size %zd", + AUDIO_STREAM_SCO_RATE, in->cfg.rate, chan_num, + in->resample_frame_num, resample_size); +skip_resampler: + *stream_in = &in->stream; + sco_dev->in = in; + sco_stream_in = in; + + return 0; +failed: + if (in->resampler) + release_resampler(in->resampler); + free(in); +failed2: + *stream_in = NULL; + sco_dev->in = NULL; + sco_stream_in = NULL; + + return ret; +} + +static void sco_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream_in) +{ + struct sco_dev *sco_dev = (struct sco_dev *) dev; + struct sco_stream_in *in = (struct sco_stream_in *) stream_in; + + DBG("dev %p stream %p fd %d", dev, in, sco_fd); + + if (in->resampler) { + release_resampler(in->resampler); + free(in->resample_buf); + } + + free(in); + sco_dev->in = NULL; + + pthread_mutex_lock(&sco_mutex); + + sco_stream_in = NULL; + + if (!sco_stream_out) + sco_close_socket(); + + pthread_mutex_unlock(&sco_mutex); +} + +static int sco_dump(const audio_hw_device_t *device, int fd) +{ + DBG(""); + + return 0; +} + +static int sco_close(hw_device_t *device) +{ + DBG(""); + + free(device); + + return 0; +} + +static void *ipc_handler(void *data) +{ + bool done = false; + struct pollfd pfd; + int sk; + + DBG(""); + + while (!done) { + DBG("Waiting for connection ..."); + + sk = accept(listen_sk, NULL, NULL); + if (sk < 0) { + int err = errno; + + if (err == EINTR) + continue; + + if (err != ECONNABORTED && err != EINVAL) + error("sco: Failed to accept socket: %d (%s)", + err, strerror(err)); + + break; + } + + pthread_mutex_lock(&sk_mutex); + ipc_sk = sk; + pthread_mutex_unlock(&sk_mutex); + + DBG("SCO IPC: Connected"); + + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = ipc_sk; + pfd.events = POLLHUP | POLLERR | POLLNVAL; + + /* Check if socket is still alive. Empty while loop.*/ + while (poll(&pfd, 1, -1) < 0 && errno == EINTR); + + info("SCO HAL: Socket closed"); + + pthread_mutex_lock(&sk_mutex); + close(ipc_sk); + ipc_sk = -1; + pthread_mutex_unlock(&sk_mutex); + } + + info("Closing SCO IPC thread"); + return NULL; +} + +static int sco_ipc_init(void) +{ + struct sockaddr_un addr; + int err; + int sk; + + DBG(""); + + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + err = -errno; + error("sco: Failed to create socket: %d (%s)", -err, + strerror(-err)); + return err; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH)); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = -errno; + error("sco: Failed to bind socket: %d (%s)", -err, + strerror(-err)); + goto failed; + } + + if (listen(sk, 1) < 0) { + err = -errno; + error("sco: Failed to listen on the socket: %d (%s)", -err, + strerror(-err)); + goto failed; + } + + listen_sk = sk; + + err = pthread_create(&ipc_th, NULL, ipc_handler, NULL); + if (err) { + err = -err; + ipc_th = 0; + error("sco: Failed to start IPC thread: %d (%s)", + -err, strerror(-err)); + goto failed; + } + + return 0; + +failed: + close(sk); + return err; +} + +static int sco_open(const hw_module_t *module, const char *name, + hw_device_t **device) +{ + struct sco_dev *dev; + int err; + + DBG(""); + + if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) { + error("SCO: interface %s not matching [%s]", name, + AUDIO_HARDWARE_INTERFACE); + return -EINVAL; + } + + err = sco_ipc_init(); + if (err < 0) + return err; + + dev = calloc(1, sizeof(struct sco_dev)); + if (!dev) + return -ENOMEM; + + dev->dev.common.tag = HARDWARE_DEVICE_TAG; + dev->dev.common.version = AUDIO_DEVICE_API_VERSION_CURRENT; + dev->dev.common.module = (struct hw_module_t *) module; + dev->dev.common.close = sco_close; + + dev->dev.init_check = sco_init_check; + dev->dev.set_voice_volume = sco_set_voice_volume; + dev->dev.set_master_volume = sco_set_master_volume; + dev->dev.set_mode = sco_set_mode; + dev->dev.set_mic_mute = sco_set_mic_mute; + dev->dev.get_mic_mute = sco_get_mic_mute; + dev->dev.set_parameters = sco_set_parameters; + dev->dev.get_parameters = sco_get_parameters; + dev->dev.get_input_buffer_size = sco_get_input_buffer_size; + dev->dev.open_output_stream = sco_open_output_stream; + dev->dev.close_output_stream = sco_close_output_stream; + dev->dev.open_input_stream = sco_open_input_stream; + dev->dev.close_input_stream = sco_close_input_stream; + dev->dev.dump = sco_dump; + + *device = &dev->dev.common; + + return 0; +} + +static struct hw_module_methods_t hal_module_methods = { + .open = sco_open, +}; + +struct audio_module HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = AUDIO_HARDWARE_MODULE_ID, + .name = "SCO Audio HW HAL", + .author = "Intel Corporation", + .methods = &hal_module_methods, + }, +}; diff -Nru bluez-4.101/android/hal-socket.c bluez-5.23/android/hal-socket.c --- bluez-4.101/android/hal-socket.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-socket.c 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "hal-ipc.h" +#include "hal-log.h" +#include "hal-msg.h" +#include "hal-utils.h" +#include "hal.h" + +static bt_status_t socket_listen(btsock_type_t type, const char *service_name, + const uint8_t *uuid, int chan, + int *sock, int flags) +{ + struct hal_cmd_socket_listen cmd; + + if (!sock) + return BT_STATUS_PARM_INVALID; + + DBG("uuid %s chan %d sock %p type %d service_name %s flags 0x%02x", + btuuid2str(uuid), chan, sock, type, service_name, flags); + + memset(&cmd, 0, sizeof(cmd)); + + /* type match IPC type */ + cmd.type = type; + cmd.flags = flags; + cmd.channel = chan; + + if (uuid) + memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); + + if (service_name) + memcpy(cmd.name, service_name, strlen(service_name)); + + return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, + sizeof(cmd), &cmd, NULL, NULL, sock); +} + +static bt_status_t socket_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type, + const uint8_t *uuid, int chan, + int *sock, int flags) +{ + struct hal_cmd_socket_connect cmd; + + if (!sock) + return BT_STATUS_PARM_INVALID; + + DBG("bdaddr %s uuid %s chan %d sock %p type %d flags 0x%02x", + bdaddr2str(bdaddr), btuuid2str(uuid), chan, sock, type, flags); + + memset(&cmd, 0, sizeof(cmd)); + + /* type match IPC type */ + cmd.type = type; + cmd.flags = flags; + cmd.channel = chan; + + if (uuid) + memcpy(cmd.uuid, uuid, sizeof(cmd.uuid)); + + if (bdaddr) + memcpy(cmd.bdaddr, bdaddr, sizeof(cmd.bdaddr)); + + return hal_ipc_cmd(HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, + sizeof(cmd), &cmd, NULL, NULL, sock); +} + +static btsock_interface_t socket_if = { + sizeof(socket_if), + socket_listen, + socket_connect +}; + +btsock_interface_t *bt_get_socket_interface(void) +{ + return &socket_if; +} diff -Nru bluez-4.101/android/hal-utils.c bluez-5.23/android/hal-utils.c --- bluez-4.101/android/hal-utils.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-utils.c 2014-01-21 00:12:58.000000000 +0000 @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "hal-utils.h" + +/* + * converts uuid to string + * buf should be at least 39 bytes + * + * returns string representation of uuid + */ +const char *bt_uuid_t2str(const uint8_t *uuid, char *buf) +{ + int shift = 0; + unsigned int i; + int is_bt; + + if (!uuid) + return strcpy(buf, "NULL"); + + is_bt = !memcmp(&uuid[4], &BT_BASE_UUID[4], HAL_UUID_LEN - 4); + + for (i = 0; i < HAL_UUID_LEN; i++) { + if (i == 4 && is_bt) + break; + + if (i == 4 || i == 6 || i == 8 || i == 10) { + buf[i * 2 + shift] = '-'; + shift++; + } + sprintf(buf + i * 2 + shift, "%02x", uuid[i]); + } + + return buf; +} + +const char *btuuid2str(const uint8_t *uuid) +{ + static char buf[MAX_UUID_STR_LEN]; + + return bt_uuid_t2str(uuid, buf); +} + +INTMAP(bt_status_t, -1, "(unknown)") + DELEMENT(BT_STATUS_SUCCESS), + DELEMENT(BT_STATUS_FAIL), + DELEMENT(BT_STATUS_NOT_READY), + DELEMENT(BT_STATUS_NOMEM), + DELEMENT(BT_STATUS_BUSY), + DELEMENT(BT_STATUS_DONE), + DELEMENT(BT_STATUS_UNSUPPORTED), + DELEMENT(BT_STATUS_PARM_INVALID), + DELEMENT(BT_STATUS_UNHANDLED), + DELEMENT(BT_STATUS_AUTH_FAILURE), + DELEMENT(BT_STATUS_RMT_DEV_DOWN), +ENDMAP + +INTMAP(bt_state_t, -1, "(unknown)") + DELEMENT(BT_STATE_OFF), + DELEMENT(BT_STATE_ON), +ENDMAP + +INTMAP(bt_device_type_t, -1, "(unknown)") + DELEMENT(BT_DEVICE_DEVTYPE_BREDR), + DELEMENT(BT_DEVICE_DEVTYPE_BLE), + DELEMENT(BT_DEVICE_DEVTYPE_DUAL), +ENDMAP + +INTMAP(bt_scan_mode_t, -1, "(unknown)") + DELEMENT(BT_SCAN_MODE_NONE), + DELEMENT(BT_SCAN_MODE_CONNECTABLE), + DELEMENT(BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE), +ENDMAP + +INTMAP(bt_discovery_state_t, -1, "(unknown)") + DELEMENT(BT_DISCOVERY_STOPPED), + DELEMENT(BT_DISCOVERY_STARTED), +ENDMAP + +INTMAP(bt_acl_state_t, -1, "(unknown)") + DELEMENT(BT_ACL_STATE_CONNECTED), + DELEMENT(BT_ACL_STATE_DISCONNECTED), +ENDMAP + +INTMAP(bt_bond_state_t, -1, "(unknown)") + DELEMENT(BT_BOND_STATE_NONE), + DELEMENT(BT_BOND_STATE_BONDING), + DELEMENT(BT_BOND_STATE_BONDED), +ENDMAP + +INTMAP(bt_ssp_variant_t, -1, "(unknown)") + DELEMENT(BT_SSP_VARIANT_PASSKEY_CONFIRMATION), + DELEMENT(BT_SSP_VARIANT_PASSKEY_ENTRY), + DELEMENT(BT_SSP_VARIANT_CONSENT), + DELEMENT(BT_SSP_VARIANT_PASSKEY_NOTIFICATION), +ENDMAP + +INTMAP(bt_property_type_t, -1, "(unknown)") + DELEMENT(BT_PROPERTY_BDNAME), + DELEMENT(BT_PROPERTY_BDADDR), + DELEMENT(BT_PROPERTY_UUIDS), + DELEMENT(BT_PROPERTY_CLASS_OF_DEVICE), + DELEMENT(BT_PROPERTY_TYPE_OF_DEVICE), + DELEMENT(BT_PROPERTY_SERVICE_RECORD), + DELEMENT(BT_PROPERTY_ADAPTER_SCAN_MODE), + DELEMENT(BT_PROPERTY_ADAPTER_BONDED_DEVICES), + DELEMENT(BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT), + DELEMENT(BT_PROPERTY_REMOTE_FRIENDLY_NAME), + DELEMENT(BT_PROPERTY_REMOTE_RSSI), + DELEMENT(BT_PROPERTY_REMOTE_VERSION_INFO), + DELEMENT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP), +ENDMAP + +INTMAP(bt_cb_thread_evt, -1, "(unknown)") + DELEMENT(ASSOCIATE_JVM), + DELEMENT(DISASSOCIATE_JVM), +ENDMAP + +/* Find first index of given value in table m */ +int int2str_findint(int v, const struct int2str m[]) +{ + int i; + + for (i = 0; m[i].str; ++i) { + if (m[i].val == v) + return i; + } + return -1; +} + +/* Find first index of given string in table m */ +int int2str_findstr(const char *str, const struct int2str m[]) +{ + int i; + + for (i = 0; m[i].str; ++i) { + if (strcmp(m[i].str, str) == 0) + return i; + } + return -1; +} + +/* + * convert bd_addr to string + * buf must be at least 18 char long + * + * returns buf + */ +const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf) +{ + const uint8_t *p = bd_addr->address; + + if (!bd_addr) + return strcpy(buf, "NULL"); + + snprintf(buf, MAX_ADDR_STR_LEN, "%02x:%02x:%02x:%02x:%02x:%02x", + p[0], p[1], p[2], p[3], p[4], p[5]); + + return buf; +} + +/* converts string to bt_bdaddr_t */ +void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr) +{ + uint8_t *p = bd_addr->address; + + sscanf(str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &p[0], &p[1], &p[2], &p[3], &p[4], &p[5]); +} + +/* converts string to uuid */ +void str2bt_uuid_t(const char *str, bt_uuid_t *uuid) +{ + int i = 0; + + memcpy(uuid, BT_BASE_UUID, sizeof(bt_uuid_t)); + + while (*str && i < (int) sizeof(bt_uuid_t)) { + while (*str == '-') + str++; + + if (sscanf(str, "%02hhx", &uuid->uu[i]) != 1) + break; + + i++; + str += 2; + } +} + +const char *enum_defines(void *v, int i) +{ + const struct int2str *m = v; + + return m[i].str != NULL ? m[i].str : NULL; +} + +const char *enum_strings(void *v, int i) +{ + const char **m = v; + + return m[i] != NULL ? m[i] : NULL; +} + +const char *enum_one_string(void *v, int i) +{ + const char *m = v; + + return (i == 0) && (m[0] != 0) ? m : NULL; +} + +const char *bdaddr2str(const bt_bdaddr_t *bd_addr) +{ + static char buf[MAX_ADDR_STR_LEN]; + + return bt_bdaddr_t2str(bd_addr, buf); +} + +const char *btproperty2str(const bt_property_t *property) +{ + static char buf[4096]; + char *p; + + p = buf + sprintf(buf, "type=%s len=%d val=", + bt_property_type_t2str(property->type), + property->len); + + switch (property->type) { + case BT_PROPERTY_BDNAME: + case BT_PROPERTY_REMOTE_FRIENDLY_NAME: + snprintf(p, property->len + 1, "%s", + ((bt_bdname_t *) property->val)->name); + break; + + case BT_PROPERTY_BDADDR: + sprintf(p, "%s", bdaddr2str((bt_bdaddr_t *) property->val)); + break; + + case BT_PROPERTY_CLASS_OF_DEVICE: + sprintf(p, "%06x", *((int *) property->val)); + break; + + case BT_PROPERTY_TYPE_OF_DEVICE: + sprintf(p, "%s", bt_device_type_t2str( + *((bt_device_type_t *) property->val))); + break; + + case BT_PROPERTY_REMOTE_RSSI: + sprintf(p, "%d", *((char *) property->val)); + break; + + case BT_PROPERTY_ADAPTER_SCAN_MODE: + sprintf(p, "%s", + bt_scan_mode_t2str(*((bt_scan_mode_t *) property->val))); + break; + + case BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT: + sprintf(p, "%d", *((int *) property->val)); + break; + + case BT_PROPERTY_ADAPTER_BONDED_DEVICES: + { + int count = property->len / sizeof(bt_bdaddr_t); + char *ptr = property->val; + + strcat(p, "{"); + + while (count--) { + strcat(p, bdaddr2str((bt_bdaddr_t *) ptr)); + if (count) + strcat(p, ", "); + ptr += sizeof(bt_bdaddr_t); + } + + strcat(p, "}"); + + } + break; + + case BT_PROPERTY_UUIDS: + { + int count = property->len / sizeof(bt_uuid_t); + uint8_t *ptr = property->val; + + strcat(p, "{"); + + while (count--) { + strcat(p, btuuid2str(ptr)); + if (count) + strcat(p, ", "); + ptr += sizeof(bt_uuid_t); + } + + strcat(p, "}"); + + } + break; + + case BT_PROPERTY_SERVICE_RECORD: + { + bt_service_record_t *rec = property->val; + + sprintf(p, "{%s, %d, %s}", btuuid2str(rec->uuid.uu), + rec->channel, rec->name); + } + break; + + default: + sprintf(p, "%p", property->val); + } + + return buf; +} diff -Nru bluez-4.101/android/hal-utils.h bluez-5.23/android/hal-utils.h --- bluez-4.101/android/hal-utils.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hal-utils.h 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#define PLATFORM_VER(a,b,c) ((a << 16) | ( b << 8) | (c)) + +#define MAX_UUID_STR_LEN 37 +#define HAL_UUID_LEN 16 +#define MAX_ADDR_STR_LEN 18 + +static const char BT_BASE_UUID[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb +}; + +const char *bt_uuid_t2str(const uint8_t *uuid, char *buf); +const char *btuuid2str(const uint8_t *uuid); +const char *bt_bdaddr_t2str(const bt_bdaddr_t *bd_addr, char *buf); +void str2bt_bdaddr_t(const char *str, bt_bdaddr_t *bd_addr); +void str2bt_uuid_t(const char *str, bt_uuid_t *uuid); +const char *btproperty2str(const bt_property_t *property); +const char *bdaddr2str(const bt_bdaddr_t *bd_addr); + +/* + * Begin mapping section + * + * There are some mappings between integer values (enums) and strings + * to be presented to user. To make it easier to convert between those two + * set of macros is given. It is specially useful when we want to have + * strings that match constants from header files like: + * BT_STATUS_SUCCESS (0) and corresponding "BT_STATUS_SUCCESS" + * Example of usage: + * + * INTMAP(int, -1, "invalid") + * DELEMENT(BT_STATUS_SUCCESS) + * DELEMENT(BT_STATUS_FAIL) + * MELEMENT(123, "Some strange value") + * ENDMAP + * + * Just by doing this we have mapping table plus two functions: + * int str2int(const char *str); + * const char *int2str(int v); + * + * second argument to INTMAP specifies value to be returned from + * str2int function when there is not mapping for such number + * third argument specifies default value to be returned from int2str + * + * If same mapping is to be used in several source files put + * INTMAP in c file and DECINTMAP in h file. + * + * For mappings that are to be used in single file only + * use SINTMAP which will create the same but everything will be marked + * as static. + */ + +struct int2str { + int val; /* int value */ + const char *str; /* corresponding string */ +}; + +int int2str_findint(int v, const struct int2str m[]); +int int2str_findstr(const char *str, const struct int2str m[]); +const char *enum_defines(void *v, int i); +const char *enum_strings(void *v, int i); +const char *enum_one_string(void *v, int i); + +#define TYPE_ENUM(type) ((void *) &__##type##2str[0]) +#define DECINTMAP(type) \ +extern struct int2str __##type##2str[]; \ +const char *type##2##str(type v); \ +type str##2##type(const char *str); \ + +#define INTMAP(type, deft, defs) \ +const char *type##2##str(type v) \ +{ \ + int i = int2str_findint((int) v, __##type##2str); \ + return (i < 0) ? defs : __##type##2str[i].str; \ +} \ +type str##2##type(const char *str) \ +{ \ + int i = int2str_findstr(str, __##type##2str); \ + return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \ +} \ +struct int2str __##type##2str[] = { + +#define SINTMAP(type, deft, defs) \ +static struct int2str __##type##2str[]; \ +static inline const char *type##2##str(type v) \ +{ \ + int i = int2str_findint((int) v, __##type##2str); \ + return (i < 0) ? defs : __##type##2str[i].str; \ +} \ +static inline type str##2##type(const char *str) \ +{ \ + int i = int2str_findstr(str, __##type##2str); \ + return (i < 0) ? (type) deft : (type) (__##type##2str[i].val); \ +} \ +static struct int2str __##type##2str[] = { + +#define ENDMAP {0, NULL} }; + +/* use this to generate string from header file constant */ +#define MELEMENT(v, s) {v, s} +/* use this to have arbitrary mapping from int to string */ +#define DELEMENT(s) {s, #s} +/* End of mapping section */ + +DECINTMAP(bt_status_t); +DECINTMAP(bt_state_t); +DECINTMAP(bt_device_type_t); +DECINTMAP(bt_scan_mode_t); +DECINTMAP(bt_discovery_state_t); +DECINTMAP(bt_acl_state_t); +DECINTMAP(bt_bond_state_t); +DECINTMAP(bt_ssp_variant_t); +DECINTMAP(bt_property_type_t); +DECINTMAP(bt_cb_thread_evt); diff -Nru bluez-4.101/android/handsfree.c bluez-5.23/android/handsfree.c --- bluez-4.101/android/handsfree.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/handsfree.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,2681 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/sdp-client.h" +#include "src/uuid-helper.h" +#include "src/shared/hfp.h" +#include "btio/btio.h" +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "handsfree.h" +#include "bluetooth.h" +#include "src/log.h" +#include "utils.h" +#include "sco-msg.h" + +#define HSP_AG_CHANNEL 12 +#define HFP_AG_CHANNEL 13 + +#define HFP_AG_FEAT_3WAY 0x00000001 +#define HFP_AG_FEAT_ECNR 0x00000002 +#define HFP_AG_FEAT_VR 0x00000004 +#define HFP_AG_FEAT_INBAND 0x00000008 +#define HFP_AG_FEAT_VTAG 0x00000010 +#define HFP_AG_FEAT_REJ_CALL 0x00000020 +#define HFP_AG_FEAT_ECS 0x00000040 +#define HFP_AG_FEAT_ECC 0x00000080 +#define HFP_AG_FEAT_EXT_ERR 0x00000100 +#define HFP_AG_FEAT_CODEC 0x00000200 + +#define HFP_HF_FEAT_ECNR 0x00000001 +#define HFP_HF_FEAT_3WAY 0x00000002 +#define HFP_HF_FEAT_CLI 0x00000004 +#define HFP_HF_FEAT_VR 0x00000008 +#define HFP_HF_FEAT_RVC 0x00000010 +#define HFP_HF_FEAT_ECS 0x00000020 +#define HFP_HF_FEAT_ECC 0x00000040 +#define HFP_HF_FEAT_CODEC 0x00000080 + +#define HFP_AG_FEATURES (HFP_AG_FEAT_3WAY | HFP_AG_FEAT_ECNR |\ + HFP_AG_FEAT_VR | HFP_AG_FEAT_REJ_CALL |\ + HFP_AG_FEAT_ECS | HFP_AG_FEAT_EXT_ERR) + +#define HFP_AG_CHLD "0,1,2,3" + +/* offsets in indicators table, should be incremented when sending CIEV */ +#define IND_SERVICE 0 +#define IND_CALL 1 +#define IND_CALLSETUP 2 +#define IND_CALLHELD 3 +#define IND_SIGNAL 4 +#define IND_ROAM 5 +#define IND_BATTCHG 6 +#define IND_COUNT (IND_BATTCHG + 1) + +#define RING_TIMEOUT 2 + +#define CVSD_OFFSET 0 +#define MSBC_OFFSET 1 +#define CODECS_COUNT (MSBC_OFFSET + 1) + +#define CODEC_ID_CVSD 0x01 +#define CODEC_ID_MSBC 0x02 + +struct indicator { + const char *name; + int min; + int max; + int val; + bool always_active; + bool active; +}; + +struct hfp_codec { + uint8_t type; + bool local_supported; + bool remote_supported; +}; + +static const struct indicator inds_defaults[] = { + { "service", 0, 1, 0, false, true }, + { "call", 0, 1, 0, true, true }, + { "callsetup", 0, 3, 0, true, true }, + { "callheld", 0, 2, 0, true, true }, + { "signal", 0, 5, 0, false, true }, + { "roam", 0, 1, 0, false, true }, + { "battchg", 0, 5, 0, false, true }, +}; + +static const struct hfp_codec codecs_defaults[] = { + { CODEC_ID_CVSD, true, false}, + { CODEC_ID_MSBC, false, false}, +}; + +static struct { + bdaddr_t bdaddr; + uint8_t state; + uint8_t audio_state; + uint32_t features; + + bool clip_enabled; + bool cmee_enabled; + bool ccwa_enabled; + bool indicators_enabled; + struct indicator inds[IND_COUNT]; + int num_active; + int num_held; + int setup_state; + bool call_hanging_up; + + uint8_t negotiated_codec; + uint8_t proposed_codec; + struct hfp_codec codecs[CODECS_COUNT]; + + guint ring; + bool hsp; + + struct hfp_gw *gw; + + GIOChannel *sco; + guint sco_watch; +} device; + +static uint32_t hfp_ag_features = 0; + +static bdaddr_t adapter_addr; + +static struct ipc *hal_ipc = NULL; +static struct ipc *sco_ipc = NULL; + +static uint32_t hfp_record_id = 0; +static GIOChannel *hfp_server = NULL; + +static uint32_t hsp_record_id = 0; +static GIOChannel *hsp_server = NULL; + +static GIOChannel *sco_server = NULL; + +static void device_set_state(uint8_t state) +{ + struct hal_ev_handsfree_conn_state ev; + char address[18]; + + if (device.state == state) + return; + + device.state = state; + + ba2str(&device.bdaddr, address); + DBG("device %s state %u", address, state); + + bdaddr2android(&device.bdaddr, ev.bdaddr); + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CONN_STATE, sizeof(ev), &ev); +} + +static void device_set_audio_state(uint8_t state) +{ + struct hal_ev_handsfree_audio_state ev; + char address[18]; + + if (device.audio_state == state) + return; + + device.audio_state = state; + + ba2str(&device.bdaddr, address); + DBG("device %s audio state %u", address, state); + + bdaddr2android(&device.bdaddr, ev.bdaddr); + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_AUDIO_STATE, sizeof(ev), &ev); +} + +static void init_codecs(void) +{ + memcpy(device.codecs, codecs_defaults, sizeof(device.codecs)); + + if (hfp_ag_features & HFP_AG_FEAT_CODEC) + device.codecs[MSBC_OFFSET].local_supported = true; +} + +static void device_init(const bdaddr_t *bdaddr) +{ + bacpy(&device.bdaddr, bdaddr); + + device.setup_state = HAL_HANDSFREE_CALL_STATE_IDLE; + + memcpy(device.inds, inds_defaults, sizeof(device.inds)); + + init_codecs(); + + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTING); +} + +static void device_cleanup(void) +{ + if (device.gw) { + hfp_gw_unref(device.gw); + device.gw = NULL; + } + + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED); + + if (device.sco_watch) { + g_source_remove(device.sco_watch); + device.sco_watch = 0; + } + + if (device.sco) { + g_io_channel_shutdown(device.sco, TRUE, NULL); + g_io_channel_unref(device.sco); + device.sco = NULL; + } + + if (device.ring) { + g_source_remove(device.ring); + device.ring = 0; + } + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); + + memset(&device, 0, sizeof(device)); +} + +static void disconnect_watch(void *user_data) +{ + DBG(""); + + device_cleanup(); +} + +static void at_cmd_unknown(const char *command, void *user_data) +{ + uint8_t buf[IPC_MTU]; + struct hal_ev_handsfree_unknown_at *ev = (void *) buf; + + if (device.state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) { + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); + hfp_gw_disconnect(device.gw); + return; + } + + /* copy while string including terminating NULL */ + ev->len = strlen(command) + 1; + memcpy(ev->buf, command, ev->len); + + if (ev->len > IPC_MTU - sizeof(*ev)) { + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); + return; + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_UNKNOWN_AT, sizeof(*ev) + ev->len, ev); +} + +static void at_cmd_vgm(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_volume ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 15) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ev.type = HAL_HANDSFREE_VOLUME_TYPE_MIC; + ev.volume = val; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev); + + /* Framework is not replying with result for AT+VGM */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_vgs(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_volume ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 15) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ev.type = HAL_HANDSFREE_VOLUME_TYPE_SPEAKER; + ev.volume = val; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_VOLUME, sizeof(ev), &ev); + + /* Framework is not replying with result for AT+VGS */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_cops(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val != 3) + break; + + if (!hfp_gw_result_get_number(result, &val) || val != 0) + break; + + if (hfp_gw_result_has_next(result)) + break; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_COPS, 0, NULL); + return; + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_bia(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val, i, def; + bool tmp[IND_COUNT]; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + for (i = 0; i < IND_COUNT; i++) + tmp[i] = device.inds[i].active; + + i = 0; + + do { + def = (i < IND_COUNT) ? device.inds[i].active : 0; + + if (!hfp_gw_result_get_number_default(result, &val, def)) + goto failed; + + if (val > 1) + goto failed; + + if (i < IND_COUNT) { + tmp[i] = val || device.inds[i].always_active; + i++; + } + } while (hfp_gw_result_has_next(result)); + + for (i = 0; i < IND_COUNT; i++) + device.inds[i].active = tmp[i]; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + +failed: + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_a(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_ANSWER, 0, NULL); + + /* Framework is not replying with result for ATA */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_SET: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_d(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + char buf[IPC_MTU]; + struct hal_ev_handsfree_dial *ev = (void *) buf; + int cnt; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_unquoted_string(result, + (char *) ev->number, 255)) + break; + + ev->number_len = strlen((char *) ev->number); + + if (ev->number[ev->number_len - 1] != ';') + break; + + if (ev->number[0] == '>') + cnt = strspn((char *) ev->number + 1, "0123456789") + 1; + else + cnt = strspn((char *) ev->number, "0123456789ABC*#+"); + + if (cnt != ev->number_len - 1) + break; + + ev->number_len++; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_DIAL, + sizeof(*ev) + ev->number_len, ev); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_ccwa(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + device.ccwa_enabled = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_chup(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_HANGUP, 0, NULL); + + /* Framework is not replying with result for AT+CHUP */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_SET: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_clcc(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CLCC, 0, NULL); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_SET: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_cmee(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + device.cmee_enabled = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_clip(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + device.clip_enabled = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_vts(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_dtmf ev; + char str[2]; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_unquoted_string(result, str, 2)) + break; + + if (!((str[0] >= '0' && str[0] <= '9') || + (str[0] >= 'A' && str[0] <= 'D') || + str[0] == '*' || str[0] == '#')) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ev.tone = str[0]; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_DTMF, sizeof(ev), &ev); + + /* Framework is not replying with result for AT+VTS */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_cnum(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CNUM, 0, NULL); + return; + case HFP_GW_CMD_TYPE_SET: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_binp(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + /* TODO */ + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_bldn(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_dial ev; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (hfp_gw_result_has_next(result)) + break; + + ev.number_len = 0; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_DIAL, sizeof(ev), &ev); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_SET: + break; + } +} + +static void at_cmd_bvra(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_vr_state ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + if (val) + ev.state = HAL_HANDSFREE_VR_STARTED; + else + ev.state = HAL_HANDSFREE_VR_STOPPED; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_VR, sizeof(ev), &ev); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_nrec(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_nrec ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + /* + * Android HAL defines start and stop parameter for NREC + * callback, but spec allows HF to only disable AG's NREC + * feature for SLC duration. Follow spec here. + */ + if (!hfp_gw_result_get_number(result, &val) || val != 0) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ev.nrec = HAL_HANDSFREE_NREC_STOP; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_NREC, sizeof(ev), &ev); + + /* Framework is not replying with result for AT+NREC */ + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_bsir(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + /* TODO */ + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_btrh(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + /* TODO */ + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static gboolean sco_watch_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + g_io_channel_shutdown(device.sco, TRUE, NULL); + g_io_channel_unref(device.sco); + device.sco = NULL; + + DBG(""); + + device.sco_watch = 0; + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); + + return FALSE; +} + +static void select_codec(uint8_t codec_type) +{ + uint8_t type = CODEC_ID_CVSD; + int i; + + if (codec_type > 0) { + type = codec_type; + goto done; + } + + for (i = CODECS_COUNT - 1; i >= CVSD_OFFSET; i--) { + if (!device.codecs[i].local_supported) + continue; + + if (!device.codecs[i].remote_supported) + continue; + + type = device.codecs[i].type; + break; + } + +done: + device.proposed_codec = type; + + hfp_gw_send_info(device.gw, "+BCS: %u", type); +} + +static void connect_sco_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + if (err) { + uint8_t status; + + error("handsfree: audio connect failed (%s)", err->message); + + status = HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED; + device_set_audio_state(status); + + if (!(device.features & HFP_HF_FEAT_CODEC)) + return; + + /* If other failed, try connecting with CVSD */ + if (device.negotiated_codec != CODEC_ID_CVSD) { + info("handsfree: trying fallback with CVSD"); + select_codec(CODEC_ID_CVSD); + } + + return; + } + + g_io_channel_set_close_on_unref(chan, TRUE); + + device.sco = g_io_channel_ref(chan); + device.sco_watch = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, + sco_watch_cb, NULL); + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED); +} + +static bool connect_sco(void) +{ + GIOChannel *io; + GError *gerr = NULL; + uint16_t voice_settings; + + if (device.sco) + return false; + + if (!(device.features & HFP_HF_FEAT_CODEC)) + voice_settings = 0; + else if (device.negotiated_codec != CODEC_ID_CVSD) + voice_settings = BT_VOICE_TRANSPARENT; + else + voice_settings = BT_VOICE_CVSD_16BIT; + + io = bt_io_connect(connect_sco_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &device.bdaddr, + BT_IO_OPT_VOICE, voice_settings, + BT_IO_OPT_INVALID); + + if (!io) { + error("handsfree: unable to connect audio: %s", gerr->message); + g_error_free(gerr); + return false; + } + + g_io_channel_unref(io); + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING); + + return true; +} + +static void at_cmd_bcc(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_COMMAND: + if (!(device.features & HFP_HF_FEAT_CODEC)) + break; + + if (hfp_gw_result_has_next(result)) + break; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + /* we haven't negotiated codec, start selection */ + if (!device.negotiated_codec) { + select_codec(0); + return; + } + /* + * we try connect to negotiated codec. If it fails, and it isn't + * CVSD codec, try connect CVSD + */ + if (!connect_sco() && device.negotiated_codec != CODEC_ID_CVSD) + select_codec(CODEC_ID_CVSD); + + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_SET: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_bcs(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val)) + break; + + if (hfp_gw_result_has_next(result)) + break; + + /* Remote replied with other codec. Reply with error */ + if (device.proposed_codec != val) { + device.proposed_codec = 0; + break; + } + + device.proposed_codec = 0; + device.negotiated_codec = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + /* Connect sco with negotiated parameters */ + connect_sco(); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_ckpd(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val != 200) + break; + + if (hfp_gw_result_has_next(result)) + break; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_HSP_KEY_PRESS, 0, NULL); + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void register_post_slc_at(void) +{ + if (device.hsp) { + hfp_gw_register(device.gw, at_cmd_ckpd, "+CKPD", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vgs, "+VGS", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vgm, "+VGM", NULL, NULL); + return; + } + + hfp_gw_register(device.gw, at_cmd_a, "A", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_d, "D", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_ccwa, "+CCWA", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_chup, "+CHUP", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_clcc, "+CLCC", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cops, "+COPS", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cmee, "+CMEE", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_clip, "+CLIP", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vts, "+VTS", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cnum, "+CNUM", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bia, "+BIA", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_binp, "+BINP", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bldn, "+BLDN", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bvra, "+BVRA", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_nrec, "+NREC", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vgs, "+VGS", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_vgm, "+VGM", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bsir, "+BSIR", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_btrh, "+BTRH", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bcc, "+BCC", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bcs, "+BCS", NULL, NULL); +} + +static void at_cmd_cmer(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + /* mode must be =3 */ + if (!hfp_gw_result_get_number(result, &val) || val != 3) + break; + + /* keyp is don't care */ + if (!hfp_gw_result_get_number(result, &val)) + break; + + /* disp is don't care */ + if (!hfp_gw_result_get_number(result, &val)) + break; + + /* ind must be 0 or 1 */ + if (!hfp_gw_result_get_number(result, &val) || val > 1) + break; + + if (hfp_gw_result_has_next(result)) + break; + + device.indicators_enabled = val; + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + if (device.features & HFP_HF_FEAT_3WAY) + return; + + register_post_slc_at(); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); + return; + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_cind(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + char *buf, *ptr; + int len; + unsigned int i; + + switch (type) { + case HFP_GW_CMD_TYPE_TEST: + + /* + * If device supports Codec Negotiation, AT+BAC should be + * received first + */ + if ((device.features & HFP_HF_FEAT_CODEC) && + !device.codecs[CVSD_OFFSET].remote_supported) + break; + + len = strlen("+CIND:") + 1; + + for (i = 0; i < IND_COUNT; i++) { + len += strlen("(\"\",(X,X)),"); + len += strlen(device.inds[i].name); + } + + buf = g_malloc(len); + + ptr = buf + sprintf(buf, "+CIND:"); + + for (i = 0; i < IND_COUNT; i++) { + ptr += sprintf(ptr, "(\"%s\",(%d%c%d)),", + device.inds[i].name, + device.inds[i].min, + device.inds[i].max == 1 ? ',' : '-', + device.inds[i].max); + } + + ptr--; + *ptr = '\0'; + + hfp_gw_send_info(device.gw, "%s", buf); + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + g_free(buf); + return; + case HFP_GW_CMD_TYPE_READ: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CIND, 0, NULL); + return; + case HFP_GW_CMD_TYPE_SET: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_brsf(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int feat; + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &feat)) + break; + + if (hfp_gw_result_has_next(result)) + break; + + /* TODO verify features */ + device.features = feat; + + hfp_gw_send_info(device.gw, "+BRSF: %u", hfp_ag_features); + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void at_cmd_chld(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + struct hal_ev_handsfree_chld ev; + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!hfp_gw_result_get_number(result, &val) || val > 3) + break; + + /* No ECC support */ + if (hfp_gw_result_has_next(result)) + break; + + /* value match HAL type */ + ev.chld = val; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_EV_HANDSFREE_CHLD, sizeof(ev), &ev); + return; + case HFP_GW_CMD_TYPE_TEST: + hfp_gw_send_info(device.gw, "+CHLD: (%s)", HFP_AG_CHLD); + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + register_post_slc_at(); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); + return; + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static struct hfp_codec *find_codec_by_type(uint8_t type) +{ + int i; + + for (i = 0; i < CODECS_COUNT; i++) + if (type == device.codecs[i].type) + return &device.codecs[i]; + + return NULL; +} + +static void at_cmd_bac(struct hfp_gw_result *result, enum hfp_gw_cmd_type type, + void *user_data) +{ + unsigned int val; + + DBG(""); + + switch (type) { + case HFP_GW_CMD_TYPE_SET: + if (!(device.features & HFP_HF_FEAT_CODEC)) + goto failed; + + /* set codecs to defaults */ + init_codecs(); + device.negotiated_codec = 0; + + /* + * At least CVSD mandatory codec must exist + * HFP V1.6 4.34.1 + */ + if (!hfp_gw_result_get_number(result, &val) || + val != CODEC_ID_CVSD) + goto failed; + + device.codecs[CVSD_OFFSET].remote_supported = true; + + if (hfp_gw_result_get_number(result, &val)) { + if (val != CODEC_ID_MSBC) + goto failed; + + device.codecs[MSBC_OFFSET].remote_supported = true; + } + + while (hfp_gw_result_has_next(result)) { + struct hfp_codec *codec; + + if (!hfp_gw_result_get_number(result, &val)) + goto failed; + + codec = find_codec_by_type(val); + if (!codec) + continue; + + codec->remote_supported = true; + } + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + if (device.proposed_codec) + select_codec(0); + return; + case HFP_GW_CMD_TYPE_TEST: + case HFP_GW_CMD_TYPE_READ: + case HFP_GW_CMD_TYPE_COMMAND: + break; + } + +failed: + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); +} + +static void register_slc_at(void) +{ + hfp_gw_register(device.gw, at_cmd_brsf, "+BRSF", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cind, "+CIND", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_cmer, "+CMER", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_chld, "+CHLD", NULL, NULL); + hfp_gw_register(device.gw, at_cmd_bac, "+BAC", NULL, NULL); +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + DBG(""); + + if (err) { + error("handsfree: connect failed (%s)", err->message); + goto failed; + } + + device.gw = hfp_gw_new(g_io_channel_unix_get_fd(chan)); + if (!device.gw) + goto failed; + + g_io_channel_set_close_on_unref(chan, FALSE); + + hfp_gw_set_close_on_unref(device.gw, true); + hfp_gw_set_command_handler(device.gw, at_cmd_unknown, NULL, NULL); + hfp_gw_set_disconnect_handler(device.gw, disconnect_watch, NULL, NULL); + + if (device.hsp) { + register_post_slc_at(); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTED); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED); + return; + } + + register_slc_at(); + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_CONNECTED); + return; + +failed: + g_io_channel_shutdown(chan, TRUE, NULL); + device_cleanup(); +} + +static void confirm_cb(GIOChannel *chan, gpointer data) +{ + char address[18]; + bdaddr_t bdaddr; + GError *err = NULL; + + bt_io_get(chan, &err, + BT_IO_OPT_DEST, address, + BT_IO_OPT_DEST_BDADDR, &bdaddr, + BT_IO_OPT_INVALID); + if (err) { + error("handsfree: confirm failed (%s)", err->message); + g_error_free(err); + goto drop; + } + + DBG("incoming connect from %s", address); + + if (device.state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) { + info("handsfree: refusing connection from %s", address); + goto drop; + } + + device_init(&bdaddr); + + if (!bt_io_accept(chan, connect_cb, NULL, NULL, NULL)) { + error("handsfree: failed to accept connection"); + device_cleanup(); + goto drop; + } + + device.hsp = GPOINTER_TO_INT(data); + return; + +drop: + g_io_channel_shutdown(chan, TRUE, NULL); +} + +static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + sdp_list_t *protos, *classes; + GError *gerr = NULL; + GIOChannel *io; + uuid_t uuid; + int channel; + + DBG(""); + + if (err < 0) { + error("handsfree: unable to get SDP record: %s", + strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + info("handsfree: no HSP SDP records found"); + goto fail; + } + + if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) { + error("handsfree: unable to get service classes from record"); + goto fail; + } + + if (sdp_get_access_protos(recs->data, &protos) < 0) { + error("handsfree: unable to get access protocols from record"); + sdp_list_free(classes, free); + goto fail; + } + + /* TODO read remote version? */ + /* TODO read volume control support */ + + memcpy(&uuid, classes->data, sizeof(uuid)); + sdp_list_free(classes, free); + + if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 || + uuid.value.uuid16 != HEADSET_SVCLASS_ID) { + sdp_list_free(protos, NULL); + error("handsfree: invalid service record or not HSP"); + goto fail; + } + + channel = sdp_get_proto_port(protos, RFCOMM_UUID); + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); + sdp_list_free(protos, NULL); + if (channel <= 0) { + error("handsfree: unable to get RFCOMM channel from record"); + goto fail; + } + + io = bt_io_connect(connect_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &device.bdaddr, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_CHANNEL, channel, + BT_IO_OPT_INVALID); + if (!io) { + error("handsfree: unable to connect: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + + device.hsp = true; + + g_io_channel_unref(io); + return; + +fail: + device_cleanup(); +} + +static int sdp_search_hsp(void) +{ + uuid_t uuid; + + sdp_uuid16_create(&uuid, HEADSET_SVCLASS_ID); + + return bt_search_service(&adapter_addr, &device.bdaddr, &uuid, + sdp_hsp_search_cb, NULL, NULL, 0); +} + +static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + sdp_list_t *protos, *classes; + GError *gerr = NULL; + GIOChannel *io; + uuid_t uuid; + int channel; + + DBG(""); + + if (err < 0) { + error("handsfree: unable to get SDP record: %s", + strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + info("handsfree: no HFP SDP records found, trying HSP"); + + if (sdp_search_hsp() < 0) { + error("handsfree: HSP SDP search failed"); + goto fail; + } + + return; + } + + if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) { + error("handsfree: unable to get service classes from record"); + goto fail; + } + + if (sdp_get_access_protos(recs->data, &protos) < 0) { + error("handsfree: unable to get access protocols from record"); + sdp_list_free(classes, free); + goto fail; + } + + /* TODO read remote version? */ + + memcpy(&uuid, classes->data, sizeof(uuid)); + sdp_list_free(classes, free); + + if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 || + uuid.value.uuid16 != HANDSFREE_SVCLASS_ID) { + sdp_list_free(protos, NULL); + error("handsfree: invalid service record or not HFP"); + goto fail; + } + + channel = sdp_get_proto_port(protos, RFCOMM_UUID); + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); + sdp_list_free(protos, NULL); + if (channel <= 0) { + error("handsfree: unable to get RFCOMM channel from record"); + goto fail; + } + + io = bt_io_connect(connect_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &device.bdaddr, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_CHANNEL, channel, + BT_IO_OPT_INVALID); + if (!io) { + error("handsfree: unable to connect: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + + g_io_channel_unref(io); + return; + +fail: + device_cleanup(); +} + +static int sdp_search_hfp(void) +{ + uuid_t uuid; + + sdp_uuid16_create(&uuid, HANDSFREE_SVCLASS_ID); + + return bt_search_service(&adapter_addr, &device.bdaddr, &uuid, + sdp_hfp_search_cb, NULL, NULL, 0); +} + +static void handle_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_connect *cmd = buf; + char addr[18]; + uint8_t status; + bdaddr_t bdaddr; + int ret; + + DBG(""); + + if (device.state != HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED) { + status = HAL_STATUS_FAILED; + goto failed; + } + + android2bdaddr(&cmd->bdaddr, &bdaddr); + + ba2str(&bdaddr, addr); + DBG("connecting to %s", addr); + + device_init(&bdaddr); + + /* prefer HFP over HSP */ + ret = hfp_server ? sdp_search_hfp() : sdp_search_hsp(); + if (ret < 0) { + error("handsfree: SDP search failed"); + device_cleanup(); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CONNECT, status); +} + +static void handle_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_disconnect *cmd = buf; + bdaddr_t bdaddr; + uint8_t status; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + if (device.state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED || + bacmp(&device.bdaddr, &bdaddr)) { + status = HAL_STATUS_FAILED; + goto failed; + + } + + if (device.state == HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING) { + status = HAL_STATUS_SUCCESS; + goto failed; + } + + if (device.state == HAL_EV_HANDSFREE_CONN_STATE_CONNECTING) { + device_cleanup(); + } else { + device_set_state(HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTING); + hfp_gw_disconnect(device.gw); + } + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DISCONNECT, status); +} + +static bool disconnect_sco(void) +{ + if (!device.sco) + return false; + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTING); + + if (device.sco_watch) { + g_source_remove(device.sco_watch); + device.sco_watch = 0; + } + + g_io_channel_shutdown(device.sco, TRUE, NULL); + g_io_channel_unref(device.sco); + device.sco = NULL; + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED); + return true; +} + +static bool connect_audio(void) +{ + if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED) + return false; + + /* we haven't negotiated codec, start selection */ + if ((device.features & HFP_HF_FEAT_CODEC) && !device.negotiated_codec) { + select_codec(0); + return true; + } + + return connect_sco(); +} + +static void handle_connect_audio(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_connect_audio *cmd = buf; + bdaddr_t bdaddr; + uint8_t status; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED || + bacmp(&device.bdaddr, &bdaddr)) { + status = HAL_STATUS_FAILED; + goto done; + } + + status = connect_audio() ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CONNECT_AUDIO, status); +} + +static void handle_disconnect_audio(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_disconnect_audio *cmd = buf; + bdaddr_t bdaddr; + uint8_t status; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + if (device.audio_state != HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED || + bacmp(&device.bdaddr, &bdaddr)) { + status = HAL_STATUS_FAILED; + goto done; + } + + status = disconnect_sco() ? HAL_STATUS_SUCCESS : HAL_STATUS_FAILED; + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DISCONNECT_AUDIO, status); +} + +static void handle_start_vr(const void *buf, uint16_t len) +{ + uint8_t status; + + DBG(""); + + if (device.features & HFP_HF_FEAT_VR) { + hfp_gw_send_info(device.gw, "+BVRA: 1"); + status = HAL_STATUS_SUCCESS; + } else { + status = HAL_STATUS_FAILED; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_START_VR, status); +} + +static void handle_stop_vr(const void *buf, uint16_t len) +{ + uint8_t status; + + DBG(""); + + if (device.features & HFP_HF_FEAT_VR) { + hfp_gw_send_info(device.gw, "+BVRA: 0"); + status = HAL_STATUS_SUCCESS; + } else { + status = HAL_STATUS_FAILED; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_STOP_VR, status); +} + +static void handle_volume_control(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_volume_control *cmd = buf; + uint8_t status, volume; + + DBG("type=%u volume=%u", cmd->type, cmd->volume); + + volume = cmd->volume > 15 ? 15 : cmd->volume; + + switch (cmd->type) { + case HAL_HANDSFREE_VOLUME_TYPE_MIC: + hfp_gw_send_info(device.gw, "+VGM: %u", volume); + + status = HAL_STATUS_SUCCESS; + break; + case HAL_HANDSFREE_VOLUME_TYPE_SPEAKER: + hfp_gw_send_info(device.gw, "+VGS: %u", volume); + + status = HAL_STATUS_SUCCESS; + break; + default: + status = HAL_STATUS_FAILED; + break; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_VOLUME_CONTROL, status); +} + +static void update_indicator(int ind, uint8_t val) +{ + DBG("ind=%u new=%u old=%u", ind, val, device.inds[ind].val); + + if (device.inds[ind].val == val) + return; + + device.inds[ind].val = val; + + if (!device.indicators_enabled) + return; + + if (!device.inds[ind].active) + return; + + /* indicator numbers in CIEV start from 1 */ + hfp_gw_send_info(device.gw, "+CIEV: %u,%u", ind + 1, val); +} + +static void handle_device_status_notif(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_device_status_notif *cmd = buf; + + DBG(""); + + update_indicator(IND_SERVICE, cmd->state); + update_indicator(IND_ROAM, cmd->type); + update_indicator(IND_SIGNAL, cmd->signal); + update_indicator(IND_BATTCHG, cmd->battery); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF, + HAL_STATUS_SUCCESS); +} + +static void handle_cops(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_cops_response *cmd = buf; + + if (len != sizeof(*cmd) + cmd->len || + (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) { + error("Invalid cops response command, terminating"); + raise(SIGTERM); + return; + } + + DBG(""); + + hfp_gw_send_info(device.gw, "+COPS: 0,0,\"%.16s\"", + cmd->len ? (char *) cmd->buf : ""); + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_COPS_RESPONSE, HAL_STATUS_SUCCESS); +} + +static unsigned int get_callsetup(uint8_t state) +{ + switch (state) { + case HAL_HANDSFREE_CALL_STATE_INCOMING: + return 1; + case HAL_HANDSFREE_CALL_STATE_DIALING: + return 2; + case HAL_HANDSFREE_CALL_STATE_ALERTING: + return 3; + default: + return 0; + } +} + +static void handle_cind(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_cind_response *cmd = buf; + + DBG(""); + + /* HAL doesn't provide indicators values so need to convert here */ + device.inds[IND_SERVICE].val = cmd->svc; + device.inds[IND_CALL].val = !!(cmd->num_active + cmd->num_held); + device.inds[IND_CALLSETUP].val = get_callsetup(cmd->state); + device.inds[IND_CALLHELD].val = cmd->num_held ? + (cmd->num_active ? 1 : 2) : 0; + device.inds[IND_SIGNAL].val = cmd->signal; + device.inds[IND_ROAM].val = cmd->roam; + device.inds[IND_BATTCHG].val = cmd->batt_chg; + + /* Order must match indicators_defaults table */ + hfp_gw_send_info(device.gw, "+CIND: %u,%u,%u,%u,%u,%u,%u", + device.inds[IND_SERVICE].val, + device.inds[IND_CALL].val, + device.inds[IND_CALLSETUP].val, + device.inds[IND_CALLHELD].val, + device.inds[IND_SIGNAL].val, + device.inds[IND_ROAM].val, + device.inds[IND_BATTCHG].val); + + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CIND_RESPONSE, HAL_STATUS_SUCCESS); +} + +static void handle_formatted_at_resp(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_formatted_at_response *cmd = buf; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len || + (cmd->len != 0 && cmd->buf[cmd->len - 1] != '\0')) { + error("Invalid formatted AT response command, terminating"); + raise(SIGTERM); + return; + } + + DBG(""); + + hfp_gw_send_info(device.gw, "%s", cmd->len ? (char *) cmd->buf : ""); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE, + HAL_STATUS_SUCCESS); +} + +static void handle_at_resp(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_at_response *cmd = buf; + + DBG(""); + + if (cmd->response == HAL_HANDSFREE_AT_RESPONSE_OK) + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + else if (device.cmee_enabled) + hfp_gw_send_error(device.gw, cmd->error); + else + hfp_gw_send_result(device.gw, HFP_RESULT_ERROR); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_AT_RESPONSE, HAL_STATUS_SUCCESS); +} + +static void handle_clcc_resp(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_clcc_response *cmd = buf; + uint8_t status; + char *number; + + if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 && + cmd->number[cmd->number_len - 1] != '\0')) { + error("Invalid CLCC response command, terminating"); + raise(SIGTERM); + return; + } + + DBG(""); + + if (!cmd->index) { + hfp_gw_send_result(device.gw, HFP_RESULT_OK); + + status = HAL_STATUS_SUCCESS; + goto done; + } + + number = cmd->number_len ? (char *) cmd->number : ""; + + switch (cmd->state) { + case HAL_HANDSFREE_CALL_STATE_INCOMING: + case HAL_HANDSFREE_CALL_STATE_WAITING: + case HAL_HANDSFREE_CALL_STATE_ACTIVE: + case HAL_HANDSFREE_CALL_STATE_HELD: + case HAL_HANDSFREE_CALL_STATE_DIALING: + case HAL_HANDSFREE_CALL_STATE_ALERTING: + if (cmd->type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && + number[0] != '+') + hfp_gw_send_info(device.gw, + "+CLCC: %u,%u,%u,%u,%u,\"+%s\",%u", + cmd->index, cmd->dir, cmd->state, + cmd->mode, cmd->mpty, number, + cmd->type); + else + hfp_gw_send_info(device.gw, + "+CLCC: %u,%u,%u,%u,%u,\"%s\",%u", + cmd->index, cmd->dir, cmd->state, + cmd->mode, cmd->mpty, number, + cmd->type); + + status = HAL_STATUS_SUCCESS; + break; + case HAL_HANDSFREE_CALL_STATE_IDLE: + default: + status = HAL_STATUS_FAILED; + break; + } + +done: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_CLCC_RESPONSE, status); +} + +static gboolean ring_cb(gpointer user_data) +{ + char *clip = user_data; + + hfp_gw_send_info(device.gw, "RING"); + + if (device.clip_enabled && clip) + hfp_gw_send_info(device.gw, "%s", clip); + + return TRUE; +} + +static void phone_state_dialing(int num_active, int num_held) +{ + update_indicator(IND_CALLSETUP, 2); + + if (num_active == 0 && num_held > 0) + update_indicator(IND_CALLHELD, 2); + + if (device.num_active == 0 && device.num_held == 0) + connect_audio(); +} + +static void phone_state_alerting(int num_active, int num_held) +{ + update_indicator(IND_CALLSETUP, 3); +} + +static void phone_state_waiting(int num_active, int num_held, uint8_t type, + const uint8_t *number, int number_len) +{ + char *num; + + if (!device.ccwa_enabled) + return; + + num = number_len ? (char *) number : ""; + + if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+') + hfp_gw_send_info(device.gw, "+CCWA: \"+%s\",%u", num, type); + else + hfp_gw_send_info(device.gw, "+CCWA: \"%s\",%u", num, type); + + update_indicator(IND_CALLSETUP, 1); +} + +static void phone_state_incoming(int num_active, int num_held, uint8_t type, + const uint8_t *number, int number_len) +{ + char *clip, *num; + + if (device.setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) { + if (device.num_active != num_active || + device.num_held != num_held) { + /* + * calls changed while waiting call ie. due to + * termination of active call + */ + update_indicator(IND_CALLHELD, + num_held ? (num_active ? 1 : 2) : 0); + update_indicator(IND_CALL, !!(num_active + num_held)); + } + + return; + } + + if (device.call_hanging_up) + return; + + if (num_active > 0 || num_held > 0) { + phone_state_waiting(num_active, num_held, type, number, + number_len); + return; + } + + update_indicator(IND_CALLSETUP, 1); + + num = number_len ? (char *) number : ""; + + if (type == HAL_HANDSFREE_CALL_ADDRTYPE_INTERNATIONAL && num[0] != '+') + clip = g_strdup_printf("+CLIP: \"+%s\",%u", num, type); + else + clip = g_strdup_printf("+CLIP: \"%s\",%u", num, type); + + /* send first RING */ + ring_cb(clip); + + device.ring = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, + RING_TIMEOUT, ring_cb, + clip, g_free); + + if (!device.ring) + g_free(clip); +} + +static void phone_state_idle(int num_active, int num_held) +{ + if (device.ring) { + g_source_remove(device.ring); + device.ring = 0; + } + + switch (device.setup_state) { + case HAL_HANDSFREE_CALL_STATE_INCOMING: + if (num_active > device.num_active) { + update_indicator(IND_CALL, 1); + + if (device.num_active == 0 && device.num_held == 0) + connect_audio(); + } + + if (num_held > device.num_held) + update_indicator(IND_CALLHELD, 1); + + update_indicator(IND_CALLSETUP, 0); + + if (num_active == device.num_active && + num_held == device.num_held) + device.call_hanging_up = true; + + break; + case HAL_HANDSFREE_CALL_STATE_DIALING: + case HAL_HANDSFREE_CALL_STATE_ALERTING: + if (num_active > device.num_active) + update_indicator(IND_CALL, 1); + + update_indicator(IND_CALLHELD, + num_held ? (num_active ? 1 : 2) : 0); + + update_indicator(IND_CALLSETUP, 0); + break; + case HAL_HANDSFREE_CALL_STATE_IDLE: + + if (device.call_hanging_up) { + device.call_hanging_up = false; + return; + } + + /* check if calls swapped */ + if (num_held != 0 && num_active != 0 && + device.num_active == num_held && + device.num_held == num_active) { + /* TODO better way for forcing indicator */ + device.inds[IND_CALLHELD].val = 0; + } else if ((num_active > 0 || num_held > 0) && + device.num_active == 0 && + device.num_held == 0) { + /* + * If number of active or held calls change but there + * was no call setup change this means that there were + * calls present when headset was connected. + */ + connect_audio(); + } else if (num_active == 0 && num_held == 0) { + disconnect_sco(); + } + + update_indicator(IND_CALLHELD, + num_held ? (num_active ? 1 : 2) : 0); + update_indicator(IND_CALL, !!(num_active + num_held)); + update_indicator(IND_CALLSETUP, 0); + + /* If call was terminated due to carrier lost send NO CARRIER */ + if (num_active == 0 && num_held == 0 && + device.inds[IND_SERVICE].val == 0 && + (device.num_active > 0 || device.num_held > 0)) + hfp_gw_send_info(device.gw, "NO CARRIER"); + + break; + default: + DBG("unhandled state %u", device.setup_state); + break; + } +} + +static void handle_phone_state_change(const void *buf, uint16_t len) +{ + const struct hal_cmd_handsfree_phone_state_change *cmd = buf; + uint8_t status; + + if (len != sizeof(*cmd) + cmd->number_len || (cmd->number_len != 0 && + cmd->number[cmd->number_len - 1] != '\0')) { + error("Invalid phone state change command, terminating"); + raise(SIGTERM); + return; + } + + DBG("active=%u hold=%u state=%u", cmd->num_active, cmd->num_held, + cmd->state); + + switch (cmd->state) { + case HAL_HANDSFREE_CALL_STATE_DIALING: + phone_state_dialing(cmd->num_active, cmd->num_held); + break; + case HAL_HANDSFREE_CALL_STATE_ALERTING: + phone_state_alerting(cmd->num_active, cmd->num_held); + break; + case HAL_HANDSFREE_CALL_STATE_INCOMING: + phone_state_incoming(cmd->num_active, cmd->num_held, cmd->type, + cmd->number, cmd->number_len); + break; + case HAL_HANDSFREE_CALL_STATE_IDLE: + phone_state_idle(cmd->num_active, cmd->num_held); + break; + default: + DBG("unhandled new state %u (current state %u)", cmd->state, + device.setup_state); + + status = HAL_STATUS_FAILED; + goto failed; + } + + device.num_active = cmd->num_active; + device.num_held = cmd->num_held; + device.setup_state = cmd->state; + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HANDSFREE, + HAL_OP_HANDSFREE_PHONE_STATE_CHANGE, status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_HANDSFREE_CONNECT */ + { handle_connect, false, + sizeof(struct hal_cmd_handsfree_connect) }, + /* HAL_OP_HANDSFREE_DISCONNECT */ + { handle_disconnect, false, + sizeof(struct hal_cmd_handsfree_disconnect) }, + /* HAL_OP_HANDSFREE_CONNECT_AUDIO */ + { handle_connect_audio, false, + sizeof(struct hal_cmd_handsfree_connect_audio) }, + /* HAL_OP_HANDSFREE_DISCONNECT_AUDIO */ + { handle_disconnect_audio, false, + sizeof(struct hal_cmd_handsfree_disconnect_audio) }, + /* define HAL_OP_HANDSFREE_START_VR */ + { handle_start_vr, false, 0 }, + /* define HAL_OP_HANDSFREE_STOP_VR */ + { handle_stop_vr, false, 0 }, + /* HAL_OP_HANDSFREE_VOLUME_CONTROL */ + { handle_volume_control, false, + sizeof(struct hal_cmd_handsfree_volume_control) }, + /* HAL_OP_HANDSFREE_DEVICE_STATUS_NOTIF */ + { handle_device_status_notif, false, + sizeof(struct hal_cmd_handsfree_device_status_notif) }, + /* HAL_OP_HANDSFREE_COPS_RESPONSE */ + { handle_cops, true, + sizeof(struct hal_cmd_handsfree_cops_response) }, + /* HAL_OP_HANDSFREE_CIND_RESPONSE */ + { handle_cind, false, + sizeof(struct hal_cmd_handsfree_cind_response) }, + /* HAL_OP_HANDSFREE_FORMATTED_AT_RESPONSE */ + { handle_formatted_at_resp, true, + sizeof(struct hal_cmd_handsfree_formatted_at_response) }, + /* HAL_OP_HANDSFREE_AT_RESPONSE */ + { handle_at_resp, false, + sizeof(struct hal_cmd_handsfree_at_response) }, + /* HAL_OP_HANDSFREE_CLCC_RESPONSE */ + { handle_clcc_resp, true, + sizeof(struct hal_cmd_handsfree_clcc_response) }, + /* HAL_OP_HANDSFREE_PHONE_STATE_CHANGE */ + { handle_phone_state_change, true, + sizeof(struct hal_cmd_handsfree_phone_state_change) }, +}; + +static sdp_record_t *headset_ag_record(void) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t *record; + sdp_data_t *channel; + uint8_t netid = 0x01; + sdp_data_t *network; + uint8_t ch = HSP_AG_CHANNEL; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + network = sdp_data_alloc(SDP_UINT8, &netid); + if (!network) { + sdp_record_free(record); + return NULL; + } + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); + profile.version = 0x0102; + pfseq = sdp_list_append(NULL, &profile); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap_uuid); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(NULL, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + sdp_set_info_attr(record, "Voice Gateway", NULL, NULL); + + sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network); + + sdp_data_free(channel); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static void confirm_sco_cb(GIOChannel *chan, gpointer user_data) +{ + char address[18]; + bdaddr_t bdaddr; + GError *err = NULL; + + if (device.sco) + goto drop; + + bt_io_get(chan, &err, + BT_IO_OPT_DEST, address, + BT_IO_OPT_DEST_BDADDR, &bdaddr, + BT_IO_OPT_INVALID); + if (err) { + error("handsfree: audio confirm failed (%s)", err->message); + g_error_free(err); + goto drop; + } + + DBG("incoming SCO connection from %s", address); + + if (device.state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED || + bacmp(&device.bdaddr, &bdaddr)) { + error("handsfree: audio connection from %s rejected", address); + goto drop; + } + + if (!bt_io_accept(chan, connect_sco_cb, NULL, NULL, NULL)) { + error("handsfree: failed to accept audio connection"); + goto drop; + } + + device_set_audio_state(HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTING); + return; + +drop: + g_io_channel_shutdown(chan, TRUE, NULL); +} + +static bool enable_hsp_ag(void) +{ + sdp_record_t *rec; + GError *err = NULL; + + DBG(""); + + hsp_server = bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(true), + NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_CHANNEL, HSP_AG_CHANNEL, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (!hsp_server) { + error("Failed to listen on Headset rfcomm: %s", err->message); + g_error_free(err); + return false; + } + + rec = headset_ag_record(); + if (!rec) { + error("Failed to allocate Headset record"); + goto failed; + } + + if (bt_adapter_add_record(rec, 0) < 0) { + error("Failed to register Headset record"); + sdp_record_free(rec); + goto failed; + } + + hsp_record_id = rec->handle; + return true; + +failed: + g_io_channel_shutdown(hsp_server, TRUE, NULL); + g_io_channel_unref(hsp_server); + hsp_server = NULL; + + return false; +} + +static void cleanup_hsp_ag(void) +{ + if (hsp_server) { + g_io_channel_shutdown(hsp_server, TRUE, NULL); + g_io_channel_unref(hsp_server); + hsp_server = NULL; + } + + if (hsp_record_id > 0) { + bt_adapter_remove_record(hsp_record_id); + hsp_record_id = 0; + } +} + +static sdp_record_t *hfp_ag_record(void) +{ + sdp_list_t *svclass_id, *pfseq, *apseq, *root; + uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; + uuid_t l2cap_uuid, rfcomm_uuid; + sdp_profile_desc_t profile; + sdp_list_t *aproto, *proto[2]; + sdp_record_t *record; + sdp_data_t *channel, *features; + uint8_t netid = 0x01; + uint16_t sdpfeat; + sdp_data_t *network; + uint8_t ch = HFP_AG_CHANNEL; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + network = sdp_data_alloc(SDP_UINT8, &netid); + if (!network) { + sdp_record_free(record); + return NULL; + } + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); + svclass_id = sdp_list_append(NULL, &svclass_uuid); + sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); + svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); + profile.version = 0x0106; + pfseq = sdp_list_append(NULL, &profile); + sdp_set_profile_descs(record, pfseq); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap_uuid); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(NULL, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &ch); + proto[1] = sdp_list_append(proto[1], channel); + apseq = sdp_list_append(apseq, proto[1]); + + /* Codec Negotiation bit in SDP feature is different then in BRSF */ + sdpfeat = hfp_ag_features & 0x0000003F; + if (hfp_ag_features & HFP_AG_FEAT_CODEC) + sdpfeat |= 0x00000020; + else + sdpfeat &= ~0x00000020; + + features = sdp_data_alloc(SDP_UINT16, &sdpfeat); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); + + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + + sdp_set_info_attr(record, "Hands-Free Audio Gateway", NULL, NULL); + + sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network); + + sdp_data_free(channel); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(apseq, NULL); + sdp_list_free(pfseq, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(root, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static bool enable_hfp_ag(void) +{ + sdp_record_t *rec; + GError *err = NULL; + + DBG(""); + + if (hfp_server) + return false; + + hfp_server = bt_io_listen(NULL, confirm_cb, GINT_TO_POINTER(false), + NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_CHANNEL, HFP_AG_CHANNEL, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (!hfp_server) { + error("Failed to listen on Handsfree rfcomm: %s", err->message); + g_error_free(err); + return false; + } + + rec = hfp_ag_record(); + if (!rec) { + error("Failed to allocate Handsfree record"); + goto failed; + } + + if (bt_adapter_add_record(rec, 0) < 0) { + error("Failed to register Handsfree record"); + sdp_record_free(rec); + goto failed; + } + + hfp_record_id = rec->handle; + return true; + +failed: + g_io_channel_shutdown(hfp_server, TRUE, NULL); + g_io_channel_unref(hfp_server); + hfp_server = NULL; + + return false; +} + +static void cleanup_hfp_ag(void) +{ + if (hfp_server) { + g_io_channel_shutdown(hfp_server, TRUE, NULL); + g_io_channel_unref(hfp_server); + hfp_server = NULL; + } + + if (hfp_record_id > 0) { + bt_adapter_remove_record(hfp_record_id); + hfp_record_id = 0; + } +} + +static bool enable_sco_server(void) +{ + GError *err = NULL; + + sco_server = bt_io_listen(NULL, confirm_sco_cb, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_INVALID); + if (!sco_server) { + error("handsfree: Failed to listen on SCO: %s", err->message); + g_error_free(err); + cleanup_hsp_ag(); + cleanup_hfp_ag(); + return false; + } + + return true; +} + +static void disable_sco_server(void) +{ + if (sco_server) { + g_io_channel_shutdown(sco_server, TRUE, NULL); + g_io_channel_unref(sco_server); + sco_server = NULL; + } +} + +static void bt_sco_get_fd(const void *buf, uint16_t len) +{ + int fd; + GError *err; + struct sco_rsp_get_fd rsp; + + DBG(""); + + if (!device.sco) + goto failed; + + err = NULL; + if (!bt_io_get(device.sco, &err, BT_IO_OPT_MTU, &rsp.mtu, + BT_IO_OPT_INVALID)) { + error("Unable to get MTU: %s\n", err->message); + g_clear_error(&err); + goto failed; + } + + fd = g_io_channel_unix_get_fd(device.sco); + + DBG("fd %d mtu %u", fd, rsp.mtu); + + ipc_send_rsp_full(sco_ipc, SCO_SERVICE_ID, SCO_OP_GET_FD, + sizeof(rsp), &rsp, fd); + + return; + +failed: + ipc_send_rsp(sco_ipc, SCO_SERVICE_ID, SCO_OP_STATUS, SCO_STATUS_FAILED); +} + +static const struct ipc_handler sco_handlers[] = { + /* SCO_OP_GET_FD */ + { bt_sco_get_fd, false, 0 } +}; + +static void bt_sco_unregister(void) +{ + DBG(""); + + ipc_cleanup(sco_ipc); + sco_ipc = NULL; +} + +static bool bt_sco_register(ipc_disconnect_cb disconnect) +{ + DBG(""); + + sco_ipc = ipc_init(BLUEZ_SCO_SK_PATH, sizeof(BLUEZ_SCO_SK_PATH), + SCO_SERVICE_ID, false, disconnect, NULL); + if (!sco_ipc) + return false; + + ipc_register(sco_ipc, SCO_SERVICE_ID, sco_handlers, + G_N_ELEMENTS(sco_handlers)); + + return true; +} + +bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + DBG("mode 0x%x", mode); + + bacpy(&adapter_addr, addr); + + if (!enable_hsp_ag()) + return false; + + if (!enable_sco_server()) { + cleanup_hsp_ag(); + return false; + } + + if (mode == HAL_MODE_HANDSFREE_HSP_ONLY) + goto done; + + hfp_ag_features = HFP_AG_FEATURES; + + if (mode == HAL_MODE_HANDSFREE_HFP_WBS) + hfp_ag_features |= HFP_AG_FEAT_CODEC; + + if (enable_hfp_ag()) + goto done; + + cleanup_hsp_ag(); + disable_sco_server(); + hfp_ag_features = 0; + return false; + +done: + hal_ipc = ipc; + ipc_register(hal_ipc, HAL_SERVICE_ID_HANDSFREE, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + bt_sco_register(NULL); + + return true; +} + +void bt_handsfree_unregister(void) +{ + DBG(""); + + bt_sco_unregister(); + ipc_unregister(hal_ipc, HAL_SERVICE_ID_HANDSFREE); + hal_ipc = NULL; + + cleanup_hfp_ag(); + cleanup_hsp_ag(); + disable_sco_server(); + + hfp_ag_features = 0; +} diff -Nru bluez-4.101/android/handsfree.h bluez-5.23/android/handsfree.h --- bluez-4.101/android/handsfree.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/handsfree.h 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +bool bt_handsfree_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_handsfree_unregister(void); diff -Nru bluez-4.101/android/hardware/audio_effect.h bluez-5.23/android/hardware/audio_effect.h --- bluez-4.101/android/hardware/audio_effect.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/audio_effect.h 2013-12-27 17:16:56.000000000 +0000 @@ -0,0 +1,1009 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_AUDIO_EFFECT_H +#define ANDROID_AUDIO_EFFECT_H + +#include +#include +#include +#include +#include + +#include + +__BEGIN_DECLS + + +///////////////////////////////////////////////// +// Common Definitions +///////////////////////////////////////////////// + +// +//--- Effect descriptor structure effect_descriptor_t +// + +// Unique effect ID (can be generated from the following site: +// http://www.itu.int/ITU-T/asn1/uuid.html) +// This format is used for both "type" and "uuid" fields of the effect descriptor structure. +// - When used for effect type and the engine is implementing and effect corresponding to a standard +// OpenSL ES interface, this ID must be the one defined in OpenSLES_IID.h for that interface. +// - When used as uuid, it should be a unique UUID for this particular implementation. +typedef struct effect_uuid_s { + uint32_t timeLow; + uint16_t timeMid; + uint16_t timeHiAndVersion; + uint16_t clockSeq; + uint8_t node[6]; +} effect_uuid_t; + +// Maximum length of character strings in structures defines by this API. +#define EFFECT_STRING_LEN_MAX 64 + +// NULL UUID definition (matches SL_IID_NULL_) +#define EFFECT_UUID_INITIALIZER { 0xec7178ec, 0xe5e1, 0x4432, 0xa3f4, \ + { 0x46, 0x57, 0xe6, 0x79, 0x52, 0x10 } } +static const effect_uuid_t EFFECT_UUID_NULL_ = EFFECT_UUID_INITIALIZER; +static const effect_uuid_t * const EFFECT_UUID_NULL = &EFFECT_UUID_NULL_; +static const char * const EFFECT_UUID_NULL_STR = "ec7178ec-e5e1-4432-a3f4-4657e6795210"; + + +// The effect descriptor contains necessary information to facilitate the enumeration of the effect +// engines present in a library. +typedef struct effect_descriptor_s { + effect_uuid_t type; // UUID of to the OpenSL ES interface implemented by this effect + effect_uuid_t uuid; // UUID for this particular implementation + uint32_t apiVersion; // Version of the effect control API implemented + uint32_t flags; // effect engine capabilities/requirements flags (see below) + uint16_t cpuLoad; // CPU load indication (see below) + uint16_t memoryUsage; // Data Memory usage (see below) + char name[EFFECT_STRING_LEN_MAX]; // human readable effect name + char implementor[EFFECT_STRING_LEN_MAX]; // human readable effect implementor name +} effect_descriptor_t; + +// CPU load and memory usage indication: each effect implementation must provide an indication of +// its CPU and memory usage for the audio effect framework to limit the number of effects +// instantiated at a given time on a given platform. +// The CPU load is expressed in 0.1 MIPS units as estimated on an ARM9E core (ARMv5TE) with 0 WS. +// The memory usage is expressed in KB and includes only dynamically allocated memory + +// Definitions for flags field of effect descriptor. +// +---------------------------+-----------+----------------------------------- +// | description | bits | values +// +---------------------------+-----------+----------------------------------- +// | connection mode | 0..2 | 0 insert: after track process +// | | | 1 auxiliary: connect to track auxiliary +// | | | output and use send level +// | | | 2 replace: replaces track process function; +// | | | must implement SRC, volume and mono to stereo. +// | | | 3 pre processing: applied below audio HAL on input +// | | | 4 post processing: applied below audio HAL on output +// | | | 5 - 7 reserved +// +---------------------------+-----------+----------------------------------- +// | insertion preference | 3..5 | 0 none +// | | | 1 first of the chain +// | | | 2 last of the chain +// | | | 3 exclusive (only effect in the insert chain) +// | | | 4..7 reserved +// +---------------------------+-----------+----------------------------------- +// | Volume management | 6..8 | 0 none +// | | | 1 implements volume control +// | | | 2 requires volume indication +// | | | 4 reserved +// +---------------------------+-----------+----------------------------------- +// | Device indication | 9..11 | 0 none +// | | | 1 requires device updates +// | | | 2, 4 reserved +// +---------------------------+-----------+----------------------------------- +// | Sample input mode | 12..13 | 1 direct: process() function or EFFECT_CMD_SET_CONFIG +// | | | command must specify a buffer descriptor +// | | | 2 provider: process() function uses the +// | | | bufferProvider indicated by the +// | | | EFFECT_CMD_SET_CONFIG command to request input. +// | | | buffers. +// | | | 3 both: both input modes are supported +// +---------------------------+-----------+----------------------------------- +// | Sample output mode | 14..15 | 1 direct: process() function or EFFECT_CMD_SET_CONFIG +// | | | command must specify a buffer descriptor +// | | | 2 provider: process() function uses the +// | | | bufferProvider indicated by the +// | | | EFFECT_CMD_SET_CONFIG command to request output +// | | | buffers. +// | | | 3 both: both output modes are supported +// +---------------------------+-----------+----------------------------------- +// | Hardware acceleration | 16..17 | 0 No hardware acceleration +// | | | 1 non tunneled hw acceleration: the process() function +// | | | reads the samples, send them to HW accelerated +// | | | effect processor, reads back the processed samples +// | | | and returns them to the output buffer. +// | | | 2 tunneled hw acceleration: the process() function is +// | | | transparent. The effect interface is only used to +// | | | control the effect engine. This mode is relevant for +// | | | global effects actually applied by the audio +// | | | hardware on the output stream. +// +---------------------------+-----------+----------------------------------- +// | Audio Mode indication | 18..19 | 0 none +// | | | 1 requires audio mode updates +// | | | 2..3 reserved +// +---------------------------+-----------+----------------------------------- +// | Audio source indication | 20..21 | 0 none +// | | | 1 requires audio source updates +// | | | 2..3 reserved +// +---------------------------+-----------+----------------------------------- +// | Effect offload supported | 22 | 0 The effect cannot be offloaded to an audio DSP +// | | | 1 The effect can be offloaded to an audio DSP +// +---------------------------+-----------+----------------------------------- + +// Insert mode +#define EFFECT_FLAG_TYPE_SHIFT 0 +#define EFFECT_FLAG_TYPE_SIZE 3 +#define EFFECT_FLAG_TYPE_MASK (((1 << EFFECT_FLAG_TYPE_SIZE) -1) \ + << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_INSERT (0 << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_AUXILIARY (1 << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_REPLACE (2 << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_PRE_PROC (3 << EFFECT_FLAG_TYPE_SHIFT) +#define EFFECT_FLAG_TYPE_POST_PROC (4 << EFFECT_FLAG_TYPE_SHIFT) + +// Insert preference +#define EFFECT_FLAG_INSERT_SHIFT (EFFECT_FLAG_TYPE_SHIFT + EFFECT_FLAG_TYPE_SIZE) +#define EFFECT_FLAG_INSERT_SIZE 3 +#define EFFECT_FLAG_INSERT_MASK (((1 << EFFECT_FLAG_INSERT_SIZE) -1) \ + << EFFECT_FLAG_INSERT_SHIFT) +#define EFFECT_FLAG_INSERT_ANY (0 << EFFECT_FLAG_INSERT_SHIFT) +#define EFFECT_FLAG_INSERT_FIRST (1 << EFFECT_FLAG_INSERT_SHIFT) +#define EFFECT_FLAG_INSERT_LAST (2 << EFFECT_FLAG_INSERT_SHIFT) +#define EFFECT_FLAG_INSERT_EXCLUSIVE (3 << EFFECT_FLAG_INSERT_SHIFT) + + +// Volume control +#define EFFECT_FLAG_VOLUME_SHIFT (EFFECT_FLAG_INSERT_SHIFT + EFFECT_FLAG_INSERT_SIZE) +#define EFFECT_FLAG_VOLUME_SIZE 3 +#define EFFECT_FLAG_VOLUME_MASK (((1 << EFFECT_FLAG_VOLUME_SIZE) -1) \ + << EFFECT_FLAG_VOLUME_SHIFT) +#define EFFECT_FLAG_VOLUME_CTRL (1 << EFFECT_FLAG_VOLUME_SHIFT) +#define EFFECT_FLAG_VOLUME_IND (2 << EFFECT_FLAG_VOLUME_SHIFT) +#define EFFECT_FLAG_VOLUME_NONE (0 << EFFECT_FLAG_VOLUME_SHIFT) + +// Device indication +#define EFFECT_FLAG_DEVICE_SHIFT (EFFECT_FLAG_VOLUME_SHIFT + EFFECT_FLAG_VOLUME_SIZE) +#define EFFECT_FLAG_DEVICE_SIZE 3 +#define EFFECT_FLAG_DEVICE_MASK (((1 << EFFECT_FLAG_DEVICE_SIZE) -1) \ + << EFFECT_FLAG_DEVICE_SHIFT) +#define EFFECT_FLAG_DEVICE_IND (1 << EFFECT_FLAG_DEVICE_SHIFT) +#define EFFECT_FLAG_DEVICE_NONE (0 << EFFECT_FLAG_DEVICE_SHIFT) + +// Sample input modes +#define EFFECT_FLAG_INPUT_SHIFT (EFFECT_FLAG_DEVICE_SHIFT + EFFECT_FLAG_DEVICE_SIZE) +#define EFFECT_FLAG_INPUT_SIZE 2 +#define EFFECT_FLAG_INPUT_MASK (((1 << EFFECT_FLAG_INPUT_SIZE) -1) \ + << EFFECT_FLAG_INPUT_SHIFT) +#define EFFECT_FLAG_INPUT_DIRECT (1 << EFFECT_FLAG_INPUT_SHIFT) +#define EFFECT_FLAG_INPUT_PROVIDER (2 << EFFECT_FLAG_INPUT_SHIFT) +#define EFFECT_FLAG_INPUT_BOTH (3 << EFFECT_FLAG_INPUT_SHIFT) + +// Sample output modes +#define EFFECT_FLAG_OUTPUT_SHIFT (EFFECT_FLAG_INPUT_SHIFT + EFFECT_FLAG_INPUT_SIZE) +#define EFFECT_FLAG_OUTPUT_SIZE 2 +#define EFFECT_FLAG_OUTPUT_MASK (((1 << EFFECT_FLAG_OUTPUT_SIZE) -1) \ + << EFFECT_FLAG_OUTPUT_SHIFT) +#define EFFECT_FLAG_OUTPUT_DIRECT (1 << EFFECT_FLAG_OUTPUT_SHIFT) +#define EFFECT_FLAG_OUTPUT_PROVIDER (2 << EFFECT_FLAG_OUTPUT_SHIFT) +#define EFFECT_FLAG_OUTPUT_BOTH (3 << EFFECT_FLAG_OUTPUT_SHIFT) + +// Hardware acceleration mode +#define EFFECT_FLAG_HW_ACC_SHIFT (EFFECT_FLAG_OUTPUT_SHIFT + EFFECT_FLAG_OUTPUT_SIZE) +#define EFFECT_FLAG_HW_ACC_SIZE 2 +#define EFFECT_FLAG_HW_ACC_MASK (((1 << EFFECT_FLAG_HW_ACC_SIZE) -1) \ + << EFFECT_FLAG_HW_ACC_SHIFT) +#define EFFECT_FLAG_HW_ACC_SIMPLE (1 << EFFECT_FLAG_HW_ACC_SHIFT) +#define EFFECT_FLAG_HW_ACC_TUNNEL (2 << EFFECT_FLAG_HW_ACC_SHIFT) + +// Audio mode indication +#define EFFECT_FLAG_AUDIO_MODE_SHIFT (EFFECT_FLAG_HW_ACC_SHIFT + EFFECT_FLAG_HW_ACC_SIZE) +#define EFFECT_FLAG_AUDIO_MODE_SIZE 2 +#define EFFECT_FLAG_AUDIO_MODE_MASK (((1 << EFFECT_FLAG_AUDIO_MODE_SIZE) -1) \ + << EFFECT_FLAG_AUDIO_MODE_SHIFT) +#define EFFECT_FLAG_AUDIO_MODE_IND (1 << EFFECT_FLAG_AUDIO_MODE_SHIFT) +#define EFFECT_FLAG_AUDIO_MODE_NONE (0 << EFFECT_FLAG_AUDIO_MODE_SHIFT) + +// Audio source indication +#define EFFECT_FLAG_AUDIO_SOURCE_SHIFT (EFFECT_FLAG_AUDIO_MODE_SHIFT + EFFECT_FLAG_AUDIO_MODE_SIZE) +#define EFFECT_FLAG_AUDIO_SOURCE_SIZE 2 +#define EFFECT_FLAG_AUDIO_SOURCE_MASK (((1 << EFFECT_FLAG_AUDIO_SOURCE_SIZE) -1) \ + << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) +#define EFFECT_FLAG_AUDIO_SOURCE_IND (1 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) +#define EFFECT_FLAG_AUDIO_SOURCE_NONE (0 << EFFECT_FLAG_AUDIO_SOURCE_SHIFT) + +// Effect offload indication +#define EFFECT_FLAG_OFFLOAD_SHIFT (EFFECT_FLAG_AUDIO_SOURCE_SHIFT + \ + EFFECT_FLAG_AUDIO_SOURCE_SIZE) +#define EFFECT_FLAG_OFFLOAD_SIZE 1 +#define EFFECT_FLAG_OFFLOAD_MASK (((1 << EFFECT_FLAG_OFFLOAD_SIZE) -1) \ + << EFFECT_FLAG_OFFLOAD_SHIFT) +#define EFFECT_FLAG_OFFLOAD_SUPPORTED (1 << EFFECT_FLAG_OFFLOAD_SHIFT) + +#define EFFECT_MAKE_API_VERSION(M, m) (((M)<<16) | ((m) & 0xFFFF)) +#define EFFECT_API_VERSION_MAJOR(v) ((v)>>16) +#define EFFECT_API_VERSION_MINOR(v) ((m) & 0xFFFF) + + + +///////////////////////////////////////////////// +// Effect control interface +///////////////////////////////////////////////// + +// Effect control interface version 2.0 +#define EFFECT_CONTROL_API_VERSION EFFECT_MAKE_API_VERSION(2,0) + +// Effect control interface structure: effect_interface_s +// The effect control interface is exposed by each effect engine implementation. It consists of +// a set of functions controlling the configuration, activation and process of the engine. +// The functions are grouped in a structure of type effect_interface_s. +// +// Effect control interface handle: effect_handle_t +// The effect_handle_t serves two purposes regarding the implementation of the effect engine: +// - 1 it is the address of a pointer to an effect_interface_s structure where the functions +// of the effect control API for a particular effect are located. +// - 2 it is the address of the context of a particular effect instance. +// A typical implementation in the effect library would define a structure as follows: +// struct effect_module_s { +// const struct effect_interface_s *itfe; +// effect_config_t config; +// effect_context_t context; +// } +// The implementation of EffectCreate() function would then allocate a structure of this +// type and return its address as effect_handle_t +typedef struct effect_interface_s **effect_handle_t; + + +// Forward definition of type audio_buffer_t +typedef struct audio_buffer_s audio_buffer_t; + + + + + + +// Effect control interface definition +struct effect_interface_s { + //////////////////////////////////////////////////////////////////////////////// + // + // Function: process + // + // Description: Effect process function. Takes input samples as specified + // (count and location) in input buffer descriptor and output processed + // samples as specified in output buffer descriptor. If the buffer descriptor + // is not specified the function must use either the buffer or the + // buffer provider function installed by the EFFECT_CMD_SET_CONFIG command. + // The effect framework will call the process() function after the EFFECT_CMD_ENABLE + // command is received and until the EFFECT_CMD_DISABLE is received. When the engine + // receives the EFFECT_CMD_DISABLE command it should turn off the effect gracefully + // and when done indicate that it is OK to stop calling the process() function by + // returning the -ENODATA status. + // + // NOTE: the process() function implementation should be "real-time safe" that is + // it should not perform blocking calls: malloc/free, sleep, read/write/open/close, + // pthread_cond_wait/pthread_mutex_lock... + // + // Input: + // self: handle to the effect interface this function + // is called on. + // inBuffer: buffer descriptor indicating where to read samples to process. + // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command. + // + // outBuffer: buffer descriptor indicating where to write processed samples. + // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG command. + // + // Output: + // returned value: 0 successful operation + // -ENODATA the engine has finished the disable phase and the framework + // can stop calling process() + // -EINVAL invalid interface handle or + // invalid input/output buffer description + //////////////////////////////////////////////////////////////////////////////// + int32_t (*process)(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer); + //////////////////////////////////////////////////////////////////////////////// + // + // Function: command + // + // Description: Send a command and receive a response to/from effect engine. + // + // Input: + // self: handle to the effect interface this function + // is called on. + // cmdCode: command code: the command can be a standardized command defined in + // effect_command_e (see below) or a proprietary command. + // cmdSize: size of command in bytes + // pCmdData: pointer to command data + // pReplyData: pointer to reply data + // + // Input/Output: + // replySize: maximum size of reply data as input + // actual size of reply data as output + // + // Output: + // returned value: 0 successful operation + // -EINVAL invalid interface handle or + // invalid command/reply size or format according to command code + // The return code should be restricted to indicate problems related to the this + // API specification. Status related to the execution of a particular command should be + // indicated as part of the reply field. + // + // *pReplyData updated with command response + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*command)(effect_handle_t self, + uint32_t cmdCode, + uint32_t cmdSize, + void *pCmdData, + uint32_t *replySize, + void *pReplyData); + //////////////////////////////////////////////////////////////////////////////// + // + // Function: get_descriptor + // + // Description: Returns the effect descriptor + // + // Input: + // self: handle to the effect interface this function + // is called on. + // + // Input/Output: + // pDescriptor: address where to return the effect descriptor. + // + // Output: + // returned value: 0 successful operation. + // -EINVAL invalid interface handle or invalid pDescriptor + // *pDescriptor: updated with the effect descriptor. + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*get_descriptor)(effect_handle_t self, + effect_descriptor_t *pDescriptor); + //////////////////////////////////////////////////////////////////////////////// + // + // Function: process_reverse + // + // Description: Process reverse stream function. This function is used to pass + // a reference stream to the effect engine. If the engine does not need a reference + // stream, this function pointer can be set to NULL. + // This function would typically implemented by an Echo Canceler. + // + // Input: + // self: handle to the effect interface this function + // is called on. + // inBuffer: buffer descriptor indicating where to read samples to process. + // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command. + // + // outBuffer: buffer descriptor indicating where to write processed samples. + // If NULL, use the configuration passed by EFFECT_CMD_SET_CONFIG_REVERSE command. + // If the buffer and buffer provider in the configuration received by + // EFFECT_CMD_SET_CONFIG_REVERSE are also NULL, do not return modified reverse + // stream data + // + // Output: + // returned value: 0 successful operation + // -ENODATA the engine has finished the disable phase and the framework + // can stop calling process_reverse() + // -EINVAL invalid interface handle or + // invalid input/output buffer description + //////////////////////////////////////////////////////////////////////////////// + int32_t (*process_reverse)(effect_handle_t self, + audio_buffer_t *inBuffer, + audio_buffer_t *outBuffer); +}; + + +// +//--- Standardized command codes for command() function +// +enum effect_command_e { + EFFECT_CMD_INIT, // initialize effect engine + EFFECT_CMD_SET_CONFIG, // configure effect engine (see effect_config_t) + EFFECT_CMD_RESET, // reset effect engine + EFFECT_CMD_ENABLE, // enable effect process + EFFECT_CMD_DISABLE, // disable effect process + EFFECT_CMD_SET_PARAM, // set parameter immediately (see effect_param_t) + EFFECT_CMD_SET_PARAM_DEFERRED, // set parameter deferred + EFFECT_CMD_SET_PARAM_COMMIT, // commit previous set parameter deferred + EFFECT_CMD_GET_PARAM, // get parameter + EFFECT_CMD_SET_DEVICE, // set audio device (see audio.h, audio_devices_t) + EFFECT_CMD_SET_VOLUME, // set volume + EFFECT_CMD_SET_AUDIO_MODE, // set the audio mode (normal, ring, ...) + EFFECT_CMD_SET_CONFIG_REVERSE, // configure effect engine reverse stream(see effect_config_t) + EFFECT_CMD_SET_INPUT_DEVICE, // set capture device (see audio.h, audio_devices_t) + EFFECT_CMD_GET_CONFIG, // read effect engine configuration + EFFECT_CMD_GET_CONFIG_REVERSE, // read configure effect engine reverse stream configuration + EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS,// get all supported configurations for a feature. + EFFECT_CMD_GET_FEATURE_CONFIG, // get current feature configuration + EFFECT_CMD_SET_FEATURE_CONFIG, // set current feature configuration + EFFECT_CMD_SET_AUDIO_SOURCE, // set the audio source (see audio.h, audio_source_t) + EFFECT_CMD_OFFLOAD, // set if effect thread is an offload one, + // send the ioHandle of the effect thread + EFFECT_CMD_FIRST_PROPRIETARY = 0x10000 // first proprietary command code +}; + +//================================================================================================== +// command: EFFECT_CMD_INIT +//-------------------------------------------------------------------------------------------------- +// description: +// Initialize effect engine: All configurations return to default +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_CONFIG +//-------------------------------------------------------------------------------------------------- +// description: +// Apply new audio parameters configurations for input and output buffers +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_config_t) +// data: effect_config_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_RESET +//-------------------------------------------------------------------------------------------------- +// description: +// Reset the effect engine. Keep configuration but resets state and buffer content +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_ENABLE +//-------------------------------------------------------------------------------------------------- +// description: +// Enable the process. Called by the framework before the first call to process() +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_DISABLE +//-------------------------------------------------------------------------------------------------- +// description: +// Disable the process. Called by the framework after the last call to process() +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_PARAM +//-------------------------------------------------------------------------------------------------- +// description: +// Set a parameter and apply it immediately +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_param_t) + size of param and value +// data: effect_param_t + param + value. See effect_param_t definition below for value offset +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_PARAM_DEFERRED +//-------------------------------------------------------------------------------------------------- +// description: +// Set a parameter but apply it only when receiving EFFECT_CMD_SET_PARAM_COMMIT command +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_param_t) + size of param and value +// data: effect_param_t + param + value. See effect_param_t definition below for value offset +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_SET_PARAM_COMMIT +//-------------------------------------------------------------------------------------------------- +// description: +// Apply all previously received EFFECT_CMD_SET_PARAM_DEFERRED commands +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_GET_PARAM +//-------------------------------------------------------------------------------------------------- +// description: +// Get a parameter value +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_param_t) + size of param +// data: effect_param_t + param +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(effect_param_t) + size of param and value +// data: effect_param_t + param + value. See effect_param_t definition below for value offset +//================================================================================================== +// command: EFFECT_CMD_SET_DEVICE +//-------------------------------------------------------------------------------------------------- +// description: +// Set the rendering device the audio output path is connected to. See audio.h, audio_devices_t +// for device values. +// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this +// command when the device changes +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_SET_VOLUME +//-------------------------------------------------------------------------------------------------- +// description: +// Set and get volume. Used by audio framework to delegate volume control to effect engine. +// The effect implementation must set EFFECT_FLAG_VOLUME_IND or EFFECT_FLAG_VOLUME_CTRL flag in +// its descriptor to receive this command before every call to process() function +// If EFFECT_FLAG_VOLUME_CTRL flag is set in the effect descriptor, the effect engine must return +// the volume that should be applied before the effect is processed. The overall volume (the volume +// actually applied by the effect engine multiplied by the returned value) should match the value +// indicated in the command. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: n * sizeof(uint32_t) +// data: volume for each channel defined in effect_config_t for output buffer expressed in +// 8.24 fixed point format +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: n * sizeof(uint32_t) / 0 +// data: - if EFFECT_FLAG_VOLUME_CTRL is set in effect descriptor: +// volume for each channel defined in effect_config_t for output buffer expressed in +// 8.24 fixed point format +// - if EFFECT_FLAG_VOLUME_CTRL is not set in effect descriptor: +// N/A +// It is legal to receive a null pointer as pReplyData in which case the effect framework has +// delegated volume control to another effect +//================================================================================================== +// command: EFFECT_CMD_SET_AUDIO_MODE +//-------------------------------------------------------------------------------------------------- +// description: +// Set the audio mode. The effect implementation must set EFFECT_FLAG_AUDIO_MODE_IND flag in its +// descriptor to receive this command when the audio mode changes. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: audio_mode_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_SET_CONFIG_REVERSE +//-------------------------------------------------------------------------------------------------- +// description: +// Apply new audio parameters configurations for input and output buffers of reverse stream. +// An example of reverse stream is the echo reference supplied to an Acoustic Echo Canceler. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_config_t) +// data: effect_config_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(int) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_INPUT_DEVICE +//-------------------------------------------------------------------------------------------------- +// description: +// Set the capture device the audio input path is connected to. See audio.h, audio_devices_t +// for device values. +// The effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to receive this +// command when the device changes +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_GET_CONFIG +//-------------------------------------------------------------------------------------------------- +// description: +// Read audio parameters configurations for input and output buffers +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(effect_config_t) +// data: effect_config_t +//================================================================================================== +// command: EFFECT_CMD_GET_CONFIG_REVERSE +//-------------------------------------------------------------------------------------------------- +// description: +// Read audio parameters configurations for input and output buffers of reverse stream +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 0 +// data: N/A +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(effect_config_t) +// data: effect_config_t +//================================================================================================== +// command: EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS +//-------------------------------------------------------------------------------------------------- +// description: +// Queries for supported configurations for a particular feature (e.g. get the supported +// combinations of main and auxiliary channels for a noise suppressor). +// The command parameter is the feature identifier (See effect_feature_e for a list of defined +// features) followed by the maximum number of configuration descriptor to return. +// The reply is composed of: +// - status (uint32_t): +// - 0 if feature is supported +// - -ENOSYS if the feature is not supported, +// - -ENOMEM if the feature is supported but the total number of supported configurations +// exceeds the maximum number indicated by the caller. +// - total number of supported configurations (uint32_t) +// - an array of configuration descriptors. +// The actual number of descriptors returned must not exceed the maximum number indicated by +// the caller. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: 2 x sizeof(uint32_t) +// data: effect_feature_e + maximum number of configurations to return +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 2 x sizeof(uint32_t) + n x sizeof () +// data: status + total number of configurations supported + array of n config descriptors +//================================================================================================== +// command: EFFECT_CMD_GET_FEATURE_CONFIG +//-------------------------------------------------------------------------------------------------- +// description: +// Retrieves current configuration for a given feature. +// The reply status is: +// - 0 if feature is supported +// - -ENOSYS if the feature is not supported, +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: effect_feature_e +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(uint32_t) + sizeof () +// data: status + config descriptor +//================================================================================================== +// command: EFFECT_CMD_SET_FEATURE_CONFIG +//-------------------------------------------------------------------------------------------------- +// description: +// Sets current configuration for a given feature. +// The reply status is: +// - 0 if feature is supported +// - -ENOSYS if the feature is not supported, +// - -EINVAL if the configuration is invalid +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) + sizeof () +// data: effect_feature_e + config descriptor +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(uint32_t) +// data: status +//================================================================================================== +// command: EFFECT_CMD_SET_AUDIO_SOURCE +//-------------------------------------------------------------------------------------------------- +// description: +// Set the audio source the capture path is configured for (Camcorder, voice recognition...). +// See audio.h, audio_source_t for values. +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: 0 +// data: N/A +//================================================================================================== +// command: EFFECT_CMD_OFFLOAD +//-------------------------------------------------------------------------------------------------- +// description: +// 1.indicate if the playback thread the effect is attached to is offloaded or not +// 2.update the io handle of the playback thread the effect is attached to +//-------------------------------------------------------------------------------------------------- +// command format: +// size: sizeof(effect_offload_param_t) +// data: effect_offload_param_t +//-------------------------------------------------------------------------------------------------- +// reply format: +// size: sizeof(uint32_t) +// data: uint32_t +//-------------------------------------------------------------------------------------------------- +// command: EFFECT_CMD_FIRST_PROPRIETARY +//-------------------------------------------------------------------------------------------------- +// description: +// All proprietary effect commands must use command codes above this value. The size and format of +// command and response fields is free in this case +//================================================================================================== + + +// Audio buffer descriptor used by process(), bufferProvider() functions and buffer_config_t +// structure. Multi-channel audio is always interleaved. The channel order is from LSB to MSB with +// regard to the channel mask definition in audio.h, audio_channel_mask_t e.g : +// Stereo: left, right +// 5 point 1: front left, front right, front center, low frequency, back left, back right +// The buffer size is expressed in frame count, a frame being composed of samples for all +// channels at a given time. Frame size for unspecified format (AUDIO_FORMAT_OTHER) is 8 bit by +// definition +struct audio_buffer_s { + size_t frameCount; // number of frames in buffer + union { + void* raw; // raw pointer to start of buffer + int32_t* s32; // pointer to signed 32 bit data at start of buffer + int16_t* s16; // pointer to signed 16 bit data at start of buffer + uint8_t* u8; // pointer to unsigned 8 bit data at start of buffer + }; +}; + +// The buffer_provider_s structure contains functions that can be used +// by the effect engine process() function to query and release input +// or output audio buffer. +// The getBuffer() function is called to retrieve a buffer where data +// should read from or written to by process() function. +// The releaseBuffer() function MUST be called when the buffer retrieved +// with getBuffer() is not needed anymore. +// The process function should use the buffer provider mechanism to retrieve +// input or output buffer if the inBuffer or outBuffer passed as argument is NULL +// and the buffer configuration (buffer_config_t) given by the EFFECT_CMD_SET_CONFIG +// command did not specify an audio buffer. + +typedef int32_t (* buffer_function_t)(void *cookie, audio_buffer_t *buffer); + +typedef struct buffer_provider_s { + buffer_function_t getBuffer; // retrieve next buffer + buffer_function_t releaseBuffer; // release used buffer + void *cookie; // for use by client of buffer provider functions +} buffer_provider_t; + + +// The buffer_config_s structure specifies the input or output audio format +// to be used by the effect engine. It is part of the effect_config_t +// structure that defines both input and output buffer configurations and is +// passed by the EFFECT_CMD_SET_CONFIG or EFFECT_CMD_SET_CONFIG_REVERSE command. +typedef struct buffer_config_s { + audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly + uint32_t samplingRate; // sampling rate + uint32_t channels; // channel mask (see audio_channel_mask_t in audio.h) + buffer_provider_t bufferProvider; // buffer provider + uint8_t format; // Audio format (see see audio_format_t in audio.h) + uint8_t accessMode; // read/write or accumulate in buffer (effect_buffer_access_e) + uint16_t mask; // indicates which of the above fields is valid +} buffer_config_t; + +// Values for "accessMode" field of buffer_config_t: +// overwrite, read only, accumulate (read/modify/write) +enum effect_buffer_access_e { + EFFECT_BUFFER_ACCESS_WRITE, + EFFECT_BUFFER_ACCESS_READ, + EFFECT_BUFFER_ACCESS_ACCUMULATE + +}; + +// feature identifiers for EFFECT_CMD_GET_FEATURE_SUPPORTED_CONFIGS command +enum effect_feature_e { + EFFECT_FEATURE_AUX_CHANNELS, // supports auxiliary channels (e.g. dual mic noise suppressor) + EFFECT_FEATURE_CNT +}; + +// EFFECT_FEATURE_AUX_CHANNELS feature configuration descriptor. Describe a combination +// of main and auxiliary channels supported +typedef struct channel_config_s { + audio_channel_mask_t main_channels; // channel mask for main channels + audio_channel_mask_t aux_channels; // channel mask for auxiliary channels +} channel_config_t; + + +// Values for bit field "mask" in buffer_config_t. If a bit is set, the corresponding field +// in buffer_config_t must be taken into account when executing the EFFECT_CMD_SET_CONFIG command +#define EFFECT_CONFIG_BUFFER 0x0001 // buffer field must be taken into account +#define EFFECT_CONFIG_SMP_RATE 0x0002 // samplingRate field must be taken into account +#define EFFECT_CONFIG_CHANNELS 0x0004 // channels field must be taken into account +#define EFFECT_CONFIG_FORMAT 0x0008 // format field must be taken into account +#define EFFECT_CONFIG_ACC_MODE 0x0010 // accessMode field must be taken into account +#define EFFECT_CONFIG_PROVIDER 0x0020 // bufferProvider field must be taken into account +#define EFFECT_CONFIG_ALL (EFFECT_CONFIG_BUFFER | EFFECT_CONFIG_SMP_RATE | \ + EFFECT_CONFIG_CHANNELS | EFFECT_CONFIG_FORMAT | \ + EFFECT_CONFIG_ACC_MODE | EFFECT_CONFIG_PROVIDER) + + +// effect_config_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_CONFIG +// command to configure audio parameters and buffers for effect engine input and output. +typedef struct effect_config_s { + buffer_config_t inputCfg; + buffer_config_t outputCfg; +} effect_config_t; + + +// effect_param_s structure describes the format of the pCmdData argument of EFFECT_CMD_SET_PARAM +// command and pCmdData and pReplyData of EFFECT_CMD_GET_PARAM command. +// psize and vsize represent the actual size of parameter and value. +// +// NOTE: the start of value field inside the data field is always on a 32 bit boundary: +// +// +-----------+ +// | status | sizeof(int) +// +-----------+ +// | psize | sizeof(int) +// +-----------+ +// | vsize | sizeof(int) +// +-----------+ +// | | | | +// ~ parameter ~ > psize | +// | | | > ((psize - 1)/sizeof(int) + 1) * sizeof(int) +// +-----------+ | +// | padding | | +// +-----------+ +// | | | +// ~ value ~ > vsize +// | | | +// +-----------+ + +typedef struct effect_param_s { + int32_t status; // Transaction status (unused for command, used for reply) + uint32_t psize; // Parameter size + uint32_t vsize; // Value size + char data[]; // Start of Parameter + Value data +} effect_param_t; + +// structure used by EFFECT_CMD_OFFLOAD command +typedef struct effect_offload_param_s { + bool isOffload; // true if the playback thread the effect is attached to is offloaded + int ioHandle; // io handle of the playback thread the effect is attached to +} effect_offload_param_t; + + +///////////////////////////////////////////////// +// Effect library interface +///////////////////////////////////////////////// + +// Effect library interface version 3.0 +// Note that EffectsFactory.c only checks the major version component, so changes to the minor +// number can only be used for fully backwards compatible changes +#define EFFECT_LIBRARY_API_VERSION EFFECT_MAKE_API_VERSION(3,0) + +#define AUDIO_EFFECT_LIBRARY_TAG ((('A') << 24) | (('E') << 16) | (('L') << 8) | ('T')) + +// Every effect library must have a data structure named AUDIO_EFFECT_LIBRARY_INFO_SYM +// and the fields of this data structure must begin with audio_effect_library_t + +typedef struct audio_effect_library_s { + // tag must be initialized to AUDIO_EFFECT_LIBRARY_TAG + uint32_t tag; + // Version of the effect library API : 0xMMMMmmmm MMMM: Major, mmmm: minor + uint32_t version; + // Name of this library + const char *name; + // Author/owner/implementor of the library + const char *implementor; + + //////////////////////////////////////////////////////////////////////////////// + // + // Function: create_effect + // + // Description: Creates an effect engine of the specified implementation uuid and + // returns an effect control interface on this engine. The function will allocate the + // resources for an instance of the requested effect engine and return + // a handle on the effect control interface. + // + // Input: + // uuid: pointer to the effect uuid. + // sessionId: audio session to which this effect instance will be attached. All effects + // created with the same session ID are connected in series and process the same signal + // stream. Knowing that two effects are part of the same effect chain can help the + // library implement some kind of optimizations. + // ioId: identifies the output or input stream this effect is directed to at audio HAL. + // For future use especially with tunneled HW accelerated effects + // + // Input/Output: + // pHandle: address where to return the effect interface handle. + // + // Output: + // returned value: 0 successful operation. + // -ENODEV library failed to initialize + // -EINVAL invalid pEffectUuid or pHandle + // -ENOENT no effect with this uuid found + // *pHandle: updated with the effect interface handle. + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*create_effect)(const effect_uuid_t *uuid, + int32_t sessionId, + int32_t ioId, + effect_handle_t *pHandle); + + //////////////////////////////////////////////////////////////////////////////// + // + // Function: release_effect + // + // Description: Releases the effect engine whose handle is given as argument. + // All resources allocated to this particular instance of the effect are + // released. + // + // Input: + // handle: handle on the effect interface to be released. + // + // Output: + // returned value: 0 successful operation. + // -ENODEV library failed to initialize + // -EINVAL invalid interface handle + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*release_effect)(effect_handle_t handle); + + //////////////////////////////////////////////////////////////////////////////// + // + // Function: get_descriptor + // + // Description: Returns the descriptor of the effect engine which implementation UUID is + // given as argument. + // + // Input/Output: + // uuid: pointer to the effect uuid. + // pDescriptor: address where to return the effect descriptor. + // + // Output: + // returned value: 0 successful operation. + // -ENODEV library failed to initialize + // -EINVAL invalid pDescriptor or uuid + // *pDescriptor: updated with the effect descriptor. + // + //////////////////////////////////////////////////////////////////////////////// + int32_t (*get_descriptor)(const effect_uuid_t *uuid, + effect_descriptor_t *pDescriptor); +} audio_effect_library_t; + +// Name of the hal_module_info +#define AUDIO_EFFECT_LIBRARY_INFO_SYM AELI + +// Name of the hal_module_info as a string +#define AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR "AELI" + +__END_DECLS + +#endif // ANDROID_AUDIO_EFFECT_H diff -Nru bluez-4.101/android/hardware/audio.h bluez-5.23/android/hardware/audio.h --- bluez-4.101/android/hardware/audio.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/audio.h 2013-12-27 17:16:56.000000000 +0000 @@ -0,0 +1,564 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_AUDIO_HAL_INTERFACE_H +#define ANDROID_AUDIO_HAL_INTERFACE_H + +#include +#include +#include +#include + +#include +#include +#include + +__BEGIN_DECLS + +/** + * The id of this module + */ +#define AUDIO_HARDWARE_MODULE_ID "audio" + +/** + * Name of the audio devices to open + */ +#define AUDIO_HARDWARE_INTERFACE "audio_hw_if" + + +/* Use version 0.1 to be compatible with first generation of audio hw module with version_major + * hardcoded to 1. No audio module API change. + */ +#define AUDIO_MODULE_API_VERSION_0_1 HARDWARE_MODULE_API_VERSION(0, 1) +#define AUDIO_MODULE_API_VERSION_CURRENT AUDIO_MODULE_API_VERSION_0_1 + +/* First generation of audio devices had version hardcoded to 0. all devices with versions < 1.0 + * will be considered of first generation API. + */ +#define AUDIO_DEVICE_API_VERSION_0_0 HARDWARE_DEVICE_API_VERSION(0, 0) +#define AUDIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0) +#define AUDIO_DEVICE_API_VERSION_2_0 HARDWARE_DEVICE_API_VERSION(2, 0) +#define AUDIO_DEVICE_API_VERSION_CURRENT AUDIO_DEVICE_API_VERSION_2_0 + +/** + * List of known audio HAL modules. This is the base name of the audio HAL + * library composed of the "audio." prefix, one of the base names below and + * a suffix specific to the device. + * e.g: audio.primary.goldfish.so or audio.a2dp.default.so + */ + +#define AUDIO_HARDWARE_MODULE_ID_PRIMARY "primary" +#define AUDIO_HARDWARE_MODULE_ID_A2DP "a2dp" +#define AUDIO_HARDWARE_MODULE_ID_USB "usb" +#define AUDIO_HARDWARE_MODULE_ID_REMOTE_SUBMIX "r_submix" +#define AUDIO_HARDWARE_MODULE_ID_CODEC_OFFLOAD "codec_offload" + +/**************************************/ + +/** + * standard audio parameters that the HAL may need to handle + */ + +/** + * audio device parameters + */ + +/* BT SCO Noise Reduction + Echo Cancellation parameters */ +#define AUDIO_PARAMETER_KEY_BT_NREC "bt_headset_nrec" +#define AUDIO_PARAMETER_VALUE_ON "on" +#define AUDIO_PARAMETER_VALUE_OFF "off" + +/* TTY mode selection */ +#define AUDIO_PARAMETER_KEY_TTY_MODE "tty_mode" +#define AUDIO_PARAMETER_VALUE_TTY_OFF "tty_off" +#define AUDIO_PARAMETER_VALUE_TTY_VCO "tty_vco" +#define AUDIO_PARAMETER_VALUE_TTY_HCO "tty_hco" +#define AUDIO_PARAMETER_VALUE_TTY_FULL "tty_full" + +/* A2DP sink address set by framework */ +#define AUDIO_PARAMETER_A2DP_SINK_ADDRESS "a2dp_sink_address" + +/* Screen state */ +#define AUDIO_PARAMETER_KEY_SCREEN_STATE "screen_state" + +/** + * audio stream parameters + */ + +#define AUDIO_PARAMETER_STREAM_ROUTING "routing" // audio_devices_t +#define AUDIO_PARAMETER_STREAM_FORMAT "format" // audio_format_t +#define AUDIO_PARAMETER_STREAM_CHANNELS "channels" // audio_channel_mask_t +#define AUDIO_PARAMETER_STREAM_FRAME_COUNT "frame_count" // size_t +#define AUDIO_PARAMETER_STREAM_INPUT_SOURCE "input_source" // audio_source_t +#define AUDIO_PARAMETER_STREAM_SAMPLING_RATE "sampling_rate" // uint32_t + +/* Query supported formats. The response is a '|' separated list of strings from + * audio_format_t enum e.g: "sup_formats=AUDIO_FORMAT_PCM_16_BIT" */ +#define AUDIO_PARAMETER_STREAM_SUP_FORMATS "sup_formats" +/* Query supported channel masks. The response is a '|' separated list of strings from + * audio_channel_mask_t enum e.g: "sup_channels=AUDIO_CHANNEL_OUT_STEREO|AUDIO_CHANNEL_OUT_MONO" */ +#define AUDIO_PARAMETER_STREAM_SUP_CHANNELS "sup_channels" +/* Query supported sampling rates. The response is a '|' separated list of integer values e.g: + * "sup_sampling_rates=44100|48000" */ +#define AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES "sup_sampling_rates" + +/** + * audio codec parameters + */ + +#define AUDIO_OFFLOAD_CODEC_PARAMS "music_offload_codec_param" +#define AUDIO_OFFLOAD_CODEC_BIT_PER_SAMPLE "music_offload_bit_per_sample" +#define AUDIO_OFFLOAD_CODEC_BIT_RATE "music_offload_bit_rate" +#define AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE "music_offload_avg_bit_rate" +#define AUDIO_OFFLOAD_CODEC_ID "music_offload_codec_id" +#define AUDIO_OFFLOAD_CODEC_BLOCK_ALIGN "music_offload_block_align" +#define AUDIO_OFFLOAD_CODEC_SAMPLE_RATE "music_offload_sample_rate" +#define AUDIO_OFFLOAD_CODEC_ENCODE_OPTION "music_offload_encode_option" +#define AUDIO_OFFLOAD_CODEC_NUM_CHANNEL "music_offload_num_channels" +#define AUDIO_OFFLOAD_CODEC_DOWN_SAMPLING "music_offload_down_sampling" +#define AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES "delay_samples" +#define AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES "padding_samples" + +/**************************************/ + +/* common audio stream configuration parameters + * You should memset() the entire structure to zero before use to + * ensure forward compatibility + */ +struct audio_config { + uint32_t sample_rate; + audio_channel_mask_t channel_mask; + audio_format_t format; + audio_offload_info_t offload_info; +}; +typedef struct audio_config audio_config_t; + +/* common audio stream parameters and operations */ +struct audio_stream { + + /** + * Return the sampling rate in Hz - eg. 44100. + */ + uint32_t (*get_sample_rate)(const struct audio_stream *stream); + + /* currently unused - use set_parameters with key + * AUDIO_PARAMETER_STREAM_SAMPLING_RATE + */ + int (*set_sample_rate)(struct audio_stream *stream, uint32_t rate); + + /** + * Return size of input/output buffer in bytes for this stream - eg. 4800. + * It should be a multiple of the frame size. See also get_input_buffer_size. + */ + size_t (*get_buffer_size)(const struct audio_stream *stream); + + /** + * Return the channel mask - + * e.g. AUDIO_CHANNEL_OUT_STEREO or AUDIO_CHANNEL_IN_STEREO + */ + audio_channel_mask_t (*get_channels)(const struct audio_stream *stream); + + /** + * Return the audio format - e.g. AUDIO_FORMAT_PCM_16_BIT + */ + audio_format_t (*get_format)(const struct audio_stream *stream); + + /* currently unused - use set_parameters with key + * AUDIO_PARAMETER_STREAM_FORMAT + */ + int (*set_format)(struct audio_stream *stream, audio_format_t format); + + /** + * Put the audio hardware input/output into standby mode. + * Driver should exit from standby mode at the next I/O operation. + * Returns 0 on success and <0 on failure. + */ + int (*standby)(struct audio_stream *stream); + + /** dump the state of the audio input/output device */ + int (*dump)(const struct audio_stream *stream, int fd); + + /** Return the set of device(s) which this stream is connected to */ + audio_devices_t (*get_device)(const struct audio_stream *stream); + + /** + * Currently unused - set_device() corresponds to set_parameters() with key + * AUDIO_PARAMETER_STREAM_ROUTING for both input and output. + * AUDIO_PARAMETER_STREAM_INPUT_SOURCE is an additional information used by + * input streams only. + */ + int (*set_device)(struct audio_stream *stream, audio_devices_t device); + + /** + * set/get audio stream parameters. The function accepts a list of + * parameter key value pairs in the form: key1=value1;key2=value2;... + * + * Some keys are reserved for standard parameters (See AudioParameter class) + * + * If the implementation does not accept a parameter change while + * the output is active but the parameter is acceptable otherwise, it must + * return -ENOSYS. + * + * The audio flinger will put the stream in standby and then change the + * parameter value. + */ + int (*set_parameters)(struct audio_stream *stream, const char *kv_pairs); + + /* + * Returns a pointer to a heap allocated string. The caller is responsible + * for freeing the memory for it using free(). + */ + char * (*get_parameters)(const struct audio_stream *stream, + const char *keys); + int (*add_audio_effect)(const struct audio_stream *stream, + effect_handle_t effect); + int (*remove_audio_effect)(const struct audio_stream *stream, + effect_handle_t effect); +}; +typedef struct audio_stream audio_stream_t; + +/* type of asynchronous write callback events. Mutually exclusive */ +typedef enum { + STREAM_CBK_EVENT_WRITE_READY, /* non blocking write completed */ + STREAM_CBK_EVENT_DRAIN_READY /* drain completed */ +} stream_callback_event_t; + +typedef int (*stream_callback_t)(stream_callback_event_t event, void *param, void *cookie); + +/* type of drain requested to audio_stream_out->drain(). Mutually exclusive */ +typedef enum { + AUDIO_DRAIN_ALL, /* drain() returns when all data has been played */ + AUDIO_DRAIN_EARLY_NOTIFY /* drain() returns a short time before all data + from the current track has been played to + give time for gapless track switch */ +} audio_drain_type_t; + +/** + * audio_stream_out is the abstraction interface for the audio output hardware. + * + * It provides information about various properties of the audio output + * hardware driver. + */ + +struct audio_stream_out { + struct audio_stream common; + + /** + * Return the audio hardware driver estimated latency in milliseconds. + */ + uint32_t (*get_latency)(const struct audio_stream_out *stream); + + /** + * Use this method in situations where audio mixing is done in the + * hardware. This method serves as a direct interface with hardware, + * allowing you to directly set the volume as apposed to via the framework. + * This method might produce multiple PCM outputs or hardware accelerated + * codecs, such as MP3 or AAC. + */ + int (*set_volume)(struct audio_stream_out *stream, float left, float right); + + /** + * Write audio buffer to driver. Returns number of bytes written, or a + * negative status_t. If at least one frame was written successfully prior to the error, + * it is suggested that the driver return that successful (short) byte count + * and then return an error in the subsequent call. + * + * If set_callback() has previously been called to enable non-blocking mode + * the write() is not allowed to block. It must write only the number of + * bytes that currently fit in the driver/hardware buffer and then return + * this byte count. If this is less than the requested write size the + * callback function must be called when more space is available in the + * driver/hardware buffer. + */ + ssize_t (*write)(struct audio_stream_out *stream, const void* buffer, + size_t bytes); + + /* return the number of audio frames written by the audio dsp to DAC since + * the output has exited standby + */ + int (*get_render_position)(const struct audio_stream_out *stream, + uint32_t *dsp_frames); + + /** + * get the local time at which the next write to the audio driver will be presented. + * The units are microseconds, where the epoch is decided by the local audio HAL. + */ + int (*get_next_write_timestamp)(const struct audio_stream_out *stream, + int64_t *timestamp); + + /** + * set the callback function for notifying completion of non-blocking + * write and drain. + * Calling this function implies that all future write() and drain() + * must be non-blocking and use the callback to signal completion. + */ + int (*set_callback)(struct audio_stream_out *stream, + stream_callback_t callback, void *cookie); + + /** + * Notifies to the audio driver to stop playback however the queued buffers are + * retained by the hardware. Useful for implementing pause/resume. Empty implementation + * if not supported however should be implemented for hardware with non-trivial + * latency. In the pause state audio hardware could still be using power. User may + * consider calling suspend after a timeout. + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*pause)(struct audio_stream_out* stream); + + /** + * Notifies to the audio driver to resume playback following a pause. + * Returns error if called without matching pause. + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*resume)(struct audio_stream_out* stream); + + /** + * Requests notification when data buffered by the driver/hardware has + * been played. If set_callback() has previously been called to enable + * non-blocking mode, the drain() must not block, instead it should return + * quickly and completion of the drain is notified through the callback. + * If set_callback() has not been called, the drain() must block until + * completion. + * If type==AUDIO_DRAIN_ALL, the drain completes when all previously written + * data has been played. + * If type==AUDIO_DRAIN_EARLY_NOTIFY, the drain completes shortly before all + * data for the current track has played to allow time for the framework + * to perform a gapless track switch. + * + * Drain must return immediately on stop() and flush() call + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*drain)(struct audio_stream_out* stream, audio_drain_type_t type ); + + /** + * Notifies to the audio driver to flush the queued data. Stream must already + * be paused before calling flush(). + * + * Implementation of this function is mandatory for offloaded playback. + */ + int (*flush)(struct audio_stream_out* stream); + + /** + * Return a recent count of the number of audio frames presented to an external observer. + * This excludes frames which have been written but are still in the pipeline. + * The count is not reset to zero when output enters standby. + * Also returns the value of CLOCK_MONOTONIC as of this presentation count. + * The returned count is expected to be 'recent', + * but does not need to be the most recent possible value. + * However, the associated time should correspond to whatever count is returned. + * Example: assume that N+M frames have been presented, where M is a 'small' number. + * Then it is permissible to return N instead of N+M, + * and the timestamp should correspond to N rather than N+M. + * The terms 'recent' and 'small' are not defined. + * They reflect the quality of the implementation. + * + * 3.0 and higher only. + */ + int (*get_presentation_position)(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp); + +}; +typedef struct audio_stream_out audio_stream_out_t; + +struct audio_stream_in { + struct audio_stream common; + + /** set the input gain for the audio driver. This method is for + * for future use */ + int (*set_gain)(struct audio_stream_in *stream, float gain); + + /** Read audio buffer in from audio driver. Returns number of bytes read, or a + * negative status_t. If at least one frame was read prior to the error, + * read should return that byte count and then return an error in the subsequent call. + */ + ssize_t (*read)(struct audio_stream_in *stream, void* buffer, + size_t bytes); + + /** + * Return the amount of input frames lost in the audio driver since the + * last call of this function. + * Audio driver is expected to reset the value to 0 and restart counting + * upon returning the current value by this function call. + * Such loss typically occurs when the user space process is blocked + * longer than the capacity of audio driver buffers. + * + * Unit: the number of input audio frames + */ + uint32_t (*get_input_frames_lost)(struct audio_stream_in *stream); +}; +typedef struct audio_stream_in audio_stream_in_t; + +/** + * return the frame size (number of bytes per sample). + */ +static inline size_t audio_stream_frame_size(const struct audio_stream *s) +{ + size_t chan_samp_sz; + audio_format_t format = s->get_format(s); + + if (audio_is_linear_pcm(format)) { + chan_samp_sz = audio_bytes_per_sample(format); + return popcount(s->get_channels(s)) * chan_samp_sz; + } + + return sizeof(int8_t); +} + + +/**********************************************************************/ + +/** + * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM + * and the fields of this data structure must begin with hw_module_t + * followed by module specific information. + */ +struct audio_module { + struct hw_module_t common; +}; + +struct audio_hw_device { + struct hw_device_t common; + + /** + * used by audio flinger to enumerate what devices are supported by + * each audio_hw_device implementation. + * + * Return value is a bitmask of 1 or more values of audio_devices_t + * + * NOTE: audio HAL implementations starting with + * AUDIO_DEVICE_API_VERSION_2_0 do not implement this function. + * All supported devices should be listed in audio_policy.conf + * file and the audio policy manager must choose the appropriate + * audio module based on information in this file. + */ + uint32_t (*get_supported_devices)(const struct audio_hw_device *dev); + + /** + * check to see if the audio hardware interface has been initialized. + * returns 0 on success, -ENODEV on failure. + */ + int (*init_check)(const struct audio_hw_device *dev); + + /** set the audio volume of a voice call. Range is between 0.0 and 1.0 */ + int (*set_voice_volume)(struct audio_hw_device *dev, float volume); + + /** + * set the audio volume for all audio activities other than voice call. + * Range between 0.0 and 1.0. If any value other than 0 is returned, + * the software mixer will emulate this capability. + */ + int (*set_master_volume)(struct audio_hw_device *dev, float volume); + + /** + * Get the current master volume value for the HAL, if the HAL supports + * master volume control. AudioFlinger will query this value from the + * primary audio HAL when the service starts and use the value for setting + * the initial master volume across all HALs. HALs which do not support + * this method may leave it set to NULL. + */ + int (*get_master_volume)(struct audio_hw_device *dev, float *volume); + + /** + * set_mode is called when the audio mode changes. AUDIO_MODE_NORMAL mode + * is for standard audio playback, AUDIO_MODE_RINGTONE when a ringtone is + * playing, and AUDIO_MODE_IN_CALL when a call is in progress. + */ + int (*set_mode)(struct audio_hw_device *dev, audio_mode_t mode); + + /* mic mute */ + int (*set_mic_mute)(struct audio_hw_device *dev, bool state); + int (*get_mic_mute)(const struct audio_hw_device *dev, bool *state); + + /* set/get global audio parameters */ + int (*set_parameters)(struct audio_hw_device *dev, const char *kv_pairs); + + /* + * Returns a pointer to a heap allocated string. The caller is responsible + * for freeing the memory for it using free(). + */ + char * (*get_parameters)(const struct audio_hw_device *dev, + const char *keys); + + /* Returns audio input buffer size according to parameters passed or + * 0 if one of the parameters is not supported. + * See also get_buffer_size which is for a particular stream. + */ + size_t (*get_input_buffer_size)(const struct audio_hw_device *dev, + const struct audio_config *config); + + /** This method creates and opens the audio hardware output stream */ + int (*open_output_stream)(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out); + + void (*close_output_stream)(struct audio_hw_device *dev, + struct audio_stream_out* stream_out); + + /** This method creates and opens the audio hardware input stream */ + int (*open_input_stream)(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in); + + void (*close_input_stream)(struct audio_hw_device *dev, + struct audio_stream_in *stream_in); + + /** This method dumps the state of the audio hardware */ + int (*dump)(const struct audio_hw_device *dev, int fd); + + /** + * set the audio mute status for all audio activities. If any value other + * than 0 is returned, the software mixer will emulate this capability. + */ + int (*set_master_mute)(struct audio_hw_device *dev, bool mute); + + /** + * Get the current master mute status for the HAL, if the HAL supports + * master mute control. AudioFlinger will query this value from the primary + * audio HAL when the service starts and use the value for setting the + * initial master mute across all HALs. HALs which do not support this + * method may leave it set to NULL. + */ + int (*get_master_mute)(struct audio_hw_device *dev, bool *mute); +}; +typedef struct audio_hw_device audio_hw_device_t; + +/** convenience API for opening and closing a supported device */ + +static inline int audio_hw_device_open(const struct hw_module_t* module, + struct audio_hw_device** device) +{ + return module->methods->open(module, AUDIO_HARDWARE_INTERFACE, + (struct hw_device_t**)device); +} + +static inline int audio_hw_device_close(struct audio_hw_device* device) +{ + return device->common.close(&device->common); +} + + +__END_DECLS + +#endif // ANDROID_AUDIO_INTERFACE_H diff -Nru bluez-4.101/android/hardware/bluetooth.h bluez-5.23/android/hardware/bluetooth.h --- bluez-4.101/android/hardware/bluetooth.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bluetooth.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BLUETOOTH_H +#define ANDROID_INCLUDE_BLUETOOTH_H + +#include +#include +#include + +#include + +__BEGIN_DECLS + +/** + * The Bluetooth Hardware Module ID + */ + +#define BT_HARDWARE_MODULE_ID "bluetooth" +#define BT_STACK_MODULE_ID "bluetooth" +#define BT_STACK_TEST_MODULE_ID "bluetooth_test" + + +/* Bluetooth profile interface IDs */ + +#define BT_PROFILE_HANDSFREE_ID "handsfree" +#define BT_PROFILE_ADVANCED_AUDIO_ID "a2dp" +#define BT_PROFILE_HEALTH_ID "health" +#define BT_PROFILE_SOCKETS_ID "socket" +#define BT_PROFILE_HIDHOST_ID "hidhost" +#define BT_PROFILE_PAN_ID "pan" + +#define BT_PROFILE_GATT_ID "gatt" +#define BT_PROFILE_AV_RC_ID "avrcp" + +/** Bluetooth Address */ +typedef struct { + uint8_t address[6]; +} __attribute__((packed))bt_bdaddr_t; + +/** Bluetooth Device Name */ +typedef struct { + uint8_t name[249]; +} __attribute__((packed))bt_bdname_t; + +/** Bluetooth Adapter Visibility Modes*/ +typedef enum { + BT_SCAN_MODE_NONE, + BT_SCAN_MODE_CONNECTABLE, + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE +} bt_scan_mode_t; + +/** Bluetooth Adapter State */ +typedef enum { + BT_STATE_OFF, + BT_STATE_ON +} bt_state_t; + +/** Bluetooth Error Status */ +/** We need to build on this */ + +typedef enum { + BT_STATUS_SUCCESS, + BT_STATUS_FAIL, + BT_STATUS_NOT_READY, + BT_STATUS_NOMEM, + BT_STATUS_BUSY, + BT_STATUS_DONE, /* request already completed */ + BT_STATUS_UNSUPPORTED, + BT_STATUS_PARM_INVALID, + BT_STATUS_UNHANDLED, + BT_STATUS_AUTH_FAILURE, + BT_STATUS_RMT_DEV_DOWN + +} bt_status_t; + +/** Bluetooth PinKey Code */ +typedef struct { + uint8_t pin[16]; +} __attribute__((packed))bt_pin_code_t; + +/** Bluetooth Adapter Discovery state */ +typedef enum { + BT_DISCOVERY_STOPPED, + BT_DISCOVERY_STARTED +} bt_discovery_state_t; + +/** Bluetooth ACL connection state */ +typedef enum { + BT_ACL_STATE_CONNECTED, + BT_ACL_STATE_DISCONNECTED +} bt_acl_state_t; + +/** Bluetooth 128-bit UUID */ +typedef struct { + uint8_t uu[16]; +} bt_uuid_t; + +/** Bluetooth SDP service record */ +typedef struct +{ + bt_uuid_t uuid; + uint16_t channel; + char name[256]; // what's the maximum length +} bt_service_record_t; + + +/** Bluetooth Remote Version info */ +typedef struct +{ + int version; + int sub_ver; + int manufacturer; +} bt_remote_version_t; + +/* Bluetooth Adapter and Remote Device property types */ +typedef enum { + /* Properties common to both adapter and remote device */ + /** + * Description - Bluetooth Device Name + * Access mode - Adapter name can be GET/SET. Remote device can be GET + * Data type - bt_bdname_t + */ + BT_PROPERTY_BDNAME = 0x1, + /** + * Description - Bluetooth Device Address + * Access mode - Only GET. + * Data type - bt_bdaddr_t + */ + BT_PROPERTY_BDADDR, + /** + * Description - Bluetooth Service 128-bit UUIDs + * Access mode - Only GET. + * Data type - Array of bt_uuid_t (Array size inferred from property length). + */ + BT_PROPERTY_UUIDS, + /** + * Description - Bluetooth Class of Device as found in Assigned Numbers + * Access mode - Only GET. + * Data type - uint32_t. + */ + BT_PROPERTY_CLASS_OF_DEVICE, + /** + * Description - Device Type - BREDR, BLE or DUAL Mode + * Access mode - Only GET. + * Data type - bt_device_type_t + */ + BT_PROPERTY_TYPE_OF_DEVICE, + /** + * Description - Bluetooth Service Record + * Access mode - Only GET. + * Data type - bt_service_record_t + */ + BT_PROPERTY_SERVICE_RECORD, + + /* Properties unique to adapter */ + /** + * Description - Bluetooth Adapter scan mode + * Access mode - GET and SET + * Data type - bt_scan_mode_t. + */ + BT_PROPERTY_ADAPTER_SCAN_MODE, + /** + * Description - List of bonded devices + * Access mode - Only GET. + * Data type - Array of bt_bdaddr_t of the bonded remote devices + * (Array size inferred from property length). + */ + BT_PROPERTY_ADAPTER_BONDED_DEVICES, + /** + * Description - Bluetooth Adapter Discovery timeout (in seconds) + * Access mode - GET and SET + * Data type - uint32_t + */ + BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + + /* Properties unique to remote device */ + /** + * Description - User defined friendly name of the remote device + * Access mode - GET and SET + * Data type - bt_bdname_t. + */ + BT_PROPERTY_REMOTE_FRIENDLY_NAME, + /** + * Description - RSSI value of the inquired remote device + * Access mode - Only GET. + * Data type - int32_t. + */ + BT_PROPERTY_REMOTE_RSSI, + /** + * Description - Remote version info + * Access mode - SET/GET. + * Data type - bt_remote_version_t. + */ + + BT_PROPERTY_REMOTE_VERSION_INFO, + + BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF, +} bt_property_type_t; + +/** Bluetooth Adapter Property data structure */ +typedef struct +{ + bt_property_type_t type; + int len; + void *val; +} bt_property_t; + +/** Bluetooth Device Type */ +typedef enum { + BT_DEVICE_DEVTYPE_BREDR = 0x1, + BT_DEVICE_DEVTYPE_BLE, + BT_DEVICE_DEVTYPE_DUAL +} bt_device_type_t; +/** Bluetooth Bond state */ +typedef enum { + BT_BOND_STATE_NONE, + BT_BOND_STATE_BONDING, + BT_BOND_STATE_BONDED +} bt_bond_state_t; + +/** Bluetooth SSP Bonding Variant */ +typedef enum { + BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + BT_SSP_VARIANT_PASSKEY_ENTRY, + BT_SSP_VARIANT_CONSENT, + BT_SSP_VARIANT_PASSKEY_NOTIFICATION +} bt_ssp_variant_t; + +#define BT_MAX_NUM_UUIDS 32 + +/** Bluetooth Interface callbacks */ + +/** Bluetooth Enable/Disable Callback. */ +typedef void (*adapter_state_changed_callback)(bt_state_t state); + +/** GET/SET Adapter Properties callback */ +/* TODO: For the GET/SET property APIs/callbacks, we may need a session + * identifier to associate the call with the callback. This would be needed + * whenever more than one simultaneous instance of the same adapter_type + * is get/set. + * + * If this is going to be handled in the Java framework, then we do not need + * to manage sessions here. + */ +typedef void (*adapter_properties_callback)(bt_status_t status, + int num_properties, + bt_property_t *properties); + +/** GET/SET Remote Device Properties callback */ +/** TODO: For remote device properties, do not see a need to get/set + * multiple properties - num_properties shall be 1 + */ +typedef void (*remote_device_properties_callback)(bt_status_t status, + bt_bdaddr_t *bd_addr, + int num_properties, + bt_property_t *properties); + +/** New device discovered callback */ +/** If EIR data is not present, then BD_NAME and RSSI shall be NULL and -1 + * respectively */ +typedef void (*device_found_callback)(int num_properties, + bt_property_t *properties); + +/** Discovery state changed callback */ +typedef void (*discovery_state_changed_callback)(bt_discovery_state_t state); + +/** Bluetooth Legacy PinKey Request callback */ +typedef void (*pin_request_callback)(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod); + +/** Bluetooth SSP Request callback - Just Works & Numeric Comparison*/ +/** pass_key - Shall be 0 for BT_SSP_PAIRING_VARIANT_CONSENT & + * BT_SSP_PAIRING_PASSKEY_ENTRY */ +/* TODO: Passkey request callback shall not be needed for devices with display + * capability. We still need support this in the stack for completeness */ +typedef void (*ssp_request_callback)(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, + uint32_t cod, + bt_ssp_variant_t pairing_variant, + uint32_t pass_key); + +/** Bluetooth Bond state changed callback */ +/* Invoked in response to create_bond, cancel_bond or remove_bond */ +typedef void (*bond_state_changed_callback)(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, + bt_bond_state_t state); + +/** Bluetooth ACL connection state changed callback */ +typedef void (*acl_state_changed_callback)(bt_status_t status, bt_bdaddr_t *remote_bd_addr, + bt_acl_state_t state); + +typedef enum { + ASSOCIATE_JVM, + DISASSOCIATE_JVM +} bt_cb_thread_evt; + +/** Thread Associate/Disassociate JVM Callback */ +/* Callback that is invoked by the callback thread to allow upper layer to attach/detach to/from + * the JVM */ +typedef void (*callback_thread_event)(bt_cb_thread_evt evt); + +/** Bluetooth Test Mode Callback */ +/* Receive any HCI event from controller. Must be in DUT Mode for this callback to be received */ +typedef void (*dut_mode_recv_callback)(uint16_t opcode, uint8_t *buf, uint8_t len); + +/* LE Test mode callbacks +* This callback shall be invoked whenever the le_tx_test, le_rx_test or le_test_end is invoked +* The num_packets is valid only for le_test_end command */ +typedef void (*le_test_mode_callback)(bt_status_t status, uint16_t num_packets); +/** TODO: Add callbacks for Link Up/Down and other generic + * notifications/callbacks */ + +/** Bluetooth DM callback structure. */ +typedef struct { + /** set to sizeof(bt_callbacks_t) */ + size_t size; + adapter_state_changed_callback adapter_state_changed_cb; + adapter_properties_callback adapter_properties_cb; + remote_device_properties_callback remote_device_properties_cb; + device_found_callback device_found_cb; + discovery_state_changed_callback discovery_state_changed_cb; + pin_request_callback pin_request_cb; + ssp_request_callback ssp_request_cb; + bond_state_changed_callback bond_state_changed_cb; + acl_state_changed_callback acl_state_changed_cb; + callback_thread_event thread_evt_cb; + dut_mode_recv_callback dut_mode_recv_cb; + le_test_mode_callback le_test_mode_cb; +} bt_callbacks_t; + +/** NOTE: By default, no profiles are initialized at the time of init/enable. + * Whenever the application invokes the 'init' API of a profile, then one of + * the following shall occur: + * + * 1.) If Bluetooth is not enabled, then the Bluetooth core shall mark the + * profile as enabled. Subsequently, when the application invokes the + * Bluetooth 'enable', as part of the enable sequence the profile that were + * marked shall be enabled by calling appropriate stack APIs. The + * 'adapter_properties_cb' shall return the list of UUIDs of the + * enabled profiles. + * + * 2.) If Bluetooth is enabled, then the Bluetooth core shall invoke the stack + * profile API to initialize the profile and trigger a + * 'adapter_properties_cb' with the current list of UUIDs including the + * newly added profile's UUID. + * + * The reverse shall occur whenever the profile 'cleanup' APIs are invoked + */ + +/** Represents the standard Bluetooth DM interface. */ +typedef struct { + /** set to sizeof(bt_interface_t) */ + size_t size; + /** + * Opens the interface and provides the callback routines + * to the implemenation of this interface. + */ + int (*init)(bt_callbacks_t* callbacks ); + + /** Enable Bluetooth. */ + int (*enable)(void); + + /** Disable Bluetooth. */ + int (*disable)(void); + + /** Closes the interface. */ + void (*cleanup)(void); + + /** Get all Bluetooth Adapter properties at init */ + int (*get_adapter_properties)(void); + + /** Get Bluetooth Adapter property of 'type' */ + int (*get_adapter_property)(bt_property_type_t type); + + /** Set Bluetooth Adapter property of 'type' */ + /* Based on the type, val shall be one of + * bt_bdaddr_t or bt_bdname_t or bt_scanmode_t etc + */ + int (*set_adapter_property)(const bt_property_t *property); + + /** Get all Remote Device properties */ + int (*get_remote_device_properties)(bt_bdaddr_t *remote_addr); + + /** Get Remote Device property of 'type' */ + int (*get_remote_device_property)(bt_bdaddr_t *remote_addr, + bt_property_type_t type); + + /** Set Remote Device property of 'type' */ + int (*set_remote_device_property)(bt_bdaddr_t *remote_addr, + const bt_property_t *property); + + /** Get Remote Device's service record for the given UUID */ + int (*get_remote_service_record)(bt_bdaddr_t *remote_addr, + bt_uuid_t *uuid); + + /** Start SDP to get remote services */ + int (*get_remote_services)(bt_bdaddr_t *remote_addr); + + /** Start Discovery */ + int (*start_discovery)(void); + + /** Cancel Discovery */ + int (*cancel_discovery)(void); + + /** Create Bluetooth Bonding */ + int (*create_bond)(const bt_bdaddr_t *bd_addr); + + /** Remove Bond */ + int (*remove_bond)(const bt_bdaddr_t *bd_addr); + + /** Cancel Bond */ + int (*cancel_bond)(const bt_bdaddr_t *bd_addr); + + /** BT Legacy PinKey Reply */ + /** If accept==FALSE, then pin_len and pin_code shall be 0x0 */ + int (*pin_reply)(const bt_bdaddr_t *bd_addr, uint8_t accept, + uint8_t pin_len, bt_pin_code_t *pin_code); + + /** BT SSP Reply - Just Works, Numeric Comparison and Passkey + * passkey shall be zero for BT_SSP_VARIANT_PASSKEY_COMPARISON & + * BT_SSP_VARIANT_CONSENT + * For BT_SSP_VARIANT_PASSKEY_ENTRY, if accept==FALSE, then passkey + * shall be zero */ + int (*ssp_reply)(const bt_bdaddr_t *bd_addr, bt_ssp_variant_t variant, + uint8_t accept, uint32_t passkey); + + /** Get Bluetooth profile interface */ + const void* (*get_profile_interface) (const char *profile_id); + + /** Bluetooth Test Mode APIs - Bluetooth must be enabled for these APIs */ + /* Configure DUT Mode - Use this mode to enter/exit DUT mode */ + int (*dut_mode_configure)(uint8_t enable); + + /* Send any test HCI (vendor-specific) command to the controller. Must be in DUT Mode */ + int (*dut_mode_send)(uint16_t opcode, uint8_t *buf, uint8_t len); + /** BLE Test Mode APIs */ + /* opcode MUST be one of: LE_Receiver_Test, LE_Transmitter_Test, LE_Test_End */ + int (*le_test_mode)(uint16_t opcode, uint8_t *buf, uint8_t len); + + /* enable or disable bluetooth HCI snoop log */ + int (*config_hci_snoop_log)(uint8_t enable); +} bt_interface_t; + +/** TODO: Need to add APIs for Service Discovery, Service authorization and + * connection management. Also need to add APIs for configuring + * properties of remote bonded devices such as name, UUID etc. */ + +typedef struct { + struct hw_device_t common; + const bt_interface_t* (*get_bluetooth_interface)(); +} bluetooth_device_t; + +typedef bluetooth_device_t bluetooth_module_t; +__END_DECLS + +#endif /* ANDROID_INCLUDE_BLUETOOTH_H */ diff -Nru bluez-4.101/android/hardware/bt_av.h bluez-5.23/android/hardware/bt_av.h --- bluez-4.101/android/hardware/bt_av.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_av.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_AV_H +#define ANDROID_INCLUDE_BT_AV_H + +__BEGIN_DECLS + +/* Bluetooth AV connection states */ +typedef enum { + BTAV_CONNECTION_STATE_DISCONNECTED = 0, + BTAV_CONNECTION_STATE_CONNECTING, + BTAV_CONNECTION_STATE_CONNECTED, + BTAV_CONNECTION_STATE_DISCONNECTING +} btav_connection_state_t; + +/* Bluetooth AV datapath states */ +typedef enum { + BTAV_AUDIO_STATE_REMOTE_SUSPEND = 0, + BTAV_AUDIO_STATE_STOPPED, + BTAV_AUDIO_STATE_STARTED, +} btav_audio_state_t; + + +/** Callback for connection state change. + * state will have one of the values from btav_connection_state_t + */ +typedef void (* btav_connection_state_callback)(btav_connection_state_t state, + bt_bdaddr_t *bd_addr); + +/** Callback for audiopath state change. + * state will have one of the values from btav_audio_state_t + */ +typedef void (* btav_audio_state_callback)(btav_audio_state_t state, + bt_bdaddr_t *bd_addr); + +/** BT-AV callback structure. */ +typedef struct { + /** set to sizeof(btav_callbacks_t) */ + size_t size; + btav_connection_state_callback connection_state_cb; + btav_audio_state_callback audio_state_cb; +} btav_callbacks_t; + +/** + * NOTE: + * + * 1. AVRCP 1.0 shall be supported initially. AVRCP passthrough commands + * shall be handled internally via uinput + * + * 2. A2DP data path shall be handled via a socket pipe between the AudioFlinger + * android_audio_hw library and the Bluetooth stack. + * + */ +/** Represents the standard BT-AV interface. */ +typedef struct { + + /** set to sizeof(btav_interface_t) */ + size_t size; + /** + * Register the BtAv callbacks + */ + bt_status_t (*init)( btav_callbacks_t* callbacks ); + + /** connect to headset */ + bt_status_t (*connect)( bt_bdaddr_t *bd_addr ); + + /** dis-connect from headset */ + bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); + + /** Closes the interface. */ + void (*cleanup)( void ); +} btav_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_AV_H */ diff -Nru bluez-4.101/android/hardware/bt_gatt_client.h bluez-5.23/android/hardware/bt_gatt_client.h --- bluez-4.101/android/hardware/bt_gatt_client.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_gatt_client.h 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_INCLUDE_BT_GATT_CLIENT_H +#define ANDROID_INCLUDE_BT_GATT_CLIENT_H + +#include +#include "bt_gatt_types.h" + +__BEGIN_DECLS + +/** + * Buffer sizes for maximum attribute length and maximum read/write + * operation buffer size. + */ +#define BTGATT_MAX_ATTR_LEN 600 + +/** Buffer type for unformatted reads/writes */ +typedef struct +{ + uint8_t value[BTGATT_MAX_ATTR_LEN]; + uint16_t len; +} btgatt_unformatted_value_t; + +/** Parameters for GATT read operations */ +typedef struct +{ + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_gatt_id_t descr_id; + btgatt_unformatted_value_t value; + uint16_t value_type; + uint8_t status; +} btgatt_read_params_t; + +/** Parameters for GATT write operations */ +typedef struct +{ + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + btgatt_gatt_id_t descr_id; + uint8_t status; +} btgatt_write_params_t; + +/** Attribute change notification parameters */ +typedef struct +{ + uint8_t value[BTGATT_MAX_ATTR_LEN]; + bt_bdaddr_t bda; + btgatt_srvc_id_t srvc_id; + btgatt_gatt_id_t char_id; + uint16_t len; + uint8_t is_notify; +} btgatt_notify_params_t; + +typedef struct +{ + bt_bdaddr_t *bda1; + bt_uuid_t *uuid1; + uint16_t u1; + uint16_t u2; + uint16_t u3; + uint16_t u4; + uint16_t u5; +} btgatt_test_params_t; + +/** BT-GATT Client callback structure. */ + +/** Callback invoked in response to register_client */ +typedef void (*register_client_callback)(int status, int client_if, + bt_uuid_t *app_uuid); + +/** Callback for scan results */ +typedef void (*scan_result_callback)(bt_bdaddr_t* bda, int rssi, uint8_t* adv_data); + +/** GATT open callback invoked in response to open */ +typedef void (*connect_callback)(int conn_id, int status, int client_if, bt_bdaddr_t* bda); + +/** Callback invoked in response to close */ +typedef void (*disconnect_callback)(int conn_id, int status, + int client_if, bt_bdaddr_t* bda); + +/** + * Invoked in response to search_service when the GATT service search + * has been completed. + */ +typedef void (*search_complete_callback)(int conn_id, int status); + +/** Reports GATT services on a remote device */ +typedef void (*search_result_callback)( int conn_id, btgatt_srvc_id_t *srvc_id); + +/** GATT characteristic enumeration result callback */ +typedef void (*get_characteristic_callback)(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int char_prop); + +/** GATT descriptor enumeration result callback */ +typedef void (*get_descriptor_callback)(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id); + +/** GATT included service enumeration result callback */ +typedef void (*get_included_service_callback)(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_srvc_id_t *incl_srvc_id); + +/** Callback invoked in response to [de]register_for_notification */ +typedef void (*register_for_notification_callback)(int conn_id, + int registered, int status, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id); + +/** + * Remote device notification callback, invoked when a remote device sends + * a notification or indication that a client has registered for. + */ +typedef void (*notify_callback)(int conn_id, btgatt_notify_params_t *p_data); + +/** Reports result of a GATT read operation */ +typedef void (*read_characteristic_callback)(int conn_id, int status, + btgatt_read_params_t *p_data); + +/** GATT write characteristic operation callback */ +typedef void (*write_characteristic_callback)(int conn_id, int status, + btgatt_write_params_t *p_data); + +/** GATT execute prepared write callback */ +typedef void (*execute_write_callback)(int conn_id, int status); + +/** Callback invoked in response to read_descriptor */ +typedef void (*read_descriptor_callback)(int conn_id, int status, + btgatt_read_params_t *p_data); + +/** Callback invoked in response to write_descriptor */ +typedef void (*write_descriptor_callback)(int conn_id, int status, + btgatt_write_params_t *p_data); + +/** Callback triggered in response to read_remote_rssi */ +typedef void (*read_remote_rssi_callback)(int client_if, bt_bdaddr_t* bda, + int rssi, int status); + +/** + * Callback indicationg the status of a listen() operation + */ +typedef void (*listen_callback)(int status, int server_if); + +typedef struct { + register_client_callback register_client_cb; + scan_result_callback scan_result_cb; + connect_callback open_cb; + disconnect_callback close_cb; + search_complete_callback search_complete_cb; + search_result_callback search_result_cb; + get_characteristic_callback get_characteristic_cb; + get_descriptor_callback get_descriptor_cb; + get_included_service_callback get_included_service_cb; + register_for_notification_callback register_for_notification_cb; + notify_callback notify_cb; + read_characteristic_callback read_characteristic_cb; + write_characteristic_callback write_characteristic_cb; + read_descriptor_callback read_descriptor_cb; + write_descriptor_callback write_descriptor_cb; + execute_write_callback execute_write_cb; + read_remote_rssi_callback read_remote_rssi_cb; + listen_callback listen_cb; +} btgatt_client_callbacks_t; + +/** Represents the standard BT-GATT client interface. */ + +typedef struct { + /** Registers a GATT client application with the stack */ + bt_status_t (*register_client)( bt_uuid_t *uuid ); + + /** Unregister a client application from the stack */ + bt_status_t (*unregister_client)(int client_if ); + + /** Start or stop LE device scanning */ + bt_status_t (*scan)( int client_if, bool start ); + + /** Create a connection to a remote LE or dual-mode device */ + bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr, + bool is_direct ); + + /** Disconnect a remote device or cancel a pending connection */ + bt_status_t (*disconnect)( int client_if, const bt_bdaddr_t *bd_addr, + int conn_id); + + /** Start or stop advertisements to listen for incoming connections */ + bt_status_t (*listen)(int client_if, bool start); + + /** Clear the attribute cache for a given device */ + bt_status_t (*refresh)( int client_if, const bt_bdaddr_t *bd_addr ); + + /** + * Enumerate all GATT services on a connected device. + * Optionally, the results can be filtered for a given UUID. + */ + bt_status_t (*search_service)(int conn_id, bt_uuid_t *filter_uuid ); + + /** + * Enumerate included services for a given service. + * Set start_incl_srvc_id to NULL to get the first included service. + */ + bt_status_t (*get_included_service)( int conn_id, btgatt_srvc_id_t *srvc_id, + btgatt_srvc_id_t *start_incl_srvc_id); + + /** + * Enumerate characteristics for a given service. + * Set start_char_id to NULL to get the first characteristic. + */ + bt_status_t (*get_characteristic)( int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *start_char_id); + + /** + * Enumerate descriptors for a given characteristic. + * Set start_descr_id to NULL to get the first descriptor. + */ + bt_status_t (*get_descriptor)( int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *start_descr_id); + + /** Read a characteristic on a remote device */ + bt_status_t (*read_characteristic)( int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int auth_req ); + + /** Write a remote characteristic */ + bt_status_t (*write_characteristic)(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int write_type, int len, int auth_req, + char* p_value); + + /** Read the descriptor for a given characteristic */ + bt_status_t (*read_descriptor)(int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, int auth_req); + + /** Write a remote descriptor for a given characteristic */ + bt_status_t (*write_descriptor)( int conn_id, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + btgatt_gatt_id_t *descr_id, int write_type, int len, + int auth_req, char* p_value); + + /** Execute a prepared write operation */ + bt_status_t (*execute_write)(int conn_id, int execute); + + /** + * Register to receive notifications or indications for a given + * characteristic + */ + bt_status_t (*register_for_notification)( int client_if, + const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id); + + /** Deregister a previous request for notifications/indications */ + bt_status_t (*deregister_for_notification)( int client_if, + const bt_bdaddr_t *bd_addr, btgatt_srvc_id_t *srvc_id, + btgatt_gatt_id_t *char_id); + + /** Request RSSI for a given remote device */ + bt_status_t (*read_remote_rssi)( int client_if, const bt_bdaddr_t *bd_addr); + + /** Determine the type of the remote device (LE, BR/EDR, Dual-mode) */ + int (*get_device_type)( const bt_bdaddr_t *bd_addr ); + + /** Set the advertising data or scan response data */ + bt_status_t (*set_adv_data)(int server_if, bool set_scan_rsp, bool include_name, + bool include_txpower, int min_interval, int max_interval, int appearance, + uint16_t manufacturer_len, char* manufacturer_data, + uint16_t service_data_len, char* service_data, + uint16_t service_uuid_len, char* service_uuid); + + /** Test mode interface */ + bt_status_t (*test_command)( int command, btgatt_test_params_t* params); +} btgatt_client_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */ diff -Nru bluez-4.101/android/hardware/bt_gatt.h bluez-5.23/android/hardware/bt_gatt.h --- bluez-4.101/android/hardware/bt_gatt.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_gatt.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_INCLUDE_BT_GATT_H +#define ANDROID_INCLUDE_BT_GATT_H + +#include +#include "bt_gatt_client.h" +#include "bt_gatt_server.h" + +__BEGIN_DECLS + +/** BT-GATT callbacks */ +typedef struct { + /** Set to sizeof(btgatt_callbacks_t) */ + size_t size; + + /** GATT Client callbacks */ + const btgatt_client_callbacks_t* client; + + /** GATT Server callbacks */ + const btgatt_server_callbacks_t* server; +} btgatt_callbacks_t; + +/** Represents the standard Bluetooth GATT interface. */ +typedef struct { + /** Set to sizeof(btgatt_interface_t) */ + size_t size; + + /** + * Initializes the interface and provides callback routines + */ + bt_status_t (*init)( const btgatt_callbacks_t* callbacks ); + + /** Closes the interface */ + void (*cleanup)( void ); + + /** Pointer to the GATT client interface methods.*/ + const btgatt_client_interface_t* client; + + /** Pointer to the GATT server interface methods.*/ + const btgatt_server_interface_t* server; +} btgatt_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_GATT_H */ diff -Nru bluez-4.101/android/hardware/bt_gatt_server.h bluez-5.23/android/hardware/bt_gatt_server.h --- bluez-4.101/android/hardware/bt_gatt_server.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_gatt_server.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_INCLUDE_BT_GATT_SERVER_H +#define ANDROID_INCLUDE_BT_GATT_SERVER_H + +#include + +#include "bt_gatt_types.h" + +__BEGIN_DECLS + +/** GATT value type used in response to remote read requests */ +typedef struct +{ + uint8_t value[BTGATT_MAX_ATTR_LEN]; + uint16_t handle; + uint16_t offset; + uint16_t len; + uint8_t auth_req; +} btgatt_value_t; + +/** GATT remote read request response type */ +typedef union +{ + btgatt_value_t attr_value; + uint16_t handle; +} btgatt_response_t; + +/** BT-GATT Server callback structure. */ + +/** Callback invoked in response to register_server */ +typedef void (*register_server_callback)(int status, int server_if, + bt_uuid_t *app_uuid); + +/** Callback indicating that a remote device has connected or been disconnected */ +typedef void (*connection_callback)(int conn_id, int server_if, int connected, + bt_bdaddr_t *bda); + +/** Callback invoked in response to create_service */ +typedef void (*service_added_callback)(int status, int server_if, + btgatt_srvc_id_t *srvc_id, int srvc_handle); + +/** Callback indicating that an included service has been added to a service */ +typedef void (*included_service_added_callback)(int status, int server_if, + int srvc_handle, int incl_srvc_handle); + +/** Callback invoked when a characteristic has been added to a service */ +typedef void (*characteristic_added_callback)(int status, int server_if, + bt_uuid_t *uuid, int srvc_handle, int char_handle); + +/** Callback invoked when a descriptor has been added to a characteristic */ +typedef void (*descriptor_added_callback)(int status, int server_if, + bt_uuid_t *uuid, int srvc_handle, int descr_handle); + +/** Callback invoked in response to start_service */ +typedef void (*service_started_callback)(int status, int server_if, + int srvc_handle); + +/** Callback invoked in response to stop_service */ +typedef void (*service_stopped_callback)(int status, int server_if, + int srvc_handle); + +/** Callback triggered when a service has been deleted */ +typedef void (*service_deleted_callback)(int status, int server_if, + int srvc_handle); + +/** + * Callback invoked when a remote device has requested to read a characteristic + * or descriptor. The application must respond by calling send_response + */ +typedef void (*request_read_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int offset, bool is_long); + +/** + * Callback invoked when a remote device has requested to write to a + * characteristic or descriptor. + */ +typedef void (*request_write_callback)(int conn_id, int trans_id, bt_bdaddr_t *bda, + int attr_handle, int offset, int length, + bool need_rsp, bool is_prep, uint8_t* value); + +/** Callback invoked when a previously prepared write is to be executed */ +typedef void (*request_exec_write_callback)(int conn_id, int trans_id, + bt_bdaddr_t *bda, int exec_write); + +/** + * Callback triggered in response to send_response if the remote device + * sends a confirmation. + */ +typedef void (*response_confirmation_callback)(int status, int handle); + +typedef struct { + register_server_callback register_server_cb; + connection_callback connection_cb; + service_added_callback service_added_cb; + included_service_added_callback included_service_added_cb; + characteristic_added_callback characteristic_added_cb; + descriptor_added_callback descriptor_added_cb; + service_started_callback service_started_cb; + service_stopped_callback service_stopped_cb; + service_deleted_callback service_deleted_cb; + request_read_callback request_read_cb; + request_write_callback request_write_cb; + request_exec_write_callback request_exec_write_cb; + response_confirmation_callback response_confirmation_cb; +} btgatt_server_callbacks_t; + +/** Represents the standard BT-GATT server interface. */ +typedef struct { + /** Registers a GATT server application with the stack */ + bt_status_t (*register_server)( bt_uuid_t *uuid ); + + /** Unregister a server application from the stack */ + bt_status_t (*unregister_server)(int server_if ); + + /** Create a connection to a remote peripheral */ + bt_status_t (*connect)(int server_if, const bt_bdaddr_t *bd_addr, bool is_direct ); + + /** Disconnect an established connection or cancel a pending one */ + bt_status_t (*disconnect)(int server_if, const bt_bdaddr_t *bd_addr, + int conn_id ); + + /** Create a new service */ + bt_status_t (*add_service)( int server_if, btgatt_srvc_id_t *srvc_id, int num_handles); + + /** Assign an included service to it's parent service */ + bt_status_t (*add_included_service)( int server_if, int service_handle, int included_handle); + + /** Add a characteristic to a service */ + bt_status_t (*add_characteristic)( int server_if, + int service_handle, bt_uuid_t *uuid, + int properties, int permissions); + + /** Add a descriptor to a given service */ + bt_status_t (*add_descriptor)(int server_if, int service_handle, + bt_uuid_t *uuid, int permissions); + + /** Starts a local service */ + bt_status_t (*start_service)(int server_if, int service_handle, + int transport); + + /** Stops a local service */ + bt_status_t (*stop_service)(int server_if, int service_handle); + + /** Delete a local service */ + bt_status_t (*delete_service)(int server_if, int service_handle); + + /** Send value indication to a remote device */ + bt_status_t (*send_indication)(int server_if, int attribute_handle, + int conn_id, int len, int confirm, + char* p_value); + + /** Send a response to a read/write operation */ + bt_status_t (*send_response)(int conn_id, int trans_id, + int status, btgatt_response_t *response); +} btgatt_server_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_GATT_CLIENT_H */ diff -Nru bluez-4.101/android/hardware/bt_gatt_types.h bluez-5.23/android/hardware/bt_gatt_types.h --- bluez-4.101/android/hardware/bt_gatt_types.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_gatt_types.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_INCLUDE_BT_GATT_TYPES_H +#define ANDROID_INCLUDE_BT_GATT_TYPES_H + +#include +#include + +__BEGIN_DECLS + +/** + * GATT Service types + */ +#define BTGATT_SERVICE_TYPE_PRIMARY 0 +#define BTGATT_SERVICE_TYPE_SECONDARY 1 + +/** GATT ID adding instance id tracking to the UUID */ +typedef struct +{ + bt_uuid_t uuid; + uint8_t inst_id; +} btgatt_gatt_id_t; + +/** GATT Service ID also identifies the service type (primary/secondary) */ +typedef struct +{ + btgatt_gatt_id_t id; + uint8_t is_primary; +} btgatt_srvc_id_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_GATT_TYPES_H */ diff -Nru bluez-4.101/android/hardware/bt_hf.h bluez-5.23/android/hardware/bt_hf.h --- bluez-4.101/android/hardware/bt_hf.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_hf.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_HF_H +#define ANDROID_INCLUDE_BT_HF_H + +__BEGIN_DECLS + +/* AT response code - OK/Error */ +typedef enum { + BTHF_AT_RESPONSE_ERROR = 0, + BTHF_AT_RESPONSE_OK +} bthf_at_response_t; + +typedef enum { + BTHF_CONNECTION_STATE_DISCONNECTED = 0, + BTHF_CONNECTION_STATE_CONNECTING, + BTHF_CONNECTION_STATE_CONNECTED, + BTHF_CONNECTION_STATE_SLC_CONNECTED, + BTHF_CONNECTION_STATE_DISCONNECTING +} bthf_connection_state_t; + +typedef enum { + BTHF_AUDIO_STATE_DISCONNECTED = 0, + BTHF_AUDIO_STATE_CONNECTING, + BTHF_AUDIO_STATE_CONNECTED, + BTHF_AUDIO_STATE_DISCONNECTING +} bthf_audio_state_t; + +typedef enum { + BTHF_VR_STATE_STOPPED = 0, + BTHF_VR_STATE_STARTED +} bthf_vr_state_t; + +typedef enum { + BTHF_VOLUME_TYPE_SPK = 0, + BTHF_VOLUME_TYPE_MIC +} bthf_volume_type_t; + +/* Noise Reduction and Echo Cancellation */ +typedef enum +{ + BTHF_NREC_STOP, + BTHF_NREC_START +} bthf_nrec_t; + +/* CHLD - Call held handling */ +typedef enum +{ + BTHF_CHLD_TYPE_RELEASEHELD, // Terminate all held or set UDUB("busy") to a waiting call + BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD, // Terminate all active calls and accepts a waiting/held call + BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD, // Hold all active calls and accepts a waiting/held call + BTHF_CHLD_TYPE_ADDHELDTOCONF, // Add all held calls to a conference +} bthf_chld_type_t; + +/** Callback for connection state change. + * state will have one of the values from BtHfConnectionState + */ +typedef void (* bthf_connection_state_callback)(bthf_connection_state_t state, bt_bdaddr_t *bd_addr); + +/** Callback for audio connection state change. + * state will have one of the values from BtHfAudioState + */ +typedef void (* bthf_audio_state_callback)(bthf_audio_state_t state, bt_bdaddr_t *bd_addr); + +/** Callback for VR connection state change. + * state will have one of the values from BtHfVRState + */ +typedef void (* bthf_vr_cmd_callback)(bthf_vr_state_t state); + +/** Callback for answer incoming call (ATA) + */ +typedef void (* bthf_answer_call_cmd_callback)(); + +/** Callback for disconnect call (AT+CHUP) + */ +typedef void (* bthf_hangup_call_cmd_callback)(); + +/** Callback for disconnect call (AT+CHUP) + * type will denote Speaker/Mic gain (BtHfVolumeControl). + */ +typedef void (* bthf_volume_cmd_callback)(bthf_volume_type_t type, int volume); + +/** Callback for dialing an outgoing call + * If number is NULL, redial + */ +typedef void (* bthf_dial_call_cmd_callback)(char *number); + +/** Callback for sending DTMF tones + * tone contains the dtmf character to be sent + */ +typedef void (* bthf_dtmf_cmd_callback)(char tone); + +/** Callback for enabling/disabling noise reduction/echo cancellation + * value will be 1 to enable, 0 to disable + */ +typedef void (* bthf_nrec_cmd_callback)(bthf_nrec_t nrec); + +/** Callback for call hold handling (AT+CHLD) + * value will contain the call hold command (0, 1, 2, 3) + */ +typedef void (* bthf_chld_cmd_callback)(bthf_chld_type_t chld); + +/** Callback for CNUM (subscriber number) + */ +typedef void (* bthf_cnum_cmd_callback)(); + +/** Callback for indicators (CIND) + */ +typedef void (* bthf_cind_cmd_callback)(); + +/** Callback for operator selection (COPS) + */ +typedef void (* bthf_cops_cmd_callback)(); + +/** Callback for call list (AT+CLCC) + */ +typedef void (* bthf_clcc_cmd_callback) (); + +/** Callback for unknown AT command recd from HF + * at_string will contain the unparsed AT string + */ +typedef void (* bthf_unknown_at_cmd_callback)(char *at_string); + +/** Callback for keypressed (HSP) event. + */ +typedef void (* bthf_key_pressed_cmd_callback)(); + +/** BT-HF callback structure. */ +typedef struct { + /** set to sizeof(BtHfCallbacks) */ + size_t size; + bthf_connection_state_callback connection_state_cb; + bthf_audio_state_callback audio_state_cb; + bthf_vr_cmd_callback vr_cmd_cb; + bthf_answer_call_cmd_callback answer_call_cmd_cb; + bthf_hangup_call_cmd_callback hangup_call_cmd_cb; + bthf_volume_cmd_callback volume_cmd_cb; + bthf_dial_call_cmd_callback dial_call_cmd_cb; + bthf_dtmf_cmd_callback dtmf_cmd_cb; + bthf_nrec_cmd_callback nrec_cmd_cb; + bthf_chld_cmd_callback chld_cmd_cb; + bthf_cnum_cmd_callback cnum_cmd_cb; + bthf_cind_cmd_callback cind_cmd_cb; + bthf_cops_cmd_callback cops_cmd_cb; + bthf_clcc_cmd_callback clcc_cmd_cb; + bthf_unknown_at_cmd_callback unknown_at_cmd_cb; + bthf_key_pressed_cmd_callback key_pressed_cmd_cb; +} bthf_callbacks_t; + +/** Network Status */ +typedef enum +{ + BTHF_NETWORK_STATE_NOT_AVAILABLE = 0, + BTHF_NETWORK_STATE_AVAILABLE +} bthf_network_state_t; + +/** Service type */ +typedef enum +{ + BTHF_SERVICE_TYPE_HOME = 0, + BTHF_SERVICE_TYPE_ROAMING +} bthf_service_type_t; + +typedef enum { + BTHF_CALL_STATE_ACTIVE = 0, + BTHF_CALL_STATE_HELD, + BTHF_CALL_STATE_DIALING, + BTHF_CALL_STATE_ALERTING, + BTHF_CALL_STATE_INCOMING, + BTHF_CALL_STATE_WAITING, + BTHF_CALL_STATE_IDLE +} bthf_call_state_t; + +typedef enum { + BTHF_CALL_DIRECTION_OUTGOING = 0, + BTHF_CALL_DIRECTION_INCOMING +} bthf_call_direction_t; + +typedef enum { + BTHF_CALL_TYPE_VOICE = 0, + BTHF_CALL_TYPE_DATA, + BTHF_CALL_TYPE_FAX +} bthf_call_mode_t; + +typedef enum { + BTHF_CALL_MPTY_TYPE_SINGLE = 0, + BTHF_CALL_MPTY_TYPE_MULTI +} bthf_call_mpty_type_t; + +typedef enum { + BTHF_CALL_ADDRTYPE_UNKNOWN = 0x81, + BTHF_CALL_ADDRTYPE_INTERNATIONAL = 0x91 +} bthf_call_addrtype_t; +/** Represents the standard BT-HF interface. */ +typedef struct { + + /** set to sizeof(BtHfInterface) */ + size_t size; + /** + * Register the BtHf callbacks + */ + bt_status_t (*init)( bthf_callbacks_t* callbacks ); + + /** connect to headset */ + bt_status_t (*connect)( bt_bdaddr_t *bd_addr ); + + /** dis-connect from headset */ + bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); + + /** create an audio connection */ + bt_status_t (*connect_audio)( bt_bdaddr_t *bd_addr ); + + /** close the audio connection */ + bt_status_t (*disconnect_audio)( bt_bdaddr_t *bd_addr ); + + /** start voice recognition */ + bt_status_t (*start_voice_recognition)(); + + /** stop voice recognition */ + bt_status_t (*stop_voice_recognition)(); + + /** volume control */ + bt_status_t (*volume_control) (bthf_volume_type_t type, int volume); + + /** Combined device status change notification */ + bt_status_t (*device_status_notification)(bthf_network_state_t ntk_state, bthf_service_type_t svc_type, int signal, + int batt_chg); + + /** Response for COPS command */ + bt_status_t (*cops_response)(const char *cops); + + /** Response for CIND command */ + bt_status_t (*cind_response)(int svc, int num_active, int num_held, bthf_call_state_t call_setup_state, + int signal, int roam, int batt_chg); + + /** Pre-formatted AT response, typically in response to unknown AT cmd */ + bt_status_t (*formatted_at_response)(const char *rsp); + + /** ok/error response + * ERROR (0) + * OK (1) + */ + bt_status_t (*at_response) (bthf_at_response_t response_code, int error_code); + + /** response for CLCC command + * Can be iteratively called for each call index + * Call index of 0 will be treated as NULL termination (Completes response) + */ + bt_status_t (*clcc_response) (int index, bthf_call_direction_t dir, + bthf_call_state_t state, bthf_call_mode_t mode, + bthf_call_mpty_type_t mpty, const char *number, + bthf_call_addrtype_t type); + + /** notify of a call state change + * Each update notifies + * 1. Number of active/held/ringing calls + * 2. call_state: This denotes the state change that triggered this msg + * This will take one of the values from BtHfCallState + * 3. number & type: valid only for incoming & waiting call + */ + bt_status_t (*phone_state_change) (int num_active, int num_held, bthf_call_state_t call_setup_state, + const char *number, bthf_call_addrtype_t type); + + /** Closes the interface. */ + void (*cleanup)( void ); +} bthf_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_HF_H */ diff -Nru bluez-4.101/android/hardware/bt_hh.h bluez-5.23/android/hardware/bt_hh.h --- bluez-4.101/android/hardware/bt_hh.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_hh.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_HH_H +#define ANDROID_INCLUDE_BT_HH_H + +#include + +__BEGIN_DECLS + +#define BTHH_MAX_DSC_LEN 884 + +/* HH connection states */ +typedef enum +{ + BTHH_CONN_STATE_CONNECTED = 0, + BTHH_CONN_STATE_CONNECTING, + BTHH_CONN_STATE_DISCONNECTED, + BTHH_CONN_STATE_DISCONNECTING, + BTHH_CONN_STATE_FAILED_MOUSE_FROM_HOST, + BTHH_CONN_STATE_FAILED_KBD_FROM_HOST, + BTHH_CONN_STATE_FAILED_TOO_MANY_DEVICES, + BTHH_CONN_STATE_FAILED_NO_BTHID_DRIVER, + BTHH_CONN_STATE_FAILED_GENERIC, + BTHH_CONN_STATE_UNKNOWN +} bthh_connection_state_t; + +typedef enum +{ + BTHH_OK = 0, + BTHH_HS_HID_NOT_READY, /* handshake error : device not ready */ + BTHH_HS_INVALID_RPT_ID, /* handshake error : invalid report ID */ + BTHH_HS_TRANS_NOT_SPT, /* handshake error : transaction not spt */ + BTHH_HS_INVALID_PARAM, /* handshake error : invalid paremter */ + BTHH_HS_ERROR, /* handshake error : unspecified HS error */ + BTHH_ERR, /* general BTA HH error */ + BTHH_ERR_SDP, /* SDP error */ + BTHH_ERR_PROTO, /* SET_Protocol error, + only used in BTA_HH_OPEN_EVT callback */ + BTHH_ERR_DB_FULL, /* device database full error, used */ + BTHH_ERR_TOD_UNSPT, /* type of device not supported */ + BTHH_ERR_NO_RES, /* out of system resources */ + BTHH_ERR_AUTH_FAILED, /* authentication fail */ + BTHH_ERR_HDL +}bthh_status_t; + +/* Protocol modes */ +typedef enum { + BTHH_REPORT_MODE = 0x00, + BTHH_BOOT_MODE = 0x01, + BTHH_UNSUPPORTED_MODE = 0xff +}bthh_protocol_mode_t; + +/* Report types */ +typedef enum { + BTHH_INPUT_REPORT = 1, + BTHH_OUTPUT_REPORT, + BTHH_FEATURE_REPORT +}bthh_report_type_t; + +typedef struct +{ + int attr_mask; + uint8_t sub_class; + uint8_t app_id; + int vendor_id; + int product_id; + int version; + uint8_t ctry_code; + int dl_len; + uint8_t dsc_list[BTHH_MAX_DSC_LEN]; +} bthh_hid_info_t; + +/** Callback for connection state change. + * state will have one of the values from bthh_connection_state_t + */ +typedef void (* bthh_connection_state_callback)(bt_bdaddr_t *bd_addr, bthh_connection_state_t state); + +/** Callback for vitual unplug api. + * the status of the vitual unplug + */ +typedef void (* bthh_virtual_unplug_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status); + +/** Callback for get hid info + * hid_info will contain attr_mask, sub_class, app_id, vendor_id, product_id, version, ctry_code, len + */ +typedef void (* bthh_hid_info_callback)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info); + +/** Callback for get/set protocal api. + * the protocol mode is one of the value from bthh_protocol_mode_t + */ +typedef void (* bthh_protocol_mode_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status,bthh_protocol_mode_t mode); + +/** Callback for get/set_idle_time api. + */ +typedef void (* bthh_idle_time_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_rate); + + +/** Callback for get report api. + * if staus is ok rpt_data contains the report data + */ +typedef void (* bthh_get_report_callback)(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t* rpt_data, int rpt_size); + + +/** BT-HH callback structure. */ +typedef struct { + /** set to sizeof(BtHfCallbacks) */ + size_t size; + bthh_connection_state_callback connection_state_cb; + bthh_hid_info_callback hid_info_cb; + bthh_protocol_mode_callback protocol_mode_cb; + bthh_idle_time_callback idle_time_cb; + bthh_get_report_callback get_report_cb; + bthh_virtual_unplug_callback virtual_unplug_cb; + +} bthh_callbacks_t; + + + +/** Represents the standard BT-HH interface. */ +typedef struct { + + /** set to sizeof(BtHhInterface) */ + size_t size; + + /** + * Register the BtHh callbacks + */ + bt_status_t (*init)( bthh_callbacks_t* callbacks ); + + /** connect to hid device */ + bt_status_t (*connect)( bt_bdaddr_t *bd_addr); + + /** dis-connect from hid device */ + bt_status_t (*disconnect)( bt_bdaddr_t *bd_addr ); + + /** Virtual UnPlug (VUP) the specified HID device */ + bt_status_t (*virtual_unplug)(bt_bdaddr_t *bd_addr); + + /** Set the HID device descriptor for the specified HID device. */ + bt_status_t (*set_info)(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info ); + + /** Get the HID proto mode. */ + bt_status_t (*get_protocol) (bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode); + + /** Set the HID proto mode. */ + bt_status_t (*set_protocol)(bt_bdaddr_t *bd_addr, bthh_protocol_mode_t protocolMode); + + /** Send a GET_REPORT to HID device. */ + bt_status_t (*get_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, uint8_t reportId, int bufferSize); + + /** Send a SET_REPORT to HID device. */ + bt_status_t (*set_report)(bt_bdaddr_t *bd_addr, bthh_report_type_t reportType, char* report); + + /** Send data to HID device. */ + bt_status_t (*send_data)(bt_bdaddr_t *bd_addr, char* data); + + /** Closes the interface. */ + void (*cleanup)( void ); + +} bthh_interface_t; +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_HH_H */ + + diff -Nru bluez-4.101/android/hardware/bt_hl.h bluez-5.23/android/hardware/bt_hl.h --- bluez-4.101/android/hardware/bt_hl.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_hl.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_HL_H +#define ANDROID_INCLUDE_BT_HL_H + +__BEGIN_DECLS + +/* HL connection states */ + +typedef enum +{ + BTHL_MDEP_ROLE_SOURCE, + BTHL_MDEP_ROLE_SINK +} bthl_mdep_role_t; + +typedef enum { + BTHL_APP_REG_STATE_REG_SUCCESS, + BTHL_APP_REG_STATE_REG_FAILED, + BTHL_APP_REG_STATE_DEREG_SUCCESS, + BTHL_APP_REG_STATE_DEREG_FAILED +} bthl_app_reg_state_t; + +typedef enum +{ + BTHL_CHANNEL_TYPE_RELIABLE, + BTHL_CHANNEL_TYPE_STREAMING, + BTHL_CHANNEL_TYPE_ANY +} bthl_channel_type_t; + + +/* HL connection states */ +typedef enum { + BTHL_CONN_STATE_CONNECTING, + BTHL_CONN_STATE_CONNECTED, + BTHL_CONN_STATE_DISCONNECTING, + BTHL_CONN_STATE_DISCONNECTED, + BTHL_CONN_STATE_DESTROYED +} bthl_channel_state_t; + +typedef struct +{ + bthl_mdep_role_t mdep_role; + int data_type; + bthl_channel_type_t channel_type; + const char *mdep_description; /* MDEP description to be used in the SDP (optional); null terminated */ +} bthl_mdep_cfg_t; + +typedef struct +{ + const char *application_name; + const char *provider_name; /* provider name to be used in the SDP (optional); null terminated */ + const char *srv_name; /* service name to be used in the SDP (optional); null terminated*/ + const char *srv_desp; /* service description to be used in the SDP (optional); null terminated */ + int number_of_mdeps; + bthl_mdep_cfg_t *mdep_cfg; /* Dynamic array */ +} bthl_reg_param_t; + +/** Callback for application registration status. + * state will have one of the values from bthl_app_reg_state_t + */ +typedef void (* bthl_app_reg_state_callback)(int app_id, bthl_app_reg_state_t state); + +/** Callback for channel connection state change. + * state will have one of the values from + * bthl_connection_state_t and fd (file descriptor) + */ +typedef void (* bthl_channel_state_callback)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int channel_id, bthl_channel_state_t state, int fd); + +/** BT-HL callback structure. */ +typedef struct { + /** set to sizeof(bthl_callbacks_t) */ + size_t size; + bthl_app_reg_state_callback app_reg_state_cb; + bthl_channel_state_callback channel_state_cb; +} bthl_callbacks_t; + + +/** Represents the standard BT-HL interface. */ +typedef struct { + + /** set to sizeof(bthl_interface_t) */ + size_t size; + + /** + * Register the Bthl callbacks + */ + bt_status_t (*init)( bthl_callbacks_t* callbacks ); + + /** Register HL application */ + bt_status_t (*register_application) ( bthl_reg_param_t *p_reg_param, int *app_id); + + /** Unregister HL application */ + bt_status_t (*unregister_application) (int app_id); + + /** connect channel */ + bt_status_t (*connect_channel)(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int *channel_id); + + /** destroy channel */ + bt_status_t (*destroy_channel)(int channel_id); + + /** Close the Bthl callback **/ + void (*cleanup)(void); + +} bthl_interface_t; +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_HL_H */ + + diff -Nru bluez-4.101/android/hardware/bt_pan.h bluez-5.23/android/hardware/bt_pan.h --- bluez-4.101/android/hardware/bt_pan.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_pan.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_PAN_H +#define ANDROID_INCLUDE_BT_PAN_H + +__BEGIN_DECLS + +#define BTPAN_ROLE_NONE 0 +#define BTPAN_ROLE_PANNAP 1 +#define BTPAN_ROLE_PANU 2 + +typedef enum { + BTPAN_STATE_CONNECTED = 0, + BTPAN_STATE_CONNECTING = 1, + BTPAN_STATE_DISCONNECTED = 2, + BTPAN_STATE_DISCONNECTING = 3 +} btpan_connection_state_t; + +typedef enum { + BTPAN_STATE_ENABLED = 0, + BTPAN_STATE_DISABLED = 1 +} btpan_control_state_t; + +/** +* Callback for pan connection state +*/ +typedef void (*btpan_connection_state_callback)(btpan_connection_state_t state, bt_status_t error, + const bt_bdaddr_t *bd_addr, int local_role, int remote_role); +typedef void (*btpan_control_state_callback)(btpan_control_state_t state, bt_status_t error, + int local_role, const char* ifname); + +typedef struct { + size_t size; + btpan_control_state_callback control_state_cb; + btpan_connection_state_callback connection_state_cb; +} btpan_callbacks_t; +typedef struct { + /** set to size of this struct*/ + size_t size; + /** + * Initialize the pan interface and register the btpan callbacks + */ + bt_status_t (*init)(const btpan_callbacks_t* callbacks); + /* + * enable the pan service by specified role. The result state of + * enabl will be returned by btpan_control_state_callback. when pan-nap is enabled, + * the state of connecting panu device will be notified by btpan_connection_state_callback + */ + bt_status_t (*enable)(int local_role); + /* + * get current pan local role + */ + int (*get_local_role)(void); + /** + * start bluetooth pan connection to the remote device by specified pan role. The result state will be + * returned by btpan_connection_state_callback + */ + bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, int local_role, int remote_role); + /** + * stop bluetooth pan connection. The result state will be returned by btpan_connection_state_callback + */ + bt_status_t (*disconnect)(const bt_bdaddr_t *bd_addr); + + /** + * Cleanup the pan interface + */ + void (*cleanup)(void); + +} btpan_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_PAN_H */ diff -Nru bluez-4.101/android/hardware/bt_rc.h bluez-5.23/android/hardware/bt_rc.h --- bluez-4.101/android/hardware/bt_rc.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_rc.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_RC_H +#define ANDROID_INCLUDE_BT_RC_H + +__BEGIN_DECLS + +/* Macros */ +#define BTRC_MAX_ATTR_STR_LEN 255 +#define BTRC_UID_SIZE 8 +#define BTRC_MAX_APP_SETTINGS 8 +#define BTRC_MAX_FOLDER_DEPTH 4 +#define BTRC_MAX_APP_ATTR_SIZE 16 +#define BTRC_MAX_ELEM_ATTR_SIZE 7 + +typedef uint8_t btrc_uid_t[BTRC_UID_SIZE]; + +typedef enum { + BTRC_FEAT_NONE = 0x00, /* AVRCP 1.0 */ + BTRC_FEAT_METADATA = 0x01, /* AVRCP 1.3 */ + BTRC_FEAT_ABSOLUTE_VOLUME = 0x02, /* Supports TG role and volume sync */ + BTRC_FEAT_BROWSE = 0x04, /* AVRCP 1.4 and up, with Browsing support */ +} btrc_remote_features_t; + +typedef enum { + BTRC_PLAYSTATE_STOPPED = 0x00, /* Stopped */ + BTRC_PLAYSTATE_PLAYING = 0x01, /* Playing */ + BTRC_PLAYSTATE_PAUSED = 0x02, /* Paused */ + BTRC_PLAYSTATE_FWD_SEEK = 0x03, /* Fwd Seek*/ + BTRC_PLAYSTATE_REV_SEEK = 0x04, /* Rev Seek*/ + BTRC_PLAYSTATE_ERROR = 0xFF, /* Error */ +} btrc_play_status_t; + +typedef enum { + BTRC_EVT_PLAY_STATUS_CHANGED = 0x01, + BTRC_EVT_TRACK_CHANGE = 0x02, + BTRC_EVT_TRACK_REACHED_END = 0x03, + BTRC_EVT_TRACK_REACHED_START = 0x04, + BTRC_EVT_PLAY_POS_CHANGED = 0x05, + BTRC_EVT_APP_SETTINGS_CHANGED = 0x08, +} btrc_event_id_t; + +typedef enum { + BTRC_NOTIFICATION_TYPE_INTERIM = 0, + BTRC_NOTIFICATION_TYPE_CHANGED = 1, +} btrc_notification_type_t; + +typedef enum { + BTRC_PLAYER_ATTR_EQUALIZER = 0x01, + BTRC_PLAYER_ATTR_REPEAT = 0x02, + BTRC_PLAYER_ATTR_SHUFFLE = 0x03, + BTRC_PLAYER_ATTR_SCAN = 0x04, +} btrc_player_attr_t; + +typedef enum { + BTRC_MEDIA_ATTR_TITLE = 0x01, + BTRC_MEDIA_ATTR_ARTIST = 0x02, + BTRC_MEDIA_ATTR_ALBUM = 0x03, + BTRC_MEDIA_ATTR_TRACK_NUM = 0x04, + BTRC_MEDIA_ATTR_NUM_TRACKS = 0x05, + BTRC_MEDIA_ATTR_GENRE = 0x06, + BTRC_MEDIA_ATTR_PLAYING_TIME = 0x07, +} btrc_media_attr_t; + +typedef enum { + BTRC_PLAYER_VAL_OFF_REPEAT = 0x01, + BTRC_PLAYER_VAL_SINGLE_REPEAT = 0x02, + BTRC_PLAYER_VAL_ALL_REPEAT = 0x03, + BTRC_PLAYER_VAL_GROUP_REPEAT = 0x04 +} btrc_player_repeat_val_t; + +typedef enum { + BTRC_PLAYER_VAL_OFF_SHUFFLE = 0x01, + BTRC_PLAYER_VAL_ALL_SHUFFLE = 0x02, + BTRC_PLAYER_VAL_GROUP_SHUFFLE = 0x03 +} btrc_player_shuffle_val_t; + +typedef enum { + BTRC_STS_BAD_CMD = 0x00, /* Invalid command */ + BTRC_STS_BAD_PARAM = 0x01, /* Invalid parameter */ + BTRC_STS_NOT_FOUND = 0x02, /* Specified parameter is wrong or not found */ + BTRC_STS_INTERNAL_ERR = 0x03, /* Internal Error */ + BTRC_STS_NO_ERROR = 0x04 /* Operation Success */ +} btrc_status_t; + +typedef struct { + uint8_t num_attr; + uint8_t attr_ids[BTRC_MAX_APP_SETTINGS]; + uint8_t attr_values[BTRC_MAX_APP_SETTINGS]; +} btrc_player_settings_t; + +typedef union +{ + btrc_play_status_t play_status; + btrc_uid_t track; /* queue position in NowPlaying */ + uint32_t song_pos; + btrc_player_settings_t player_setting; +} btrc_register_notification_t; + +typedef struct { + uint8_t id; /* can be attr_id or value_id */ + uint8_t text[BTRC_MAX_ATTR_STR_LEN]; +} btrc_player_setting_text_t; + +typedef struct { + uint32_t attr_id; + uint8_t text[BTRC_MAX_ATTR_STR_LEN]; +} btrc_element_attr_val_t; + +/** Callback for the controller's supported feautres */ +typedef void (* btrc_remote_features_callback)(bt_bdaddr_t *bd_addr, + btrc_remote_features_t features); + +/** Callback for play status request */ +typedef void (* btrc_get_play_status_callback)(); + +/** Callback for list player application attributes (Shuffle, Repeat,...) */ +typedef void (* btrc_list_player_app_attr_callback)(); + +/** Callback for list player application attributes (Shuffle, Repeat,...) */ +typedef void (* btrc_list_player_app_values_callback)(btrc_player_attr_t attr_id); + +/** Callback for getting the current player application settings value +** num_attr: specifies the number of attribute ids contained in p_attrs +*/ +typedef void (* btrc_get_player_app_value_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs); + +/** Callback for getting the player application settings attributes' text +** num_attr: specifies the number of attribute ids contained in p_attrs +*/ +typedef void (* btrc_get_player_app_attrs_text_callback) (uint8_t num_attr, btrc_player_attr_t *p_attrs); + +/** Callback for getting the player application settings values' text +** num_attr: specifies the number of value ids contained in p_vals +*/ +typedef void (* btrc_get_player_app_values_text_callback) (uint8_t attr_id, uint8_t num_val, uint8_t *p_vals); + +/** Callback for setting the player application settings values */ +typedef void (* btrc_set_player_app_value_callback) (btrc_player_settings_t *p_vals); + +/** Callback to fetch the get element attributes of the current song +** num_attr: specifies the number of attributes requested in p_attrs +*/ +typedef void (* btrc_get_element_attr_callback) (uint8_t num_attr, btrc_media_attr_t *p_attrs); + +/** Callback for register notification (Play state change/track change/...) +** param: Is only valid if event_id is BTRC_EVT_PLAY_POS_CHANGED +*/ +typedef void (* btrc_register_notification_callback) (btrc_event_id_t event_id, uint32_t param); + +/* AVRCP 1.4 Enhancements */ +/** Callback for volume change on CT +** volume: Current volume setting on the CT (0-127) +*/ +typedef void (* btrc_volume_change_callback) (uint8_t volume, uint8_t ctype); + +/** Callback for passthrough commands */ +typedef void (* btrc_passthrough_cmd_callback) (int id, int key_state); + +/** BT-RC callback structure. */ +typedef struct { + /** set to sizeof(BtRcCallbacks) */ + size_t size; + btrc_remote_features_callback remote_features_cb; + btrc_get_play_status_callback get_play_status_cb; + btrc_list_player_app_attr_callback list_player_app_attr_cb; + btrc_list_player_app_values_callback list_player_app_values_cb; + btrc_get_player_app_value_callback get_player_app_value_cb; + btrc_get_player_app_attrs_text_callback get_player_app_attrs_text_cb; + btrc_get_player_app_values_text_callback get_player_app_values_text_cb; + btrc_set_player_app_value_callback set_player_app_value_cb; + btrc_get_element_attr_callback get_element_attr_cb; + btrc_register_notification_callback register_notification_cb; + btrc_volume_change_callback volume_change_cb; + btrc_passthrough_cmd_callback passthrough_cmd_cb; +} btrc_callbacks_t; + +/** Represents the standard BT-RC interface. */ +typedef struct { + + /** set to sizeof(BtRcInterface) */ + size_t size; + /** + * Register the BtRc callbacks + */ + bt_status_t (*init)( btrc_callbacks_t* callbacks ); + + /** Respose to GetPlayStatus request. Contains the current + ** 1. Play status + ** 2. Song duration/length + ** 3. Song position + */ + bt_status_t (*get_play_status_rsp)( btrc_play_status_t play_status, uint32_t song_len, uint32_t song_pos); + + /** Lists the support player application attributes (Shuffle/Repeat/...) + ** num_attr: Specifies the number of attributes contained in the pointer p_attrs + */ + bt_status_t (*list_player_app_attr_rsp)( int num_attr, btrc_player_attr_t *p_attrs); + + /** Lists the support player application attributes (Shuffle Off/On/Group) + ** num_val: Specifies the number of values contained in the pointer p_vals + */ + bt_status_t (*list_player_app_value_rsp)( int num_val, uint8_t *p_vals); + + /** Returns the current application attribute values for each of the specified attr_id */ + bt_status_t (*get_player_app_value_rsp)( btrc_player_settings_t *p_vals); + + /** Returns the application attributes text ("Shuffle"/"Repeat"/...) + ** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs + */ + bt_status_t (*get_player_app_attr_text_rsp)( int num_attr, btrc_player_setting_text_t *p_attrs); + + /** Returns the application attributes text ("Shuffle"/"Repeat"/...) + ** num_attr: Specifies the number of attribute values' text contained in the pointer p_vals + */ + bt_status_t (*get_player_app_value_text_rsp)( int num_val, btrc_player_setting_text_t *p_vals); + + /** Returns the current songs' element attributes text ("Title"/"Album"/"Artist") + ** num_attr: Specifies the number of attributes' text contained in the pointer p_attrs + */ + bt_status_t (*get_element_attr_rsp)( uint8_t num_attr, btrc_element_attr_val_t *p_attrs); + + /** Response to set player attribute request ("Shuffle"/"Repeat") + ** rsp_status: Status of setting the player attributes for the current media player + */ + bt_status_t (*set_player_app_value_rsp)(btrc_status_t rsp_status); + + /* Response to the register notification request (Play state change/track change/...). + ** event_id: Refers to the event_id this notification change corresponds too + ** type: Response type - interim/changed + ** p_params: Based on the event_id, this parameter should be populated + */ + bt_status_t (*register_notification_rsp)(btrc_event_id_t event_id, + btrc_notification_type_t type, + btrc_register_notification_t *p_param); + + /* AVRCP 1.4 enhancements */ + + /**Send current volume setting to remote side. Support limited to SetAbsoluteVolume + ** This can be enhanced to support Relative Volume (AVRCP 1.0). + ** With RelateVolume, we will send VOLUME_UP/VOLUME_DOWN opposed to absolute volume level + ** volume: Should be in the range 0-127. bit7 is reseved and cannot be set + */ + bt_status_t (*set_volume)(uint8_t volume); + + /** Closes the interface. */ + void (*cleanup)( void ); +} btrc_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_RC_H */ diff -Nru bluez-4.101/android/hardware/bt_sock.h bluez-5.23/android/hardware/bt_sock.h --- bluez-4.101/android/hardware/bt_sock.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/bt_sock.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_BT_SOCK_H +#define ANDROID_INCLUDE_BT_SOCK_H + +__BEGIN_DECLS + +#define BTSOCK_FLAG_ENCRYPT 1 +#define BTSOCK_FLAG_AUTH (1 << 1) + +typedef enum { + BTSOCK_RFCOMM = 1, + BTSOCK_SCO = 2, + BTSOCK_L2CAP = 3 +} btsock_type_t; + +/** Represents the standard BT SOCKET interface. */ +typedef struct { + short size; + bt_bdaddr_t bd_addr; + int channel; + int status; +} __attribute__((packed)) sock_connect_signal_t; + +typedef struct { + + /** set to size of this struct*/ + size_t size; + /** + * listen to a rfcomm uuid or channel. It returns the socket fd from which + * btsock_connect_signal can be read out when a remote device connected + */ + bt_status_t (*listen)(btsock_type_t type, const char* service_name, const uint8_t* service_uuid, int channel, int* sock_fd, int flags); + /* + * connect to a rfcomm uuid channel of remote device, It returns the socket fd from which + * the btsock_connect_signal and a new socket fd to be accepted can be read out when connected + */ + bt_status_t (*connect)(const bt_bdaddr_t *bd_addr, btsock_type_t type, const uint8_t* uuid, int channel, int* sock_fd, int flags); + +} btsock_interface_t; + +__END_DECLS + +#endif /* ANDROID_INCLUDE_BT_SOCK_H */ diff -Nru bluez-4.101/android/hardware/hardware.c bluez-5.23/android/hardware/hardware.c --- bluez-4.101/android/hardware/hardware.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/hardware.c 2014-01-21 00:12:58.000000000 +0000 @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#define LOG_TAG "HAL" + +#define LOG_INFO " I" +#define LOG_WARN " W" +#define LOG_ERROR " E" +#define LOG_DEBUG " D" +#define ALOG(pri, tag, fmt, arg...) fprintf(stderr, tag pri": " fmt"\n", ##arg) + +#define info(fmt, arg...) ALOG(LOG_INFO, LOG_TAG, fmt, ##arg) +#define warn(fmt, arg...) ALOG(LOG_WARN, LOG_TAG, fmt, ##arg) +#define error(fmt, arg...) ALOG(LOG_ERROR, LOG_TAG, fmt, ##arg) + +/** + * Load the file defined by the variant and if successful + * return the dlopen handle and the hmi. + * @return 0 = success, !0 = failure. + */ +static int load(const char *id, + const char *path, + const struct hw_module_t **pHmi) +{ + int status; + void *handle; + struct hw_module_t *hmi; + const char *sym = HAL_MODULE_INFO_SYM_AS_STR; + + /* + * load the symbols resolving undefined symbols before + * dlopen returns. Since RTLD_GLOBAL is not or'd in with + * RTLD_NOW the external symbols will not be global + */ + handle = dlopen(path, RTLD_NOW); + if (handle == NULL) { + char const *err_str = dlerror(); + error("load: module=%s\n%s", path, err_str?err_str:"unknown"); + status = -EINVAL; + goto done; + } + + /* Get the address of the struct hal_module_info. */ + hmi = (struct hw_module_t *)dlsym(handle, sym); + if (hmi == NULL) { + error("load: couldn't find symbol %s", sym); + status = -EINVAL; + goto done; + } + + /* Check that the id matches */ + if (strcmp(id, hmi->id) != 0) { + error("load: id=%s != hmi->id=%s", id, hmi->id); + status = -EINVAL; + goto done; + } + + hmi->dso = handle; + + *pHmi = hmi; + + info("loaded HAL id=%s path=%s hmi=%p handle=%p", + id, path, *pHmi, handle); + + return 0; + +done: + hmi = NULL; + if (handle != NULL) { + dlclose(handle); + handle = NULL; + } + + return status; +} + +int hw_get_module_by_class(const char *class_id, const char *inst, + const struct hw_module_t **module) +{ + char path[PATH_MAX]; + char name[PATH_MAX]; + + if (inst) + snprintf(name, PATH_MAX, "%s.%s", class_id, inst); + else + snprintf(name, PATH_MAX, "%s", class_id); + + /* + * Here we rely on the fact that calling dlopen multiple times on + * the same .so will simply increment a refcount (and not load + * a new copy of the library). + * We also assume that dlopen() is thread-safe. + */ + snprintf(path, sizeof(path), "%s/%s.default.so", PLUGINDIR, name); + + return load(class_id, path, module); +} + +int hw_get_module(const char *id, const struct hw_module_t **module) +{ + return hw_get_module_by_class(id, NULL, module); +} diff -Nru bluez-4.101/android/hardware/hardware.h bluez-5.23/android/hardware/hardware.h --- bluez-4.101/android/hardware/hardware.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hardware/hardware.h 2013-11-15 21:37:30.000000000 +0000 @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_INCLUDE_HARDWARE_HARDWARE_H +#define ANDROID_INCLUDE_HARDWARE_HARDWARE_H + +#include +#include + +__BEGIN_DECLS + +/* + * Value for the hw_module_t.tag field + */ + +#define MAKE_TAG_CONSTANT(A,B,C,D) (((A) << 24) | ((B) << 16) | ((C) << 8) | (D)) + +#define HARDWARE_MODULE_TAG MAKE_TAG_CONSTANT('H', 'W', 'M', 'T') +#define HARDWARE_DEVICE_TAG MAKE_TAG_CONSTANT('H', 'W', 'D', 'T') + +#define HARDWARE_MAKE_API_VERSION(maj,min) \ + ((((maj) & 0xff) << 8) | ((min) & 0xff)) + +#define HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) \ + ((((maj) & 0xff) << 24) | (((min) & 0xff) << 16) | ((hdr) & 0xffff)) +#define HARDWARE_API_VERSION_2_MAJ_MIN_MASK 0xffff0000 +#define HARDWARE_API_VERSION_2_HEADER_MASK 0x0000ffff + + +/* + * The current HAL API version. + * + * All module implementations must set the hw_module_t.hal_api_version field + * to this value when declaring the module with HAL_MODULE_INFO_SYM. + * + * Note that previous implementations have always set this field to 0. + * Therefore, libhardware HAL API will always consider versions 0.0 and 1.0 + * to be 100% binary compatible. + * + */ +#define HARDWARE_HAL_API_VERSION HARDWARE_MAKE_API_VERSION(1, 0) + +/* + * Helper macros for module implementors. + * + * The derived modules should provide convenience macros for supported + * versions so that implementations can explicitly specify module/device + * versions at definition time. + * + * Use this macro to set the hw_module_t.module_api_version field. + */ +#define HARDWARE_MODULE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min) +#define HARDWARE_MODULE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) + +/* + * Use this macro to set the hw_device_t.version field + */ +#define HARDWARE_DEVICE_API_VERSION(maj,min) HARDWARE_MAKE_API_VERSION(maj,min) +#define HARDWARE_DEVICE_API_VERSION_2(maj,min,hdr) HARDWARE_MAKE_API_VERSION_2(maj,min,hdr) + +struct hw_module_t; +struct hw_module_methods_t; +struct hw_device_t; + +/** + * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM + * and the fields of this data structure must begin with hw_module_t + * followed by module specific information. + */ +typedef struct hw_module_t { + /** tag must be initialized to HARDWARE_MODULE_TAG */ + uint32_t tag; + + /** + * The API version of the implemented module. The module owner is + * responsible for updating the version when a module interface has + * changed. + * + * The derived modules such as gralloc and audio own and manage this field. + * The module user must interpret the version field to decide whether or + * not to inter-operate with the supplied module implementation. + * For example, SurfaceFlinger is responsible for making sure that + * it knows how to manage different versions of the gralloc-module API, + * and AudioFlinger must know how to do the same for audio-module API. + * + * The module API version should include a major and a minor component. + * For example, version 1.0 could be represented as 0x0100. This format + * implies that versions 0x0100-0x01ff are all API-compatible. + * + * In the future, libhardware will expose a hw_get_module_version() + * (or equivalent) function that will take minimum/maximum supported + * versions as arguments and would be able to reject modules with + * versions outside of the supplied range. + */ + uint16_t module_api_version; +#define version_major module_api_version + /** + * version_major/version_minor defines are supplied here for temporary + * source code compatibility. They will be removed in the next version. + * ALL clients must convert to the new version format. + */ + + /** + * The API version of the HAL module interface. This is meant to + * version the hw_module_t, hw_module_methods_t, and hw_device_t + * structures and definitions. + * + * The HAL interface owns this field. Module users/implementations + * must NOT rely on this value for version information. + * + * Presently, 0 is the only valid value. + */ + uint16_t hal_api_version; +#define version_minor hal_api_version + + /** Identifier of module */ + const char *id; + + /** Name of this module */ + const char *name; + + /** Author/owner/implementor of the module */ + const char *author; + + /** Modules methods */ + struct hw_module_methods_t* methods; + + /** module's dso */ + void* dso; + + /** padding to 128 bytes, reserved for future use */ + uint32_t reserved[32-7]; + +} hw_module_t; + +typedef struct hw_module_methods_t { + /** Open a specific device */ + int (*open)(const struct hw_module_t* module, const char* id, + struct hw_device_t** device); + +} hw_module_methods_t; + +/** + * Every device data structure must begin with hw_device_t + * followed by module specific public methods and attributes. + */ +typedef struct hw_device_t { + /** tag must be initialized to HARDWARE_DEVICE_TAG */ + uint32_t tag; + + /** + * Version of the module-specific device API. This value is used by + * the derived-module user to manage different device implementations. + * + * The module user is responsible for checking the module_api_version + * and device version fields to ensure that the user is capable of + * communicating with the specific module implementation. + * + * One module can support multiple devices with different versions. This + * can be useful when a device interface changes in an incompatible way + * but it is still necessary to support older implementations at the same + * time. One such example is the Camera 2.0 API. + * + * This field is interpreted by the module user and is ignored by the + * HAL interface itself. + */ + uint32_t version; + + /** reference to the module this device belongs to */ + struct hw_module_t* module; + + /** padding reserved for future use */ + uint32_t reserved[12]; + + /** Close this device */ + int (*close)(struct hw_device_t* device); + +} hw_device_t; + +/** + * Name of the hal_module_info + */ +#define HAL_MODULE_INFO_SYM HMI + +/** + * Name of the hal_module_info as a string + */ +#define HAL_MODULE_INFO_SYM_AS_STR "HMI" + +/** + * Get the module info associated with a module by id. + * + * @return: 0 == success, <0 == error and *module == NULL + */ +int hw_get_module(const char *id, const struct hw_module_t **module); + +/** + * Get the module info associated with a module instance by class 'class_id' + * and instance 'inst'. + * + * Some modules types necessitate multiple instances. For example audio supports + * multiple concurrent interfaces and thus 'audio' is the module class + * and 'primary' or 'a2dp' are module interfaces. This implies that the files + * providing these modules would be named audio.primary..so and + * audio.a2dp..so + * + * @return: 0 == success, <0 == error and *module == NULL + */ +int hw_get_module_by_class(const char *class_id, const char *inst, + const struct hw_module_t **module); + +__END_DECLS + +#endif /* ANDROID_INCLUDE_HARDWARE_HARDWARE_H */ diff -Nru bluez-4.101/android/health.c bluez-5.23/android/health.c --- bluez-4.101/android/health.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/health.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,2121 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. + * + * + * This library is free software; you can redistribute it and/or + * 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 +#endif + +#include +#include +#include +#include +#include + +#include "btio/btio.h" +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "lib/uuid.h" +#include "lib/l2cap.h" +#include "src/log.h" +#include "src/shared/util.h" +#include "src/shared/queue.h" +#include "src/uuid-helper.h" +#include "src/sdp-client.h" + +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "utils.h" +#include "bluetooth.h" +#include "health.h" +#include "mcap-lib.h" + +#define SVC_HINT_HEALTH 0x00 +#define HDP_VERSION 0x0101 +#define DATA_EXCHANGE_SPEC_11073 0x01 + +#define CHANNEL_TYPE_ANY 0x00 +#define CHANNEL_TYPE_RELIABLE 0x01 +#define CHANNEL_TYPE_STREAM 0x02 + +#define MDEP_ECHO 0x00 +#define MDEP_INITIAL 0x01 +#define MDEP_FINAL 0x7F + +static bdaddr_t adapter_addr; +static struct ipc *hal_ipc = NULL; +static struct queue *apps = NULL; +static struct mcap_instance *mcap = NULL; +static uint32_t record_id = 0; +static uint32_t record_state = 0; + +struct mdep_cfg { + uint8_t role; + uint16_t data_type; + uint8_t channel_type; + char *descr; + + uint8_t id; /* mdep id */ +}; + +struct health_device { + bdaddr_t dst; + uint16_t app_id; + + struct mcap_mcl *mcl; + + struct queue *channels; /* data channels */ + + uint16_t ccpsm; + uint16_t dcpsm; +}; + +struct health_channel { + uint8_t mdep_id; + uint8_t type; + + struct health_device *dev; + + uint8_t remote_mdep; + struct mcap_mdl *mdl; + bool mdl_conn; + uint16_t mdl_id; /* MDL ID */ + + uint16_t id; /* channel id */ +}; + +struct health_app { + char *app_name; + char *provider_name; + char *service_name; + char *service_descr; + uint8_t num_of_mdep; + struct queue *mdeps; + + uint16_t id; /* app id */ + struct queue *devices; +}; + +static void send_app_reg_notify(struct health_app *app, uint8_t state) +{ + struct hal_ev_health_app_reg_state ev; + + DBG(""); + + ev.id = app->id; + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_EV_HEALTH_APP_REG_STATE, sizeof(ev), &ev); +} + +static void send_channel_state_notify(struct health_channel *channel, + uint8_t state, int fd) +{ + struct hal_ev_health_channel_state ev; + + DBG(""); + + bdaddr2android(&channel->dev->dst, ev.bdaddr); + ev.app_id = channel->dev->app_id; + ev.mdep_index = channel->mdep_id - 1; + ev.channel_id = channel->id; + ev.channel_state = state; + + ipc_send_notif_with_fd(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_EV_HEALTH_CHANNEL_STATE, + sizeof(ev), &ev, fd); +} + +static void unref_mdl(struct health_channel *channel) +{ + if (!channel || !channel->mdl) + return; + + mcap_mdl_unref(channel->mdl); + channel->mdl = NULL; + channel->mdl_conn = false; +} + +static void free_health_channel(void *data) +{ + struct health_channel *channel = data; + int fd; + + DBG("channel %p", channel); + + if (!channel) + return; + + fd = mcap_mdl_get_fd(channel->mdl); + if (fd >= 0) + shutdown(fd, SHUT_RDWR); + + unref_mdl(channel); + free(channel); +} + +static void destroy_channel(void *data) +{ + struct health_channel *channel = data; + + if (!channel) + return; + + send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_DESTROYED, -1); + queue_remove(channel->dev->channels, channel); + free_health_channel(channel); +} + +static void unref_mcl(struct health_device *dev) +{ + if (!dev || !dev->mcl) + return; + + mcap_close_mcl(dev->mcl, FALSE); + mcap_mcl_unref(dev->mcl); + dev->mcl = NULL; +} + +static void free_health_device(void *data) +{ + struct health_device *dev = data; + + if (!dev) + return; + + unref_mcl(dev); + queue_destroy(dev->channels, free_health_channel); + free(dev); +} + +static void free_mdep_cfg(void *data) +{ + struct mdep_cfg *cfg = data; + + if (!cfg) + return; + + free(cfg->descr); + free(cfg); +} + +static void free_health_app(void *data) +{ + struct health_app *app = data; + + if (!app) + return; + + free(app->app_name); + free(app->provider_name); + free(app->service_name); + free(app->service_descr); + queue_destroy(app->mdeps, free_mdep_cfg); + queue_destroy(app->devices, free_health_device); + free(app); +} + +static bool match_channel_by_mdl(const void *data, const void *user_data) +{ + const struct health_channel *channel = data; + const struct mcap_mdl *mdl = user_data; + + return channel->mdl == mdl; +} + +static bool match_channel_by_id(const void *data, const void *user_data) +{ + const struct health_channel *channel = data; + uint16_t channel_id = PTR_TO_INT(user_data); + + return channel->id == channel_id; +} + +static bool match_dev_by_mcl(const void *data, const void *user_data) +{ + const struct health_device *dev = data; + const struct mcap_mcl *mcl = user_data; + + return dev->mcl == mcl; +} + +static bool match_dev_by_addr(const void *data, const void *user_data) +{ + const struct health_device *dev = data; + const bdaddr_t *addr = user_data; + + return !bacmp(&dev->dst, addr); +} + +static bool match_channel_by_mdep_id(const void *data, const void *user_data) +{ + const struct health_channel *channel = data; + uint16_t mdep_id = PTR_TO_INT(user_data); + + return channel->mdep_id == mdep_id; +} + +static bool match_mdep_by_role(const void *data, const void *user_data) +{ + const struct mdep_cfg *mdep = data; + uint16_t role = PTR_TO_INT(user_data); + + return mdep->role == role; +} + +static bool match_mdep_by_id(const void *data, const void *user_data) +{ + const struct mdep_cfg *mdep = data; + uint16_t mdep_id = PTR_TO_INT(user_data); + + return mdep->id == mdep_id; +} + +static bool match_app_by_id(const void *data, const void *user_data) +{ + const struct health_app *app = data; + uint16_t app_id = PTR_TO_INT(user_data); + + return app->id == app_id; +} + +/* + * Helper struct and utility to search channel when only channel id + * is the option. i.e. destroy_channel call from HAL is passing only + * channel id. + */ +struct channel_search { + uint16_t channel_id; + struct mcap_mdl *mdl; + struct health_channel *channel; +}; + +static void device_search_channel(void *data, void *user_data) +{ + struct health_device *dev = data; + struct channel_search *search = user_data; + + if (search->channel) + return; + + if (search->channel_id) + search->channel = queue_find(dev->channels, match_channel_by_id, + INT_TO_PTR(search->channel_id)); + else if (search->mdl) + search->channel = queue_find(dev->channels, + match_channel_by_mdl, + search->mdl); +} + +static void app_search_channel(void *data, void *user_data) +{ + struct health_app *app = data; + struct channel_search *search = user_data; + + if (search->channel) + return; + + queue_foreach(app->devices, device_search_channel, search); +} + +static struct health_channel *search_channel_by_id(uint16_t id) +{ + struct channel_search search; + + DBG(""); + + search.channel_id = id; + search.mdl = NULL; + search.channel = NULL; + queue_foreach(apps, app_search_channel, &search); + + return search.channel; +} + +static struct health_channel *search_channel_by_mdl(struct mcap_mdl *mdl) +{ + struct channel_search search; + + DBG(""); + + search.channel_id = 0; + search.mdl = mdl; + search.channel = NULL; + queue_foreach(apps, app_search_channel, &search); + + return search.channel; +} + +struct mcl_search { + struct mcap_mcl *mcl; + struct health_device *dev; +}; + +static void app_search_dev(void *data, void *user_data) +{ + struct health_app *app = data; + struct mcl_search *search = user_data; + + if (search->dev) + return; + + search->dev = queue_find(app->devices, match_dev_by_mcl, search->mcl); +} + +static struct health_device *search_dev_by_mcl(struct mcap_mcl *mcl) +{ + struct mcl_search search; + + DBG(""); + + search.mcl = mcl; + search.dev = NULL; + + queue_foreach(apps, app_search_dev, &search); + + return search.dev; +} + +struct app_search { + uint8_t mdepid; + struct health_app *app; +}; + +static void app_search_mdep(void *data, void *user_data) +{ + struct health_app *app = data; + struct app_search *search = user_data; + + if (search->app) + return; + + if (queue_find(app->mdeps, match_mdep_by_id, + INT_TO_PTR(search->mdepid))) + search->app = app; +} + +static struct health_app *search_app_by_mdepid(uint8_t mdepid) +{ + struct app_search search; + + DBG(""); + + search.mdepid = mdepid; + search.app = NULL; + + queue_foreach(apps, app_search_mdep, &search); + + return search.app; +} + +static int register_service_protocols(sdp_record_t *rec, + struct health_app *app) +{ + uuid_t l2cap_uuid, mcap_c_uuid; + sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL; + sdp_list_t *access_proto_list = NULL; + sdp_data_t *psm = NULL, *mcap_ver = NULL; + uint32_t ccpsm; + uint16_t version = MCAP_VERSION; + GError *err = NULL; + int ret = -1; + + DBG(""); + + /* set l2cap information */ + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + l2cap_list = sdp_list_append(NULL, &l2cap_uuid); + if (!l2cap_list) + goto fail; + + ccpsm = mcap_get_ctrl_psm(mcap, &err); + if (err) + goto fail; + + psm = sdp_data_alloc(SDP_UINT16, &ccpsm); + if (!psm) + goto fail; + + if (!sdp_list_append(l2cap_list, psm)) + goto fail; + + proto_list = sdp_list_append(NULL, l2cap_list); + if (!proto_list) + goto fail; + + /* set mcap information */ + sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID); + mcap_list = sdp_list_append(NULL, &mcap_c_uuid); + if (!mcap_list) + goto fail; + + mcap_ver = sdp_data_alloc(SDP_UINT16, &version); + if (!mcap_ver) + goto fail; + + if (!sdp_list_append(mcap_list, mcap_ver)) + goto fail; + + if (!sdp_list_append(proto_list, mcap_list)) + goto fail; + + /* attach protocol information to service record */ + access_proto_list = sdp_list_append(NULL, proto_list); + if (!access_proto_list) + goto fail; + + sdp_set_access_protos(rec, access_proto_list); + ret = 0; + +fail: + sdp_list_free(l2cap_list, NULL); + sdp_list_free(mcap_list, NULL); + sdp_list_free(proto_list, NULL); + sdp_list_free(access_proto_list, NULL); + + if (psm) + sdp_data_free(psm); + + if (mcap_ver) + sdp_data_free(mcap_ver); + + if (err) + g_error_free(err); + + return ret; +} + +static int register_service_profiles(sdp_record_t *rec) +{ + int ret; + sdp_list_t *profile_list; + sdp_profile_desc_t hdp_profile; + + DBG(""); + + /* set hdp information */ + sdp_uuid16_create(&hdp_profile.uuid, HDP_SVCLASS_ID); + hdp_profile.version = HDP_VERSION; + profile_list = sdp_list_append(NULL, &hdp_profile); + if (!profile_list) + return -1; + + /* set profile descriptor list */ + ret = sdp_set_profile_descs(rec, profile_list); + sdp_list_free(profile_list, NULL); + + return ret; +} + +static int register_service_additional_protocols(sdp_record_t *rec, + struct health_app *app) +{ + int ret = -1; + uuid_t l2cap_uuid, mcap_d_uuid; + sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL; + sdp_list_t *access_proto_list = NULL; + sdp_data_t *psm = NULL; + uint32_t dcpsm; + GError *err = NULL; + + DBG(""); + + /* set l2cap information */ + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + l2cap_list = sdp_list_append(NULL, &l2cap_uuid); + if (!l2cap_list) + goto fail; + + dcpsm = mcap_get_data_psm(mcap, &err); + if (err) + goto fail; + + psm = sdp_data_alloc(SDP_UINT16, &dcpsm); + if (!psm) + goto fail; + + if (!sdp_list_append(l2cap_list, psm)) + goto fail; + + proto_list = sdp_list_append(NULL, l2cap_list); + if (!proto_list) + goto fail; + + /* set mcap information */ + sdp_uuid16_create(&mcap_d_uuid, MCAP_DATA_UUID); + mcap_list = sdp_list_append(NULL, &mcap_d_uuid); + if (!mcap_list) + goto fail; + + if (!sdp_list_append(proto_list, mcap_list)) + goto fail; + + /* attach protocol information to service record */ + access_proto_list = sdp_list_append(NULL, proto_list); + if (!access_proto_list) + goto fail; + + sdp_set_add_access_protos(rec, access_proto_list); + ret = 0; + +fail: + sdp_list_free(l2cap_list, NULL); + sdp_list_free(mcap_list, NULL); + sdp_list_free(proto_list, NULL); + sdp_list_free(access_proto_list, NULL); + + if (psm) + sdp_data_free(psm); + + if (err) + g_error_free(err); + + return ret; +} + +static sdp_list_t *mdeps_to_sdp_features(struct mdep_cfg *mdep) +{ + sdp_data_t *mdepid, *dtype = NULL, *role = NULL, *descr = NULL; + sdp_list_t *f_list = NULL; + + DBG(""); + + mdepid = sdp_data_alloc(SDP_UINT8, &mdep->id); + if (!mdepid) + return NULL; + + dtype = sdp_data_alloc(SDP_UINT16, &mdep->data_type); + if (!dtype) + goto fail; + + role = sdp_data_alloc(SDP_UINT8, &mdep->role); + if (!role) + goto fail; + + if (mdep->descr) { + descr = sdp_data_alloc(SDP_TEXT_STR8, mdep->descr); + if (!descr) + goto fail; + } + + f_list = sdp_list_append(NULL, mdepid); + if (!f_list) + goto fail; + + if (!sdp_list_append(f_list, dtype)) + goto fail; + + if (!sdp_list_append(f_list, role)) + goto fail; + + if (descr && !sdp_list_append(f_list, descr)) + goto fail; + + return f_list; + +fail: + sdp_list_free(f_list, NULL); + + if (mdepid) + sdp_data_free(mdepid); + + if (dtype) + sdp_data_free(dtype); + + if (role) + sdp_data_free(role); + + if (descr) + sdp_data_free(descr); + + return NULL; +} + +static void free_hdp_list(void *list) +{ + sdp_list_t *hdp_list = list; + + sdp_list_free(hdp_list, (sdp_free_func_t)sdp_data_free); +} + +static void register_features(void *data, void *user_data) +{ + struct mdep_cfg *mdep = data; + sdp_list_t **sup_features = user_data; + sdp_list_t *hdp_feature; + + DBG(""); + + hdp_feature = mdeps_to_sdp_features(mdep); + if (!hdp_feature) + return; + + if (!*sup_features) { + *sup_features = sdp_list_append(NULL, hdp_feature); + if (!*sup_features) + sdp_list_free(hdp_feature, + (sdp_free_func_t)sdp_data_free); + } else if (!sdp_list_append(*sup_features, hdp_feature)) { + sdp_list_free(hdp_feature, + (sdp_free_func_t)sdp_data_free); + } +} + +static int register_service_sup_features(sdp_record_t *rec, + struct health_app *app) +{ + sdp_list_t *sup_features = NULL; + + DBG(""); + + queue_foreach(app->mdeps, register_features, &sup_features); + if (!sup_features) + return -1; + + if (sdp_set_supp_feat(rec, sup_features) < 0) { + sdp_list_free(sup_features, free_hdp_list); + return -1; + } + + sdp_list_free(sup_features, free_hdp_list); + return 0; +} + +static int register_data_exchange_spec(sdp_record_t *rec) +{ + sdp_data_t *spec; + uint8_t data_spec = DATA_EXCHANGE_SPEC_11073; + /* As of now only 11073 is supported, so we set it as default */ + + DBG(""); + + spec = sdp_data_alloc(SDP_UINT8, &data_spec); + if (!spec) + return -1; + + if (sdp_attr_add(rec, SDP_ATTR_DATA_EXCHANGE_SPEC, spec) < 0) { + sdp_data_free(spec); + return -1; + } + + return 0; +} + +static int register_mcap_features(sdp_record_t *rec) +{ + sdp_data_t *mcap_proc; + uint8_t mcap_sup_proc = MCAP_SUP_PROC; + + DBG(""); + + mcap_proc = sdp_data_alloc(SDP_UINT8, &mcap_sup_proc); + if (!mcap_proc) + return -1; + + if (sdp_attr_add(rec, SDP_ATTR_MCAP_SUPPORTED_PROCEDURES, + mcap_proc) < 0) { + sdp_data_free(mcap_proc); + return -1; + } + + return 0; +} + +static int set_sdp_services_uuid(sdp_record_t *rec, uint8_t role) +{ + uuid_t source, sink; + sdp_list_t *list = NULL; + + sdp_uuid16_create(&sink, HDP_SINK_SVCLASS_ID); + sdp_uuid16_create(&source, HDP_SOURCE_SVCLASS_ID); + sdp_get_service_classes(rec, &list); + + switch (role) { + case HAL_HEALTH_MDEP_ROLE_SOURCE: + if (!sdp_list_find(list, &source, sdp_uuid_cmp)) + list = sdp_list_append(list, &source); + break; + case HAL_HEALTH_MDEP_ROLE_SINK: + if (!sdp_list_find(list, &sink, sdp_uuid_cmp)) + list = sdp_list_append(list, &sink); + break; + } + + if (sdp_set_service_classes(rec, list) < 0) { + sdp_list_free(list, NULL); + return -1; + } + + sdp_list_free(list, NULL); + + return 0; +} + +static int update_sdp_record(struct health_app *app) +{ + sdp_record_t *rec; + uint8_t role; + + DBG(""); + + if (record_id > 0) { + bt_adapter_remove_record(record_id); + record_id = 0; + } + + rec = sdp_record_alloc(); + if (!rec) + return -1; + + role = HAL_HEALTH_MDEP_ROLE_SOURCE; + if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role))) + set_sdp_services_uuid(rec, role); + + role = HAL_HEALTH_MDEP_ROLE_SINK; + if (queue_find(app->mdeps, match_mdep_by_role, INT_TO_PTR(role))) + set_sdp_services_uuid(rec, role); + + sdp_set_info_attr(rec, app->service_name, app->provider_name, + app->service_descr); + + if (register_service_protocols(rec, app) < 0) + goto fail; + + if (register_service_profiles(rec) < 0) + goto fail; + + if (register_service_additional_protocols(rec, app) < 0) + goto fail; + + if (register_service_sup_features(rec, app) < 0) + goto fail; + + if (register_data_exchange_spec(rec) < 0) + goto fail; + + if (register_mcap_features(rec) < 0) + goto fail; + + if (sdp_set_record_state(rec, record_state++) < 0) + goto fail; + + if (bt_adapter_add_record(rec, SVC_HINT_HEALTH) < 0) { + error("health: Failed to register HEALTH record"); + goto fail; + } + + record_id = rec->handle; + + return 0; + +fail: + sdp_record_free(rec); + + return -1; +} + +static struct health_app *create_health_app(const char *app_name, + const char *provider, const char *srv_name, + const char *srv_descr, uint8_t mdeps) +{ + struct health_app *app; + static unsigned int app_id = 1; + + DBG(""); + + app = new0(struct health_app, 1); + if (!app) + return NULL; + + app->id = app_id++; + app->num_of_mdep = mdeps; + app->app_name = strdup(app_name); + + if (provider) { + app->provider_name = strdup(provider); + if (!app->provider_name) + goto fail; + } + + if (srv_name) { + app->service_name = strdup(srv_name); + if (!app->service_name) + goto fail; + } + + if (srv_descr) { + app->service_descr = strdup(srv_descr); + if (!app->service_descr) + goto fail; + } + + app->mdeps = queue_new(); + if (!app->mdeps) + goto fail; + + app->devices = queue_new(); + if (!app->devices) + goto fail; + + return app; + +fail: + free_health_app(app); + return NULL; +} + +static void bt_health_register_app(const void *buf, uint16_t len) +{ + const struct hal_cmd_health_reg_app *cmd = buf; + struct hal_rsp_health_reg_app rsp; + struct health_app *app; + uint16_t off; + uint16_t app_name_len, provider_len, srv_name_len, srv_descr_len; + char *app_name, *provider = NULL, *srv_name = NULL, *srv_descr = NULL; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len || + cmd->app_name_off > cmd->provider_name_off || + cmd->provider_name_off > cmd->service_name_off || + cmd->service_name_off > cmd->service_descr_off || + cmd->service_descr_off > cmd->len) { + error("health: Invalid register app command, terminating"); + raise(SIGTERM); + return; + } + + app_name = (char *) cmd->data; + app_name_len = cmd->provider_name_off - cmd->app_name_off; + + off = app_name_len; + provider_len = cmd->service_name_off - off; + if (provider_len > 0) + provider = (char *) cmd->data + off; + + off += provider_len; + srv_name_len = cmd->service_descr_off - off; + if (srv_name_len > 0) + srv_name = (char *) cmd->data + off; + + off += srv_name_len; + srv_descr_len = cmd->len - off; + if (srv_descr_len > 0) + srv_descr = (char *) cmd->data + off; + + app = create_health_app(app_name, provider, srv_name, srv_descr, + cmd->num_of_mdep); + if (!app) + goto fail; + + if (!queue_push_tail(apps, app)) + goto fail; + + rsp.app_id = app->id; + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_REG_APP, + sizeof(rsp), &rsp, -1); + return; + +fail: + free_health_app(app); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, + HAL_STATUS_FAILED); +} + +static uint8_t android2channel_type(uint8_t type) +{ + switch (type) { + case HAL_HEALTH_CHANNEL_TYPE_RELIABLE: + return CHANNEL_TYPE_RELIABLE; + case HAL_HEALTH_CHANNEL_TYPE_STREAMING: + return CHANNEL_TYPE_STREAM; + default: + return CHANNEL_TYPE_ANY; + } +} + +static void bt_health_mdep_cfg_data(const void *buf, uint16_t len) +{ + const struct hal_cmd_health_mdep *cmd = buf; + struct health_app *app; + struct mdep_cfg *mdep = NULL; + uint8_t status; + + DBG(""); + + app = queue_find(apps, match_app_by_id, INT_TO_PTR(cmd->app_id)); + if (!app) { + status = HAL_STATUS_INVALID; + goto fail; + } + + mdep = new0(struct mdep_cfg, 1); + if (!mdep) { + status = HAL_STATUS_INVALID; + goto fail; + } + + mdep->role = cmd->role; + mdep->data_type = cmd->data_type; + mdep->channel_type = android2channel_type(cmd->channel_type); + mdep->id = queue_length(app->mdeps) + 1; + + if (cmd->descr_len > 0) { + mdep->descr = malloc0(cmd->descr_len); + memcpy(mdep->descr, cmd->descr, cmd->descr_len); + } + + if (!queue_push_tail(app->mdeps, mdep)) { + status = HAL_STATUS_FAILED; + goto fail; + } + + if (app->num_of_mdep != queue_length(app->mdeps)) + goto send_rsp; + + /* add sdp record from app configuration data */ + /* + * TODO: Check what to be done if mupltple applications are trying to + * register with different role and different configurations. + * 1) Does device supports SOURCE and SINK at the same time ? + * 2) Does it require different SDP records or one record with + * multile MDEP configurations ? + */ + if (update_sdp_record(app) < 0) { + error("health: HDP SDP record preparation failed"); + status = HAL_STATUS_FAILED; + goto fail; + } + + send_app_reg_notify(app, HAL_HEALTH_APP_REG_SUCCESS); + +send_rsp: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, + HAL_STATUS_SUCCESS); + return; + +fail: + if (status != HAL_STATUS_SUCCESS) { + free_mdep_cfg(mdep); + queue_remove(apps, app); + free_health_app(app); + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, HAL_OP_HEALTH_MDEP, + status); +} + +static void bt_health_unregister_app(const void *buf, uint16_t len) +{ + const struct hal_cmd_health_unreg_app *cmd = buf; + struct health_app *app; + + DBG(""); + + app = queue_remove_if(apps, match_app_by_id, INT_TO_PTR(cmd->app_id)); + if (!app) { + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_INVALID); + return; + } + + send_app_reg_notify(app, HAL_HEALTH_APP_DEREG_SUCCESS); + + if (record_id > 0) { + bt_adapter_remove_record(record_id); + record_id = 0; + } + + free_health_app(app); + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_UNREG_APP, HAL_STATUS_SUCCESS); +} + +static int get_prot_desc_entry(sdp_data_t *entry, int type, guint16 *val) +{ + sdp_data_t *iter; + int proto; + + if (!entry || !SDP_IS_SEQ(entry->dtd)) + return -1; + + iter = entry->val.dataseq; + if (!(iter->dtd & SDP_UUID_UNSPEC)) + return -1; + + proto = sdp_uuid_to_proto(&iter->val.uuid); + if (proto != type) + return -1; + + if (!val) + return 0; + + iter = iter->next; + if (iter->dtd != SDP_UINT16) + return -1; + + *val = iter->val.uint16; + + return 0; +} + +static int get_prot_desc_list(const sdp_record_t *rec, uint16_t *psm, + uint16_t *version) +{ + sdp_data_t *pdl, *p0, *p1; + + if (!psm && !version) + return -1; + + pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST); + if (!pdl || !SDP_IS_SEQ(pdl->dtd)) + return -1; + + p0 = pdl->val.dataseq; + if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0) + return -1; + + p1 = p0->next; + if (get_prot_desc_entry(p1, MCAP_CTRL_UUID, version) < 0) + return -1; + + return 0; +} + +static int get_ccpsm(sdp_list_t *recs, uint16_t *ccpsm) +{ + sdp_list_t *l; + + for (l = recs; l; l = l->next) { + sdp_record_t *rec = l->data; + + if (!get_prot_desc_list(rec, ccpsm, NULL)) + return 0; + } + + return -1; +} + +static int get_add_prot_desc_list(const sdp_record_t *rec, uint16_t *psm) +{ + sdp_data_t *pdl, *p0, *p1; + + if (!psm) + return -1; + + pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST); + if (!pdl || pdl->dtd != SDP_SEQ8) + return -1; + + pdl = pdl->val.dataseq; + if (pdl->dtd != SDP_SEQ8) + return -1; + + p0 = pdl->val.dataseq; + + if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0) + return -1; + + p1 = p0->next; + if (get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL) < 0) + return -1; + + return 0; +} + +static int get_dcpsm(sdp_list_t *recs, uint16_t *dcpsm) +{ + sdp_list_t *l; + + for (l = recs; l; l = l->next) { + sdp_record_t *rec = l->data; + + if (!get_add_prot_desc_list(rec, dcpsm)) + return 0; + } + + return -1; +} + +static int send_echo_data(int sock, const void *buf, uint32_t size) +{ + const uint8_t *buf_b = buf; + uint32_t sent = 0; + + while (sent < size) { + int n = write(sock, buf_b + sent, size - sent); + if (n < 0) + return -1; + sent += n; + } + + return 0; +} + +static gboolean serve_echo(GIOChannel *io, GIOCondition cond, gpointer data) +{ + struct health_channel *channel = data; + uint8_t buf[MCAP_DC_MTU]; + int fd, len, ret; + + DBG("channel %p", channel); + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { + DBG("Error condition on channel"); + return FALSE; + } + + fd = g_io_channel_unix_get_fd(io); + + len = read(fd, buf, sizeof(buf)); + if (len < 0) { + DBG("Error reading ECHO"); + return FALSE; + } + + ret = send_echo_data(fd, buf, len); + if (ret != len) + DBG("Error sending ECHO back"); + + return FALSE; +} + +static void mcap_mdl_connected_cb(struct mcap_mdl *mdl, void *data) +{ + struct health_channel *channel = data; + int fd; + + DBG("Data channel connected: mdl %p channel %p", mdl, channel); + + if (!channel) { + channel = search_channel_by_mdl(mdl); + if (!channel) { + error("health: channel data does not exist"); + return; + } + } + + if (!channel->mdl) + channel->mdl = mcap_mdl_ref(mdl); + + fd = mcap_mdl_get_fd(channel->mdl); + if (fd < 0) { + error("health: error retrieving fd"); + goto fail; + } + + if (channel->mdep_id == MDEP_ECHO) { + GIOChannel *io; + + io = g_io_channel_unix_new(fd); + g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL | G_IO_IN, + serve_echo, channel); + g_io_channel_unref(io); + + return; + } + + info("health: MDL connected"); + send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd); + + return; +fail: + /* TODO: mcap_mdl_abort */ + destroy_channel(channel); +} + +static void mcap_mdl_closed_cb(struct mcap_mdl *mdl, void *data) +{ + struct health_channel *channel = data; + + info("health: MDL closed"); + + if (!channel) + return; + + channel->mdl_conn = false; +} + +static void mcap_mdl_deleted_cb(struct mcap_mdl *mdl, void *data) +{ + struct health_channel *channel; + + info("health: MDL deleted"); + + channel = search_channel_by_mdl(mdl); + if (!channel) + return; + + DBG("channel %p mdl %p", channel, mdl); + destroy_channel(channel); +} + +static void mcap_mdl_aborted_cb(struct mcap_mdl *mdl, void *data) +{ + DBG("Not Implemeneted"); +} + +static struct health_device *create_device(struct health_app *app, + const uint8_t *addr) +{ + struct health_device *dev; + + if (!app) + return NULL; + + /* create device and push it to devices queue */ + dev = new0(struct health_device, 1); + if (!dev) + return NULL; + + android2bdaddr(addr, &dev->dst); + dev->channels = queue_new(); + if (!dev->channels) { + free_health_device(dev); + return NULL; + } + + if (!queue_push_tail(app->devices, dev)) { + free_health_device(dev); + return NULL; + } + + dev->app_id = app->id; + + return dev; +} + +static struct health_device *get_device(struct health_app *app, + const uint8_t *addr) +{ + struct health_device *dev; + bdaddr_t bdaddr; + + android2bdaddr(addr, &bdaddr); + dev = queue_find(app->devices, match_dev_by_addr, &bdaddr); + if (dev) + return dev; + + return create_device(app, addr); +} + +static struct health_channel *create_channel(struct health_app *app, + uint8_t mdep_index, + struct health_device *dev) +{ + struct mdep_cfg *mdep; + struct health_channel *channel; + static unsigned int channel_id = 1; + + DBG("mdep %u", mdep_index); + + if (!dev || !app) + return NULL; + + mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdep_index)); + if (!mdep) { + if (mdep_index == MDEP_ECHO) { + mdep = new0(struct mdep_cfg, 1); + if (!mdep) + return NULL; + + /* Leave other configuration zeroes */ + mdep->id = MDEP_ECHO; + + if (!queue_push_tail(app->mdeps, mdep)) { + free_mdep_cfg(mdep); + return NULL; + } + } else + return NULL; + } + + /* create channel and push it to device */ + channel = new0(struct health_channel, 1); + if (!channel) + return NULL; + + channel->mdep_id = mdep->id; + channel->type = mdep->channel_type; + channel->id = channel_id++; + channel->dev = dev; + + if (!queue_push_tail(dev->channels, channel)) { + free_health_channel(channel); + return NULL; + } + + return channel; +} + +static struct health_channel *connect_channel(struct health_app *app, + struct mcap_mcl *mcl, + uint8_t mdepid) +{ + struct health_device *device; + struct health_channel *channel = NULL; + bdaddr_t addr; + + DBG("app %p mdepid %u", app, mdepid); + + mcap_mcl_get_addr(mcl, &addr); + + if (!app) { + DBG("No app found for mdepid %u", mdepid); + return NULL; + } + + device = get_device(app, (uint8_t *) &addr); + if (!device) + return NULL; + + channel = create_channel(app, mdepid, device); + + return channel; +} + +static uint8_t conf_to_l2cap(uint8_t conf) +{ + return conf == CHANNEL_TYPE_STREAM ? L2CAP_MODE_STREAMING : + L2CAP_MODE_ERTM; +} + +static uint8_t mcap_mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid, + uint16_t mdlid, uint8_t *conf, void *data) +{ + GError *gerr = NULL; + struct health_channel *channel; + struct health_app *app; + struct mdep_cfg *mdep; + + DBG("Data channel request: mdepid %u mdlid %u conf %u", + mdepid, mdlid, *conf); + + if (mdepid == MDEP_ECHO) + /* For echo service take last app */ + app = queue_peek_tail(apps); + else + app = search_app_by_mdepid(mdepid); + + if (!app) + return MCAP_MDL_BUSY; + + channel = connect_channel(app, mcl, mdepid); + if (!channel) + return MCAP_MDL_BUSY; + + /* Channel is assigned here after creation */ + mcl->cb->user_data = channel; + + if (mdepid == MDEP_ECHO) { + switch (*conf) { + case CHANNEL_TYPE_ANY: + *conf = CHANNEL_TYPE_RELIABLE; + break; + case CHANNEL_TYPE_RELIABLE: + break; + case CHANNEL_TYPE_STREAM: + return MCAP_CONFIGURATION_REJECTED; + default: + /* + * Special case defined in HDP spec 3.4. + * When an invalid configuration is received we shall + * close the MCL when we are still processing the + * callback. + */ + /* TODO close device */ + return MCAP_CONFIGURATION_REJECTED; /* not processed */ + } + + if (!mcap_set_data_chan_mode(mcap, L2CAP_MODE_ERTM, &gerr)) { + error("Error: %s", gerr->message); + g_error_free(gerr); + return MCAP_MDL_BUSY; + } + + /* TODO: Create channel */ + + return MCAP_SUCCESS; + } + + mdep = queue_find(app->mdeps, match_mdep_by_id, INT_TO_PTR(mdepid)); + if (!mdep) + return MCAP_MDL_BUSY; + + switch (*conf) { + case CHANNEL_TYPE_ANY: + if (mdep->role == HAL_HEALTH_MDEP_ROLE_SINK) { + return MCAP_CONFIGURATION_REJECTED; + } else { + if (queue_length(channel->dev->channels) <= 1) + *conf = CHANNEL_TYPE_RELIABLE; + else + *conf = CHANNEL_TYPE_STREAM; + } + break; + case CHANNEL_TYPE_STREAM: + if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE) + return MCAP_CONFIGURATION_REJECTED; + break; + case CHANNEL_TYPE_RELIABLE: + if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE) + return MCAP_CONFIGURATION_REJECTED; + break; + default: + /* + * Special case defined in HDP spec 3.4. When an invalid + * configuration is received we shall close the MCL when + * we are still processing the callback. + */ + /* TODO: close device */ + return MCAP_CONFIGURATION_REJECTED; /* not processed */ + } + + if (!mcap_set_data_chan_mode(mcap, conf_to_l2cap(*conf), &gerr)) { + error("health: error setting L2CAP mode: %s", gerr->message); + g_error_free(gerr); + return MCAP_MDL_BUSY; + } + + return MCAP_SUCCESS; +} + +static uint8_t mcap_mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data) +{ + struct health_channel *channel; + GError *err = NULL; + + DBG(""); + + channel = search_channel_by_mdl(mdl); + if (!channel) { + error("health: channel data does not exist"); + return MCAP_UNSPECIFIED_ERROR; + } + + if (!mcap_set_data_chan_mode(mcap, + conf_to_l2cap(channel->type), &err)) { + error("health: %s", err->message); + g_error_free(err); + return MCAP_MDL_BUSY; + } + + return MCAP_SUCCESS; +} + +static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data) +{ + struct health_channel *channel = data; + int fd; + + DBG(""); + + if (gerr) { + error("health: error connecting to MDL %s", gerr->message); + goto fail; + } + + fd = mcap_mdl_get_fd(channel->mdl); + if (fd < 0) { + error("health: error retrieving fd"); + goto fail; + } + + info("health: MDL connected"); + channel->mdl_conn = true; + + /* first data channel should be reliable data channel */ + if (!queue_length(channel->dev->channels)) + if (channel->type != CHANNEL_TYPE_RELIABLE) + goto fail; + + send_channel_state_notify(channel, HAL_HEALTH_CHANNEL_CONNECTED, fd); + + return; + +fail: + /* TODO: mcap_mdl_abort */ + destroy_channel(channel); +} + +static void reconnect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data) +{ + struct health_channel *channel = data; + uint8_t mode; + GError *err = NULL; + + DBG(""); + + if (gerr) { + error("health: error reconnecting to MDL %s", gerr->message); + goto fail; + } + + channel->mdl_id = mcap_mdl_get_mdlid(mdl); + + if (channel->type == CHANNEL_TYPE_RELIABLE) + mode = L2CAP_MODE_ERTM; + else + mode = L2CAP_MODE_STREAMING; + + if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm, + connect_mdl_cb, channel, + NULL, &err)) { + error("health: error connecting to mdl"); + g_error_free(err); + goto fail; + } + + return; + +fail: + /* TODO: mcap_mdl_abort */ + destroy_channel(channel); +} + +static int reconnect_mdl(struct health_channel *channel) +{ + GError *gerr = NULL; + + DBG(""); + + if (!channel) + return -1; + + if (!mcap_reconnect_mdl(channel->mdl, reconnect_mdl_cb, channel, + NULL, &gerr)){ + error("health: reconnect failed %s", gerr->message); + destroy_channel(channel); + } + + return 0; +} + +static void create_mdl_cb(struct mcap_mdl *mdl, uint8_t type, GError *gerr, + gpointer data) +{ + struct health_channel *channel = data; + uint8_t mode; + GError *err = NULL; + + DBG(""); + if (gerr) { + error("health: error creating MDL %s", gerr->message); + goto fail; + } + + if (channel->type == CHANNEL_TYPE_ANY && type != CHANNEL_TYPE_ANY) + channel->type = type; + + /* + * if requested channel type is not same as preferred + * channel type from remote device, then abort the connection. + */ + if (channel->type != type) { + /* TODO: abort mdl */ + error("health: channel type requested %d preferred %d not same", + channel->type, type); + goto fail; + } + + if (!channel->mdl) + channel->mdl = mcap_mdl_ref(mdl); + + channel->type = type; + channel->mdl_id = mcap_mdl_get_mdlid(mdl); + + if (channel->type == CHANNEL_TYPE_RELIABLE) + mode = L2CAP_MODE_ERTM; + else + mode = L2CAP_MODE_STREAMING; + + if (!mcap_connect_mdl(channel->mdl, mode, channel->dev->dcpsm, + connect_mdl_cb, channel, + NULL, &err)) { + error("health: error connecting to mdl"); + g_error_free(err); + goto fail; + } + + return; + +fail: + destroy_channel(channel); +} + +static bool check_role(uint8_t rec_role, uint8_t app_role) +{ + if ((rec_role == HAL_HEALTH_MDEP_ROLE_SINK && + app_role == HAL_HEALTH_MDEP_ROLE_SOURCE) || + (rec_role == HAL_HEALTH_MDEP_ROLE_SOURCE && + app_role == HAL_HEALTH_MDEP_ROLE_SINK)) + return true; + + return false; +} + +static bool get_mdep_from_rec(const sdp_record_t *rec, uint8_t role, + uint16_t d_type, uint8_t *mdep) +{ + sdp_data_t *list, *feat; + + if (!mdep) + return false; + + list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST); + if (!list || !SDP_IS_SEQ(list->dtd)) + return false; + + for (feat = list->val.dataseq; feat; feat = feat->next) { + sdp_data_t *data_type, *mdepid, *role_t; + + if (!SDP_IS_SEQ(feat->dtd)) + continue; + + mdepid = feat->val.dataseq; + if (!mdepid) + continue; + + data_type = mdepid->next; + if (!data_type) + continue; + + role_t = data_type->next; + if (!role_t) + continue; + + if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 || + role_t->dtd != SDP_UINT8) + continue; + + if (data_type->val.uint16 != d_type || + !check_role(role_t->val.uint8, role)) + continue; + + *mdep = mdepid->val.uint8; + + return true; + } + + return false; +} + +static bool get_remote_mdep(sdp_list_t *recs, struct health_channel *channel) +{ + struct health_app *app; + struct mdep_cfg *mdep; + uint8_t mdep_id; + + app = queue_find(apps, match_app_by_id, + INT_TO_PTR(channel->dev->app_id)); + if (!app) + return false; + + mdep = queue_find(app->mdeps, match_mdep_by_id, + INT_TO_PTR(channel->mdep_id)); + if (!mdep) + return false; + + if (!get_mdep_from_rec(recs->data, mdep->role, mdep->data_type, + &mdep_id)) { + error("health: no matching MDEP: %u", channel->mdep_id); + return false; + } + + channel->remote_mdep = mdep_id; + return true; +} + +static bool create_mdl(struct health_channel *channel) +{ + struct health_app *app; + struct mdep_cfg *mdep; + uint8_t type; + GError *gerr = NULL; + + app = queue_find(apps, match_app_by_id, + INT_TO_PTR(channel->dev->app_id)); + if (!app) + return false; + + mdep = queue_find(app->mdeps, match_mdep_by_id, + INT_TO_PTR(channel->mdep_id)); + if (!mdep) + return false; + + if (mdep->role == HAL_HEALTH_MDEP_ROLE_SOURCE) + type = channel->type; + else + type = CHANNEL_TYPE_ANY; + + if (!mcap_create_mdl(channel->dev->mcl, channel->remote_mdep, + type, create_mdl_cb, channel, NULL, &gerr)) { + error("health: error creating mdl %s", gerr->message); + g_error_free(gerr); + return false; + } + + return true; +} + +static bool set_mcl_cb(struct mcap_mcl *mcl, gpointer user_data, GError **err) +{ + return mcap_mcl_set_cb(mcl, user_data, err, + MCAP_MDL_CB_CONNECTED, mcap_mdl_connected_cb, + MCAP_MDL_CB_CLOSED, mcap_mdl_closed_cb, + MCAP_MDL_CB_DELETED, mcap_mdl_deleted_cb, + MCAP_MDL_CB_ABORTED, mcap_mdl_aborted_cb, + MCAP_MDL_CB_REMOTE_CONN_REQ, mcap_mdl_conn_req_cb, + MCAP_MDL_CB_REMOTE_RECONN_REQ, mcap_mdl_reconn_req_cb, + MCAP_MDL_CB_INVALID); +} + +static void create_mcl_cb(struct mcap_mcl *mcl, GError *err, gpointer data) +{ + struct health_channel *channel = data; + gboolean ret; + GError *gerr = NULL; + + DBG(""); + + if (err) { + error("health: error creating MCL : %s", err->message); + goto fail; + } + + if (!channel->dev->mcl) + channel->dev->mcl = mcap_mcl_ref(mcl); + + info("health: MCL connected"); + + ret = set_mcl_cb(channel->dev->mcl, channel, &gerr); + if (!ret) { + error("health: error setting mdl callbacks: %s", gerr->message); + g_error_free(gerr); + goto fail; + } + + if (!create_mdl(channel)) + goto fail; + + return; + +fail: + destroy_channel(channel); +} + +static void search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct health_channel *channel = data; + GError *gerr = NULL; + + DBG(""); + + if (err < 0 || !recs) { + error("health: Error getting remote SDP records"); + goto fail; + } + + if (get_ccpsm(recs, &channel->dev->ccpsm) < 0) { + error("health: Can't get remote PSM for control channel"); + goto fail; + } + + if (get_dcpsm(recs, &channel->dev->dcpsm) < 0) { + error("health: Can't get remote PSM for data channel"); + goto fail; + } + + if (!get_remote_mdep(recs, channel)) { + error("health: Can't get remote MDEP data"); + goto fail; + } + + if (!mcap_create_mcl(mcap, &channel->dev->dst, channel->dev->ccpsm, + create_mcl_cb, channel, NULL, &gerr)) { + error("health: error creating mcl %s", gerr->message); + g_error_free(gerr); + goto fail; + } + + return; + +fail: + destroy_channel(channel); +} + +static int connect_mcl(struct health_channel *channel) +{ + uuid_t uuid; + int err; + + DBG(""); + + bt_string2uuid(&uuid, HDP_UUID); + + err = bt_search_service(&adapter_addr, &channel->dev->dst, &uuid, + search_cb, channel, NULL, 0); + if (!err) + send_channel_state_notify(channel, + HAL_HEALTH_CHANNEL_CONNECTING, -1); + + return err; +} + +static struct health_app *get_app(uint16_t app_id) +{ + return queue_find(apps, match_app_by_id, INT_TO_PTR(app_id)); +} + +static struct health_channel *get_channel(struct health_app *app, + uint8_t mdep_index, + struct health_device *dev) +{ + struct health_channel *channel; + uint8_t index; + + if (!dev) + return NULL; + + index = mdep_index + 1; + channel = queue_find(dev->channels, match_channel_by_mdep_id, + INT_TO_PTR(index)); + if (channel) + return channel; + + return create_channel(app, index, dev); +} + +static void bt_health_connect_channel(const void *buf, uint16_t len) +{ + const struct hal_cmd_health_connect_channel *cmd = buf; + struct hal_rsp_health_connect_channel rsp; + struct health_device *dev = NULL; + struct health_channel *channel = NULL; + struct health_app *app; + + DBG(""); + + app = get_app(cmd->app_id); + if (!app) + goto send_rsp; + + dev = get_device(app, cmd->bdaddr); + if (!dev) + goto send_rsp; + + channel = get_channel(app, cmd->mdep_index, dev); + if (!channel) + goto send_rsp; + + if (!queue_length(dev->channels)) { + if (channel->type != CHANNEL_TYPE_RELIABLE) { + error("health: first data shannel should be reliable"); + goto fail; + } + } + + if (!dev->mcl) { + if (connect_mcl(channel) < 0) { + error("health: error retrieving HDP SDP record"); + goto fail; + } + } else { + /* data channel is already connected */ + if (channel->mdl && channel->mdl_conn) + goto fail; + + /* create mdl if it does not exists */ + if (!channel->mdl && !create_mdl(channel)) + goto fail; + + /* reconnect mdl if it exists */ + if (channel->mdl && !channel->mdl_conn) { + if (reconnect_mdl(channel) < 0) + goto fail; + } + + } + + rsp.channel_id = channel->id; + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_CONNECT_CHANNEL, + sizeof(rsp), &rsp, -1); + return; + +fail: + queue_remove(channel->dev->channels, channel); + free_health_channel(channel); + +send_rsp: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_CONNECT_CHANNEL, HAL_STATUS_FAILED); +} + +static void channel_delete_cb(GError *gerr, gpointer data) +{ + struct health_channel *channel = data; + + DBG(""); + + if (gerr) { + error("health: channel delete failed %s", gerr->message); + return; + } + + destroy_channel(channel); +} + +static void bt_health_destroy_channel(const void *buf, uint16_t len) +{ + const struct hal_cmd_health_destroy_channel *cmd = buf; + struct health_channel *channel; + GError *gerr = NULL; + + DBG(""); + + channel = search_channel_by_id(cmd->channel_id); + if (!channel) + goto fail; + + if (!mcap_delete_mdl(channel->mdl, channel_delete_cb, channel, + NULL, &gerr)) { + error("health: channel delete failed %s", gerr->message); + goto fail; + } + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_SUCCESS); + + return; + +fail: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HEALTH, + HAL_OP_HEALTH_DESTROY_CHANNEL, HAL_STATUS_INVALID); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_HEALTH_REG_APP */ + { bt_health_register_app, true, + sizeof(struct hal_cmd_health_reg_app) }, + /* HAL_OP_HEALTH_MDEP */ + { bt_health_mdep_cfg_data, true, + sizeof(struct hal_cmd_health_mdep) }, + /* HAL_OP_HEALTH_UNREG_APP */ + { bt_health_unregister_app, false, + sizeof(struct hal_cmd_health_unreg_app) }, + /* HAL_OP_HEALTH_CONNECT_CHANNEL */ + { bt_health_connect_channel, false, + sizeof(struct hal_cmd_health_connect_channel) }, + /* HAL_OP_HEALTH_DESTROY_CHANNEL */ + { bt_health_destroy_channel, false, + sizeof(struct hal_cmd_health_destroy_channel) }, +}; + +static void mcl_connected(struct mcap_mcl *mcl, gpointer data) +{ + GError *gerr = NULL; + bool ret; + + DBG(""); + + info("health: MCL connected"); + ret = set_mcl_cb(mcl, NULL, &gerr); + if (!ret) { + error("health: error setting mcl callbacks: %s", gerr->message); + g_error_free(gerr); + } +} + +static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data) +{ + struct health_device *dev; + + DBG(""); + + info("health: MCL reconnected"); + dev = search_dev_by_mcl(mcl); + if (!dev) { + error("device data does not exists"); + return; + } +} + +static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data) +{ + struct health_device *dev; + + DBG(""); + + info("health: MCL disconnected"); + dev = search_dev_by_mcl(mcl); + unref_mcl(dev); +} + +static void mcl_uncached(struct mcap_mcl *mcl, gpointer data) +{ + /* mcap library maintains cache of mcls, not required here */ +} + +bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + GError *err = NULL; + + DBG(""); + + bacpy(&adapter_addr, addr); + + mcap = mcap_create_instance(&adapter_addr, BT_IO_SEC_MEDIUM, 0, 0, + mcl_connected, mcl_reconnected, + mcl_disconnected, mcl_uncached, + NULL, /* CSP is not used right now */ + NULL, &err); + if (!mcap) { + error("health: MCAP instance creation failed %s", err->message); + g_error_free(err); + return false; + } + + hal_ipc = ipc; + apps = queue_new(); + if (!apps) + return false; + + ipc_register(hal_ipc, HAL_SERVICE_ID_HEALTH, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + return true; +} + +void bt_health_unregister(void) +{ + DBG(""); + + mcap_instance_unref(mcap); + queue_destroy(apps, free_health_app); + ipc_unregister(hal_ipc, HAL_SERVICE_ID_HEALTH); + hal_ipc = NULL; +} diff -Nru bluez-4.101/android/health.h bluez-5.23/android/health.h --- bluez-4.101/android/health.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/health.h 2014-03-25 20:53:41.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +bool bt_health_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_health_unregister(void); diff -Nru bluez-4.101/android/hidhost.c bluez-5.23/android/hidhost.c --- bluez-4.101/android/hidhost.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hidhost.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,1567 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "btio/btio.h" +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/shared/mgmt.h" +#include "src/shared/util.h" +#include "src/shared/uhid.h" +#include "src/sdp-client.h" +#include "src/uuid-helper.h" +#include "src/log.h" + +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "bluetooth.h" +#include "gatt.h" +#include "hog.h" +#include "hidhost.h" +#include "utils.h" + +#define L2CAP_PSM_HIDP_CTRL 0x11 +#define L2CAP_PSM_HIDP_INTR 0x13 + +/* HID message types */ +#define HID_MSG_CONTROL 0x10 +#define HID_MSG_GET_REPORT 0x40 +#define HID_MSG_SET_REPORT 0x50 +#define HID_MSG_GET_PROTOCOL 0x60 +#define HID_MSG_SET_PROTOCOL 0x70 +#define HID_MSG_DATA 0xa0 + +/* HID data types */ +#define HID_DATA_TYPE_INPUT 0x01 +#define HID_DATA_TYPE_OUTPUT 0x02 +#define HID_DATA_TYPE_FEATURE 0x03 + +/* HID protocol header parameters */ +#define HID_PROTO_BOOT 0x00 +#define HID_PROTO_REPORT 0x01 + +/* HID GET REPORT Size Field */ +#define HID_GET_REPORT_SIZE_FIELD 0x08 + +/* HID Virtual Cable Unplug */ +#define HID_VIRTUAL_CABLE_UNPLUG 0x05 + +#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb" + +static bdaddr_t adapter_addr; + +static GIOChannel *ctrl_io = NULL; +static GIOChannel *intr_io = NULL; +static GSList *devices = NULL; +static unsigned int hog_app = 0; + +static struct ipc *hal_ipc = NULL; + +struct hid_device { + bdaddr_t dst; + uint8_t state; + uint8_t subclass; + uint16_t vendor; + uint16_t product; + uint16_t version; + uint8_t country; + int rd_size; + void *rd_data; + uint8_t boot_dev; + GIOChannel *ctrl_io; + GIOChannel *intr_io; + guint ctrl_watch; + guint intr_watch; + struct bt_uhid *uhid; + uint8_t last_hid_msg; + struct bt_hog *hog; + int sec_level; +}; + +static int device_cmp(gconstpointer s, gconstpointer user_data) +{ + const struct hid_device *dev = s; + const bdaddr_t *dst = user_data; + + return bacmp(&dev->dst, dst); +} + +static void hid_device_free(void *data) +{ + struct hid_device *dev = data; + + if (dev->ctrl_watch > 0) + g_source_remove(dev->ctrl_watch); + + if (dev->intr_watch > 0) + g_source_remove(dev->intr_watch); + + if (dev->intr_io) + g_io_channel_unref(dev->intr_io); + + if (dev->ctrl_io) + g_io_channel_unref(dev->ctrl_io); + + if (dev->uhid) + bt_uhid_unref(dev->uhid); + + if (dev->hog) + bt_hog_unref(dev->hog); + + g_free(dev->rd_data); + g_free(dev); +} + +static void hid_device_remove(struct hid_device *dev) +{ + devices = g_slist_remove(devices, dev); + hid_device_free(dev); +} + +static struct hid_device *hid_device_new(const bdaddr_t *addr) +{ + struct hid_device *dev; + + dev = g_new0(struct hid_device, 1); + bacpy(&dev->dst, addr); + dev->state = HAL_HIDHOST_STATE_DISCONNECTED; + dev->sec_level = BT_IO_SEC_LOW; + + devices = g_slist_append(devices, dev); + + return dev; +} + +static bool hex2buf(const uint8_t *hex, uint8_t *buf, int buf_size) +{ + int i, j; + char c; + uint8_t b; + + for (i = 0, j = 0; i < buf_size; i++, j++) { + c = toupper(hex[j]); + + if (c >= '0' && c <= '9') + b = c - '0'; + else if (c >= 'A' && c <= 'F') + b = 10 + c - 'A'; + else + return false; + + j++; + + c = toupper(hex[j]); + + if (c >= '0' && c <= '9') + b = b * 16 + c - '0'; + else if (c >= 'A' && c <= 'F') + b = b * 16 + 10 + c - 'A'; + else + return false; + + buf[i] = b; + } + + return true; +} + +static void handle_uhid_output(struct uhid_event *event, void *user_data) +{ + struct uhid_output_req *output = &event->u.output; + struct hid_device *dev = user_data; + int fd, req_size; + uint8_t *req; + + if (!dev->ctrl_io) + return; + + req_size = 1 + output->size; + req = malloc0(req_size); + if (!req) + return; + + req[0] = HID_MSG_SET_REPORT | output->rtype; + memcpy(req + 1, output->data, req_size - 1); + + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, req, req_size) < 0) + error("hidhost: error writing set_report: %s (%d)", + strerror(errno), errno); + + free(req); +} + +static gboolean intr_io_watch_cb(GIOChannel *chan, gpointer data) +{ + struct hid_device *dev = data; + uint8_t buf[UHID_DATA_MAX]; + struct uhid_event ev; + int fd, bread, err; + + /* Wait uHID if not ready */ + if (!dev->uhid) + return TRUE; + + fd = g_io_channel_unix_get_fd(chan); + bread = read(fd, buf, sizeof(buf)); + if (bread < 0) { + error("hidhost: read from interrupt failed: %s(%d)", + strerror(errno), -errno); + return TRUE; + } + + /* Discard non-data packets */ + if (bread == 0 || buf[0] != (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) + return TRUE; + + /* send data to uHID device skipping HIDP header byte */ + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + ev.u.input.size = bread - 1; + memcpy(ev.u.input.data, &buf[1], ev.u.input.size); + + err = bt_uhid_send(dev->uhid, &ev); + if (err < 0) + DBG("bt_uhid_send: %s (%d)", strerror(-err), -err); + + return TRUE; +} + +static void bt_hid_notify_state(struct hid_device *dev, uint8_t state) +{ + struct hal_ev_hidhost_conn_state ev; + char address[18]; + + if (dev->state == state) + return; + + dev->state = state; + + ba2str(&dev->dst, address); + DBG("device %s state %u", address, state); + + bdaddr2android(&dev->dst, ev.bdaddr); + ev.state = state; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_CONN_STATE, sizeof(ev), &ev); +} + +static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct hid_device *dev = data; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) + goto error; + + if (cond & G_IO_IN) + return intr_io_watch_cb(chan, data); + +error: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + + /* + * Checking for ctrl_watch avoids a double g_io_channel_shutdown since + * it's likely that ctrl_watch_cb has been queued for dispatching in + * this mainloop iteration + */ + if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->ctrl_watch) + g_io_channel_shutdown(chan, TRUE, NULL); + + /* Close control channel */ + if (dev->ctrl_io && !(cond & G_IO_NVAL)) + g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); + + hid_device_remove(dev); + + return FALSE; +} + +static void bt_hid_notify_proto_mode(struct hid_device *dev, uint8_t *buf, + int len) +{ + struct hal_ev_hidhost_proto_mode ev; + char address[18]; + + ba2str(&dev->dst, address); + DBG("device %s", address); + + memset(&ev, 0, sizeof(ev)); + bdaddr2android(&dev->dst, ev.bdaddr); + + if (buf[0] == HID_MSG_DATA) { + ev.status = HAL_HIDHOST_STATUS_OK; + if (buf[1] == HID_PROTO_REPORT) + ev.mode = HAL_HIDHOST_REPORT_PROTOCOL; + else if (buf[1] == HID_PROTO_BOOT) + ev.mode = HAL_HIDHOST_BOOT_PROTOCOL; + else + ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL; + + } else { + ev.status = buf[0]; + ev.mode = HAL_HIDHOST_UNSUPPORTED_PROTOCOL; + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_PROTO_MODE, sizeof(ev), &ev); +} + +static void bt_hid_notify_get_report(struct hid_device *dev, uint8_t *buf, + int len) +{ + struct hal_ev_hidhost_get_report *ev; + int ev_len; + char address[18]; + + ba2str(&dev->dst, address); + DBG("device %s", address); + + ev_len = sizeof(*ev); + + if (!((buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_INPUT)) || + (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_OUTPUT)) || + (buf[0] == (HID_MSG_DATA | HID_DATA_TYPE_FEATURE)))) { + ev = g_malloc0(ev_len); + ev->status = buf[0]; + bdaddr2android(&dev->dst, ev->bdaddr); + goto send; + } + + /* + * Report porotocol mode reply contains id after hdr, in boot + * protocol mode id doesn't exist + */ + ev_len += (dev->boot_dev) ? (len - 1) : (len - 2); + ev = g_malloc0(ev_len); + ev->status = HAL_HIDHOST_STATUS_OK; + bdaddr2android(&dev->dst, ev->bdaddr); + + /* + * Report porotocol mode reply contains id after hdr, in boot + * protocol mode id doesn't exist + */ + if (dev->boot_dev) { + ev->len = len - 1; + memcpy(ev->data, buf + 1, ev->len); + } else { + ev->len = len - 2; + memcpy(ev->data, buf + 2, ev->len); + } + +send: + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_GET_REPORT, ev_len, ev); + g_free(ev); +} + +static void bt_hid_notify_virtual_unplug(struct hid_device *dev, + uint8_t *buf, int len) +{ + struct hal_ev_hidhost_virtual_unplug ev; + char address[18]; + + ba2str(&dev->dst, address); + DBG("device %s", address); + bdaddr2android(&dev->dst, ev.bdaddr); + + ev.status = HAL_HIDHOST_GENERAL_ERROR; + + /* Wait either channels to HUP */ + if (dev->intr_io && dev->ctrl_io) { + g_io_channel_shutdown(dev->intr_io, TRUE, NULL); + g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); + ev.status = HAL_HIDHOST_STATUS_OK; + } + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_EV_HIDHOST_VIRTUAL_UNPLUG, sizeof(ev), &ev); +} + +static gboolean ctrl_io_watch_cb(GIOChannel *chan, gpointer data) +{ + struct hid_device *dev = data; + int fd, bread; + uint8_t buf[UHID_DATA_MAX]; + + DBG(""); + + fd = g_io_channel_unix_get_fd(chan); + bread = read(fd, buf, sizeof(buf)); + if (bread < 0) { + error("hidhost: read from control failed: %s(%d)", + strerror(errno), -errno); + return TRUE; + } + + switch (dev->last_hid_msg) { + case HID_MSG_GET_PROTOCOL: + case HID_MSG_SET_PROTOCOL: + bt_hid_notify_proto_mode(dev, buf, bread); + break; + case HID_MSG_GET_REPORT: + bt_hid_notify_get_report(dev, buf, bread); + break; + } + + if (buf[0] == (HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG)) + bt_hid_notify_virtual_unplug(dev, buf, bread); + + /* reset msg type request */ + dev->last_hid_msg = 0; + + return TRUE; +} + +static gboolean ctrl_watch_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + struct hid_device *dev = data; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) + goto error; + + if (cond & G_IO_IN) + return ctrl_io_watch_cb(chan, data); + +error: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + + /* + * Checking for intr_watch avoids a double g_io_channel_shutdown since + * it's likely that intr_watch_cb has been queued for dispatching in + * this mainloop iteration + */ + if ((cond & (G_IO_HUP | G_IO_ERR)) && dev->intr_watch) + g_io_channel_shutdown(chan, TRUE, NULL); + + if (dev->intr_io && !(cond & G_IO_NVAL)) + g_io_channel_shutdown(dev->intr_io, TRUE, NULL); + + hid_device_remove(dev); + + return FALSE; +} + +static void bt_hid_set_info(struct hid_device *dev) +{ + struct hal_ev_hidhost_info ev; + + DBG(""); + + bdaddr2android(&dev->dst, ev.bdaddr); + ev.attr = 0; /* TODO: Check what is this field */ + ev.subclass = dev->subclass; + ev.app_id = 0; /* TODO: Check what is this field */ + ev.vendor = dev->vendor; + ev.product = dev->product; + ev.version = dev->version; + ev.country = dev->country; + ev.descr_len = dev->rd_size; + memset(ev.descr, 0, sizeof(ev.descr)); + memcpy(ev.descr, dev->rd_data, ev.descr_len); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_EV_HIDHOST_INFO, + sizeof(ev), &ev); +} + +static int uhid_create(struct hid_device *dev) +{ + struct uhid_event ev; + int err; + + dev->uhid = bt_uhid_new_default(); + if (!dev->uhid) { + err = -errno; + error("hidhost: Failed to create bt_uhid instance"); + return err; + } + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + strcpy((char *) ev.u.create.name, "bluez-input-device"); + ev.u.create.bus = BUS_BLUETOOTH; + ev.u.create.vendor = dev->vendor; + ev.u.create.product = dev->product; + ev.u.create.version = dev->version; + ev.u.create.country = dev->country; + ev.u.create.rd_size = dev->rd_size; + ev.u.create.rd_data = dev->rd_data; + + err = bt_uhid_send(dev->uhid, &ev); + if (err < 0) { + error("hidhost: Failed to create uHID device: %s", + strerror(-err)); + bt_uhid_unref(dev->uhid); + dev->uhid = NULL; + return err; + } + + bt_uhid_register(dev->uhid, UHID_OUTPUT, handle_uhid_output, dev); + bt_hid_set_info(dev); + + return 0; +} + +static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, + gpointer user_data) +{ + struct hid_device *dev = user_data; + uint8_t state; + + DBG(""); + + if (conn_err) { + error("hidhost: Failed to connect interrupt channel (%s)", + conn_err->message); + state = HAL_HIDHOST_STATE_FAILED; + goto failed; + } + + if (uhid_create(dev) < 0) { + state = HAL_HIDHOST_STATE_NO_HID; + goto failed; + } + + dev->intr_watch = g_io_add_watch(dev->intr_io, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + intr_watch_cb, dev); + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); + + return; + +failed: + bt_hid_notify_state(dev, state); + hid_device_remove(dev); +} + +static void control_connect_cb(GIOChannel *chan, GError *conn_err, + gpointer user_data) +{ + struct hid_device *dev = user_data; + GError *err = NULL; + + DBG(""); + + if (conn_err) { + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + error("hidhost: Failed to connect control channel (%s)", + conn_err->message); + goto failed; + } + + /* Connect to the HID interrupt channel */ + dev->intr_io = bt_io_connect(interrupt_connect_cb, dev, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, + BT_IO_OPT_SEC_LEVEL, dev->sec_level, + BT_IO_OPT_INVALID); + if (!dev->intr_io) { + error("hidhost: Failed to connect interrupt channel (%s)", + err->message); + g_error_free(err); + goto failed; + } + + dev->ctrl_watch = g_io_add_watch(dev->ctrl_io, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + ctrl_watch_cb, dev); + + return; + +failed: + hid_device_remove(dev); +} + +static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct hid_device *dev = data; + sdp_list_t *list; + GError *gerr = NULL; + + DBG(""); + + if (err < 0) { + error("hidhost: Unable to get SDP record: %s", strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + error("hidhost: No SDP records found"); + goto fail; + } + + for (list = recs; list != NULL; list = list->next) { + sdp_record_t *rec = list->data; + sdp_data_t *data; + + data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); + if (data) + dev->country = data->val.uint8; + + data = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); + if (data) { + dev->subclass = data->val.uint8; + + /* Encryption is mandatory for keyboards */ + if (dev->subclass & 0x40) + dev->sec_level = BT_IO_SEC_MEDIUM; + } + + data = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); + if (data) + dev->boot_dev = data->val.uint8; + + data = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); + if (data) { + if (!SDP_IS_SEQ(data->dtd)) + goto fail; + + /* First HIDDescriptor */ + data = data->val.dataseq; + if (!SDP_IS_SEQ(data->dtd)) + goto fail; + + /* ClassDescriptorType */ + data = data->val.dataseq; + if (data->dtd != SDP_UINT8) + goto fail; + + /* ClassDescriptorData */ + data = data->next; + if (!data || !SDP_IS_TEXT_STR(data->dtd)) + goto fail; + + dev->rd_size = data->unitSize; + dev->rd_data = g_memdup(data->val.str, data->unitSize); + } + } + + if (dev->ctrl_io) { + /* Raise the security level for this device if needed. */ + if ((dev->sec_level > BT_IO_SEC_LOW) && + !bt_io_set(dev->ctrl_io, &gerr, + BT_IO_OPT_SEC_LEVEL, dev->sec_level, + BT_IO_OPT_INVALID)) { + error("hidhost: Cannot raise security level: %s", + gerr->message); + g_error_free(gerr); + + goto fail; + } + + if (uhid_create(dev) < 0) + goto fail; + return; + } + + dev->ctrl_io = bt_io_connect(control_connect_cb, dev, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, + BT_IO_OPT_SEC_LEVEL, dev->sec_level, + BT_IO_OPT_INVALID); + if (gerr) { + error("hidhost: Failed to connect control channel (%s)", + gerr->message); + g_error_free(gerr); + goto fail; + } + + return; + +fail: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + hid_device_remove(dev); +} + +static void hid_sdp_did_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct hid_device *dev = data; + sdp_list_t *list; + uuid_t uuid; + + DBG(""); + + if (err < 0) { + error("hidhost: Unable to get Device ID SDP record: %s", + strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + error("hidhost: No Device ID SDP records found"); + goto fail; + } + + for (list = recs; list; list = list->next) { + sdp_record_t *rec = list->data; + sdp_data_t *data; + + data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); + if (data) + dev->vendor = data->val.uint16; + + data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); + if (data) + dev->product = data->val.uint16; + + data = sdp_data_get(rec, SDP_ATTR_VERSION); + if (data) + dev->version = data->val.uint16; + } + + sdp_uuid16_create(&uuid, HID_SVCLASS_ID); + if (bt_search_service(&adapter_addr, &dev->dst, &uuid, + hid_sdp_search_cb, dev, NULL, 0) < 0) { + error("hidhost: Failed to search SDP details"); + goto fail; + } + + return; + +fail: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + hid_device_remove(dev); +} + +static void hog_conn_cb(const bdaddr_t *addr, int err, void *attrib) +{ + GSList *l; + struct hid_device *dev; + + l = g_slist_find_custom(devices, addr, device_cmp); + dev = l ? l->data : NULL; + + if (err < 0) { + if (!dev) + return; + if (dev->hog) { + bt_hid_notify_state(dev, + HAL_HIDHOST_STATE_DISCONNECTED); + bt_hog_detach(dev->hog); + return; + } + goto fail; + } + + if (!dev) + dev = hid_device_new(addr); + + if (!dev->hog) { + /* TODO: Get device details and primary */ + dev->hog = bt_hog_new("bluez-input-device", dev->vendor, + dev->product, dev->version, NULL); + if (!dev->hog) { + error("HoG: unable to create session"); + goto fail; + } + } + + if (!bt_hog_attach(dev->hog, attrib)) { + error("HoG: unable to attach"); + goto fail; + } + + if (!bt_gatt_set_security(addr, BT_IO_SEC_MEDIUM)) { + error("Failed to set security level"); + goto fail; + } + + DBG(""); + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); + + if (!bt_gatt_add_autoconnect(hog_app, &dev->dst)) + error("hidhost: Could not add to autoconnect list"); + + return; + +fail: + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + hid_device_remove(dev); +} + +static bool hog_connect(struct hid_device *dev) +{ + DBG(""); + + if (hog_app) + return bt_gatt_connect_app(hog_app, &dev->dst); + + hog_app = bt_gatt_register_app(HOG_UUID, GATT_CLIENT, hog_conn_cb); + if (!hog_app) { + error("hidhost: bt_gatt_register_app failed"); + return false; + } + + return bt_gatt_connect_app(hog_app, &dev->dst); +} + +static void bt_hid_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_connect *cmd = buf; + struct hid_device *dev; + uint8_t status; + char addr[18]; + bdaddr_t dst; + GSList *l; + uuid_t uuid; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) + dev = l->data; + else + dev = hid_device_new(&dst); + + if (dev->state != HAL_HIDHOST_STATE_DISCONNECTED) + goto done; + + ba2str(&dev->dst, addr); + DBG("connecting to %s", addr); + + if (bt_is_device_le(&dst)) { + if (!hog_connect(dev)) { + status = HAL_STATUS_FAILED; + hid_device_remove(dev); + goto failed; + } + goto done; + } + + sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID); + if (bt_search_service(&adapter_addr, &dev->dst, &uuid, + hid_sdp_did_search_cb, dev, NULL, 0) < 0) { + error("hidhost: Failed to search DeviceID SDP details"); + hid_device_remove(dev); + status = HAL_STATUS_FAILED; + goto failed; + } + +done: + if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED) + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_CONNECT, + status); +} + +static bool hog_disconnect(struct hid_device *dev) +{ + DBG(""); + + if (dev->state == HAL_HIDHOST_STATE_DISCONNECTED) + return false; + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); + + if (!bt_gatt_disconnect_app(hog_app, &dev->dst)) { + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); + hid_device_remove(dev); + } + + return true; +} + +static void bt_hid_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_disconnect *cmd = buf; + struct hid_device *dev; + uint8_t status; + GSList *l; + bdaddr_t dst; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + if (bt_is_device_le(&dst)) { + if (!hog_disconnect(dev)) { + status = HAL_STATUS_FAILED; + goto failed; + } + goto done; + } + + /* Wait either channels to HUP */ + if (dev->intr_io) + g_io_channel_shutdown(dev->intr_io, TRUE, NULL); + + if (dev->ctrl_io) + g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); + + +done: + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_DISCONNECT, + status); +} + +static bool bt_hid_write_virtual_unplug(GIOChannel *chan) +{ + uint8_t hdr = HID_MSG_CONTROL | HID_VIRTUAL_CABLE_UNPLUG; + int fd = g_io_channel_unix_get_fd(chan); + + if (write(fd, &hdr, sizeof(hdr)) == sizeof(hdr)) + return true; + + error("hidhost: Error writing virtual unplug command: %s (%d)", + strerror(errno), errno); + return false; +} + +static void bt_hid_virtual_unplug(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_virtual_unplug *cmd = buf; + struct hid_device *dev; + GSList *l; + uint8_t status; + bdaddr_t dst; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + + if (!(dev->ctrl_io)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + if (!bt_hid_write_virtual_unplug(dev->ctrl_io)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + /* Wait either channels to HUP */ + if (dev->intr_io) + g_io_channel_shutdown(dev->intr_io, TRUE, NULL); + + if (dev->ctrl_io) + g_io_channel_shutdown(dev->ctrl_io, TRUE, NULL); + + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTING); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_VIRTUAL_UNPLUG, status); +} + +static void bt_hid_info(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_set_info *cmd = buf; + + if (len != sizeof(*cmd) + cmd->descr_len) { + error("Invalid hid set info size (%u bytes), terminating", len); + raise(SIGTERM); + return; + } + + /* + * Data from hal_cmd_hidhost_set_info is usefull only when we create + * UHID device. Once device is created all the transactions will be + * done through the fd. There is no way to use this information + * once device is created with HID internals. + */ + DBG("Not supported"); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_INFO, + HAL_STATUS_UNSUPPORTED); +} + +static void bt_hid_get_protocol(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_get_protocol *cmd = buf; + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int fd; + uint8_t hdr; + uint8_t status; + + DBG(""); + + switch (cmd->mode) { + case HAL_HIDHOST_REPORT_PROTOCOL: + case HAL_HIDHOST_BOOT_PROTOCOL: + break; + default: + status = HAL_STATUS_INVALID; + goto failed; + } + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + + hdr = HID_MSG_GET_PROTOCOL | cmd->mode; + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, &hdr, sizeof(hdr)) < 0) { + error("hidhost: Error writing device_get_protocol: %s (%d)", + strerror(errno), errno); + status = HAL_STATUS_FAILED; + goto failed; + } + + dev->last_hid_msg = HID_MSG_GET_PROTOCOL; + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_PROTOCOL, status); +} + +static void bt_hid_set_protocol(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_set_protocol *cmd = buf; + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int fd; + uint8_t hdr; + uint8_t status; + + DBG(""); + + switch (cmd->mode) { + case HAL_HIDHOST_REPORT_PROTOCOL: + case HAL_HIDHOST_BOOT_PROTOCOL: + break; + default: + status = HAL_STATUS_INVALID; + goto failed; + } + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + + hdr = HID_MSG_SET_PROTOCOL | cmd->mode; + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, &hdr, sizeof(hdr)) < 0) { + error("hidhost: error writing device_set_protocol: %s (%d)", + strerror(errno), errno); + status = HAL_STATUS_FAILED; + goto failed; + } + + dev->last_hid_msg = HID_MSG_SET_PROTOCOL; + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_PROTOCOL, status); +} + +static void bt_hid_get_report(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_get_report *cmd = buf; + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int fd; + uint8_t *req; + uint8_t req_size; + uint8_t status; + + DBG(""); + + switch (cmd->type) { + case HAL_HIDHOST_INPUT_REPORT: + case HAL_HIDHOST_OUTPUT_REPORT: + case HAL_HIDHOST_FEATURE_REPORT: + break; + default: + status = HAL_STATUS_INVALID; + goto failed; + } + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + req_size = (cmd->buf_size > 0) ? 4 : 2; + req = g_try_malloc0(req_size); + if (!req) { + status = HAL_STATUS_NOMEM; + goto failed; + } + + req[0] = HID_MSG_GET_REPORT | cmd->type; + req[1] = cmd->id; + + if (cmd->buf_size > 0) { + req[0] = req[0] | HID_GET_REPORT_SIZE_FIELD; + put_le16(cmd->buf_size, &req[2]); + } + + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, req, req_size) < 0) { + error("hidhost: error writing hid_get_report: %s (%d)", + strerror(errno), errno); + g_free(req); + status = HAL_STATUS_FAILED; + goto failed; + } + + dev->last_hid_msg = HID_MSG_GET_REPORT; + g_free(req); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_GET_REPORT, + status); +} + +static void bt_hid_set_report(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_set_report *cmd = buf; + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int fd; + uint8_t *req = NULL; + uint8_t req_size; + uint8_t status; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid hid set report size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + switch (cmd->type) { + case HAL_HIDHOST_INPUT_REPORT: + case HAL_HIDHOST_OUTPUT_REPORT: + case HAL_HIDHOST_FEATURE_REPORT: + break; + default: + status = HAL_STATUS_INVALID; + goto failed; + } + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + + if (!dev->ctrl_io && !dev->hog) { + status = HAL_STATUS_FAILED; + goto failed; + } + + req_size = 1 + (cmd->len / 2); + req = g_try_malloc0(req_size); + if (!req) { + status = HAL_STATUS_NOMEM; + goto failed; + } + + req[0] = HID_MSG_SET_REPORT | cmd->type; + /* + * Report data coming to HAL is in ascii format, HAL sends + * data in hex to daemon, so convert to binary. + */ + if (!hex2buf(cmd->data, req + 1, req_size - 1)) { + status = HAL_STATUS_INVALID; + goto failed; + } + + if (dev->hog) { + if (bt_hog_send_report(dev->hog, req + 1, req_size - 1, + cmd->type) < 0) { + status = HAL_STATUS_FAILED; + goto failed; + } + + goto done; + } + + fd = g_io_channel_unix_get_fd(dev->ctrl_io); + + if (write(fd, req, req_size) < 0) { + error("hidhost: error writing hid_set_report: %s (%d)", + strerror(errno), errno); + status = HAL_STATUS_FAILED; + goto failed; + } + + dev->last_hid_msg = HID_MSG_SET_REPORT; + +done: + status = HAL_STATUS_SUCCESS; + +failed: + g_free(req); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SET_REPORT, + status); +} + +static void bt_hid_send_data(const void *buf, uint16_t len) +{ + const struct hal_cmd_hidhost_send_data *cmd = buf; + struct hid_device *dev; + GSList *l; + bdaddr_t dst; + int fd; + uint8_t *req = NULL; + uint8_t req_size; + uint8_t status; + + DBG(""); + + if (len != sizeof(*cmd) + cmd->len) { + error("Invalid hid send data size (%u bytes), terminating", + len); + raise(SIGTERM); + return; + } + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + + if (!(dev->intr_io)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + req_size = 1 + (cmd->len / 2); + req = g_try_malloc0(req_size); + if (!req) { + status = HAL_STATUS_NOMEM; + goto failed; + } + + req[0] = HID_MSG_DATA | HID_DATA_TYPE_OUTPUT; + /* + * Report data coming to HAL is in ascii format, HAL sends + * data in hex to daemon, so convert to binary. + */ + if (!hex2buf(cmd->data, req + 1, req_size - 1)) { + status = HAL_STATUS_INVALID; + goto failed; + } + + fd = g_io_channel_unix_get_fd(dev->intr_io); + + if (write(fd, req, req_size) < 0) { + error("hidhost: error writing data to HID device: %s (%d)", + strerror(errno), errno); + status = HAL_STATUS_FAILED; + goto failed; + } + + status = HAL_STATUS_SUCCESS; + +failed: + g_free(req); + + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_HIDHOST, HAL_OP_HIDHOST_SEND_DATA, + status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_HIDHOST_CONNECT */ + { bt_hid_connect, false, sizeof(struct hal_cmd_hidhost_connect) }, + /* HAL_OP_HIDHOST_DISCONNECT */ + { bt_hid_disconnect, false, sizeof(struct hal_cmd_hidhost_disconnect) }, + /* HAL_OP_HIDHOST_VIRTUAL_UNPLUG */ + { bt_hid_virtual_unplug, false, + sizeof(struct hal_cmd_hidhost_virtual_unplug) }, + /* HAL_OP_HIDHOST_SET_INFO */ + { bt_hid_info, true, sizeof(struct hal_cmd_hidhost_set_info) }, + /* HAL_OP_HIDHOST_GET_PROTOCOL */ + { bt_hid_get_protocol, false, + sizeof(struct hal_cmd_hidhost_get_protocol) }, + /* HAL_OP_HIDHOST_SET_PROTOCOL */ + { bt_hid_set_protocol, false, + sizeof(struct hal_cmd_hidhost_get_protocol) }, + /* HAL_OP_HIDHOST_GET_REPORT */ + { bt_hid_get_report, false, sizeof(struct hal_cmd_hidhost_get_report) }, + /* HAL_OP_HIDHOST_SET_REPORT */ + { bt_hid_set_report, true, sizeof(struct hal_cmd_hidhost_set_report) }, + /* HAL_OP_HIDHOST_SEND_DATA */ + { bt_hid_send_data, true, sizeof(struct hal_cmd_hidhost_send_data) }, +}; + +static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + struct hid_device *dev; + bdaddr_t dst; + char address[18]; + uint16_t psm; + GError *gerr = NULL; + GSList *l; + uuid_t uuid; + + if (err) { + error("hidhost: Connect failed (%s)", err->message); + return; + } + + bt_io_get(chan, &gerr, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_PSM, &psm, + BT_IO_OPT_INVALID); + if (gerr) { + error("hidhost: Failed to read remote address (%s)", + gerr->message); + g_io_channel_shutdown(chan, TRUE, NULL); + g_error_free(gerr); + return; + } + + ba2str(&dst, address); + DBG("Incoming connection from %s on PSM %d", address, psm); + + if (!bt_device_is_bonded(&dst)) { + warn("hidhost: Rejecting connection from unknown device %s", + address); + if (psm == L2CAP_PSM_HIDP_CTRL) + bt_hid_write_virtual_unplug(chan); + + g_io_channel_shutdown(chan, TRUE, NULL); + return; + } + + switch (psm) { + case L2CAP_PSM_HIDP_CTRL: + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) + return; + + dev = hid_device_new(&dst); + dev->ctrl_io = g_io_channel_ref(chan); + + sdp_uuid16_create(&uuid, PNP_INFO_SVCLASS_ID); + if (bt_search_service(&adapter_addr, &dev->dst, &uuid, + hid_sdp_did_search_cb, dev, NULL, 0) < 0) { + error("hidhost: Failed to search DID SDP details"); + hid_device_remove(dev); + return; + } + + dev->ctrl_watch = g_io_add_watch(dev->ctrl_io, + G_IO_HUP | G_IO_ERR | G_IO_NVAL, + ctrl_watch_cb, dev); + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTING); + break; + + case L2CAP_PSM_HIDP_INTR: + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) + return; + + dev = l->data; + dev->intr_io = g_io_channel_ref(chan); + dev->intr_watch = g_io_add_watch(dev->intr_io, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + intr_watch_cb, dev); + bt_hid_notify_state(dev, HAL_HIDHOST_STATE_CONNECTED); + break; + } +} + +static void hid_unpaired_cb(const bdaddr_t *addr, uint8_t type) +{ + GSList *l; + struct hid_device *dev; + char address[18]; + + l = g_slist_find_custom(devices, addr, device_cmp); + if (!l) + return; + + dev = l->data; + + ba2str(addr, address); + DBG("Unpaired device %s", address); + + if (hog_app) + bt_gatt_remove_autoconnect(hog_app, addr); + + hid_device_remove(dev); +} + +bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + GError *err = NULL; + + DBG(""); + + if (!bt_unpaired_register(hid_unpaired_cb)) { + error("hidhost: Could not register unpaired callback"); + return false; + } + + bacpy(&adapter_addr, addr); + + ctrl_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!ctrl_io) { + error("hidhost: Failed to listen on control channel: %s", + err->message); + g_error_free(err); + return false; + } + + intr_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!intr_io) { + error("hidhost: Failed to listen on interrupt channel: %s", + err->message); + g_error_free(err); + + g_io_channel_shutdown(ctrl_io, TRUE, NULL); + g_io_channel_unref(ctrl_io); + ctrl_io = NULL; + + return false; + } + + hal_ipc = ipc; + + ipc_register(hal_ipc, HAL_SERVICE_ID_HIDHOST, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + return true; +} + +void bt_hid_unregister(void) +{ + DBG(""); + + if (hog_app > 0) + bt_gatt_unregister_app(hog_app); + + g_slist_free_full(devices, hid_device_free); + devices = NULL; + + if (ctrl_io) { + g_io_channel_shutdown(ctrl_io, TRUE, NULL); + g_io_channel_unref(ctrl_io); + ctrl_io = NULL; + } + + if (intr_io) { + g_io_channel_shutdown(intr_io, TRUE, NULL); + g_io_channel_unref(intr_io); + intr_io = NULL; + } + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_HIDHOST); + hal_ipc = NULL; + + bt_unpaired_unregister(hid_unpaired_cb); +} diff -Nru bluez-4.101/android/hidhost.h bluez-5.23/android/hidhost.h --- bluez-4.101/android/hidhost.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hidhost.h 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +bool bt_hid_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_hid_unregister(void); diff -Nru bluez-4.101/android/hog.c bluez-5.23/android/hog.c --- bluez-4.101/android/hog.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hog.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,1136 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. + * Copyright (C) 2012 Marcel Holtmann + * Copyright (C) 2012 Nordic Semiconductor Inc. + * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "src/log.h" + +#include "lib/uuid.h" +#include "src/shared/util.h" +#include "src/shared/uhid.h" + +#include "attrib/att.h" +#include "attrib/gattrib.h" +#include "attrib/gatt.h" + +#include "btio/btio.h" + +#include "android/scpp.h" +#include "android/dis.h" +#include "android/bas.h" +#include "android/hog.h" + +#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb" + +#define HOG_INFO_UUID 0x2A4A +#define HOG_REPORT_MAP_UUID 0x2A4B +#define HOG_REPORT_UUID 0x2A4D +#define HOG_PROTO_MODE_UUID 0x2A4E +#define HOG_CONTROL_POINT_UUID 0x2A4C + +#define HOG_REPORT_TYPE_INPUT 1 +#define HOG_REPORT_TYPE_OUTPUT 2 +#define HOG_REPORT_TYPE_FEATURE 3 + +#define HOG_PROTO_MODE_BOOT 0 +#define HOG_PROTO_MODE_REPORT 1 + +#define HOG_REPORT_MAP_MAX_SIZE 512 +#define HID_INFO_SIZE 4 +#define ATT_NOTIFICATION_HEADER_SIZE 3 + +struct bt_hog { + int ref_count; + char *name; + uint16_t vendor; + uint16_t product; + uint16_t version; + struct gatt_primary *primary; + GAttrib *attrib; + GSList *reports; + struct bt_uhid *uhid; + gboolean has_report_id; + uint16_t bcdhid; + uint8_t bcountrycode; + uint16_t proto_mode_handle; + uint16_t ctrlpt_handle; + uint8_t flags; + struct bt_scpp *scpp; + struct bt_dis *dis; + struct bt_bas *bas; + GSList *instances; +}; + +struct report { + struct bt_hog *hog; + uint8_t id; + uint8_t type; + uint16_t ccc_handle; + guint notifyid; + struct gatt_char *decl; + uint16_t len; + uint8_t *value; +}; + +static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data) +{ + struct report *report = user_data; + struct bt_hog *hog = report->hog; + struct uhid_event ev; + uint8_t *buf; + int err; + + if (len < ATT_NOTIFICATION_HEADER_SIZE) { + error("Malformed ATT notification"); + return; + } + + pdu += ATT_NOTIFICATION_HEADER_SIZE; + len -= ATT_NOTIFICATION_HEADER_SIZE; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + buf = ev.u.input.data; + + if (hog->has_report_id) { + buf[0] = report->id; + len = MIN(len, sizeof(ev.u.input.data) - 1); + memcpy(buf + 1, pdu, len); + ev.u.input.size = ++len; + } else { + len = MIN(len, sizeof(ev.u.input.data)); + memcpy(buf, pdu, len); + ev.u.input.size = len; + } + + err = bt_uhid_send(hog->uhid, &ev); + if (err < 0) { + error("bt_uhid_send: %s (%d)", strerror(-err), -err); + return; + } + + DBG("HoG report (%u bytes)", ev.u.input.size); +} + +static void report_ccc_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct report *report = user_data; + struct bt_hog *hog = report->hog; + + if (status != 0) { + error("Write report characteristic descriptor failed: %s", + att_ecode2str(status)); + return; + } + + report->notifyid = g_attrib_register(hog->attrib, + ATT_OP_HANDLE_NOTIFY, + report->decl->value_handle, + report_value_cb, report, NULL); + + DBG("Report characteristic descriptor written: notifications enabled"); +} + +static void write_ccc(GAttrib *attrib, uint16_t handle, void *user_data) +{ + uint8_t value[2]; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + + gatt_write_char(attrib, handle, value, sizeof(value), + report_ccc_written_cb, user_data); +} + +static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct report *report = user_data; + + if (status != 0) { + error("Error reading CCC value: %s", att_ecode2str(status)); + return; + } + + write_ccc(report->hog->attrib, report->ccc_handle, report); +} + +static void report_reference_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct report *report = user_data; + + if (status != 0) { + error("Read Report Reference descriptor failed: %s", + att_ecode2str(status)); + return; + } + + if (plen != 3) { + error("Malformed ATT read response"); + return; + } + + report->id = pdu[1]; + report->type = pdu[2]; + DBG("Report ID: 0x%02x Report type: 0x%02x", pdu[1], pdu[2]); + + /* Enable notifications only for Input Reports */ + if (report->type == HOG_REPORT_TYPE_INPUT) + gatt_read_char(report->hog->attrib, report->ccc_handle, + ccc_read_cb, report); +} + +static void external_report_reference_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data); + +static void discover_external_cb(uint8_t status, GSList *descs, void *user_data) +{ + struct bt_hog *hog = user_data; + + if (status != 0) { + error("Discover external descriptors failed: %s", + att_ecode2str(status)); + return; + } + + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; + + gatt_read_char(hog->attrib, desc->handle, + external_report_reference_cb, hog); + } +} + +static void discover_external(GAttrib *attrib, uint16_t start, uint16_t end, + gpointer user_data) +{ + bt_uuid_t uuid; + + if (start > end) + return; + + bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE); + + gatt_discover_desc(attrib, start, end, NULL, discover_external_cb, + user_data); +} + +static void discover_report_cb(uint8_t status, GSList *descs, void *user_data) +{ + struct report *report = user_data; + struct bt_hog *hog = report->hog; + + if (status != 0) { + error("Discover report descriptors failed: %s", + att_ecode2str(status)); + return; + } + + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; + + switch (desc->uuid16) { + case GATT_CLIENT_CHARAC_CFG_UUID: + report->ccc_handle = desc->handle; + break; + case GATT_REPORT_REFERENCE: + gatt_read_char(hog->attrib, desc->handle, + report_reference_cb, report); + break; + } + } +} + +static void discover_report(GAttrib *attrib, uint16_t start, uint16_t end, + gpointer user_data) +{ + if (start > end) + return; + + gatt_discover_desc(attrib, start, end, NULL, discover_report_cb, + user_data); +} + +static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct report *report = user_data; + + if (status != 0) { + error("Error reading Report value: %s", att_ecode2str(status)); + return; + } + + if (report->value) + g_free(report->value); + + report->value = g_memdup(pdu, len); + report->len = len; +} + +static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr) +{ + struct report *report; + + report = g_new0(struct report, 1); + report->hog = hog; + report->decl = g_memdup(chr, sizeof(*chr)); + hog->reports = g_slist_append(hog->reports, report); + + gatt_read_char(hog->attrib, chr->value_handle, report_read_cb, report); + + return report; +} + +static void external_service_char_cb(uint8_t status, GSList *chars, + void *user_data) +{ + struct bt_hog *hog = user_data; + struct gatt_primary *primary = hog->primary; + struct report *report; + GSList *l; + + if (status != 0) { + const char *str = att_ecode2str(status); + DBG("Discover external service characteristic failed: %s", str); + return; + } + + for (l = chars; l; l = g_slist_next(l)) { + struct gatt_char *chr, *next; + uint16_t start, end; + + chr = l->data; + next = l->next ? l->next->data : NULL; + + DBG("0x%04x UUID: %s properties: %02x", + chr->handle, chr->uuid, chr->properties); + + report = report_new(hog, chr); + start = chr->value_handle + 1; + end = (next ? next->handle - 1 : primary->range.end); + discover_report(hog->attrib, start, end, report); + } +} + +static void external_report_reference_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct bt_hog *hog = user_data; + uint16_t uuid16; + bt_uuid_t uuid; + + if (status != 0) { + error("Read External Report Reference descriptor failed: %s", + att_ecode2str(status)); + return; + } + + if (plen != 3) { + error("Malformed ATT read response"); + return; + } + + uuid16 = get_le16(&pdu[1]); + DBG("External report reference read, external report characteristic " + "UUID: 0x%04x", uuid16); + + /* Do not discover if is not a Report */ + if (uuid16 != HOG_REPORT_UUID) + return; + + bt_uuid16_create(&uuid, uuid16); + gatt_discover_char(hog->attrib, 0x0001, 0xffff, &uuid, + external_service_char_cb, hog); +} + + +static int report_cmp(gconstpointer a, gconstpointer b) +{ + const struct report *ra = a, *rb = b; + + /* sort by type first.. */ + if (ra->type != rb->type) + return ra->type - rb->type; + + /* skip id check in case of report id 0 */ + if (!rb->id) + return 0; + + /* ..then by id */ + return ra->id - rb->id; +} + +static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id) +{ + struct report cmp; + GSList *l; + + switch (type) { + case UHID_FEATURE_REPORT: + cmp.type = HOG_REPORT_TYPE_FEATURE; + break; + case UHID_OUTPUT_REPORT: + cmp.type = HOG_REPORT_TYPE_OUTPUT; + break; + case UHID_INPUT_REPORT: + cmp.type = HOG_REPORT_TYPE_INPUT; + break; + default: + return NULL; + } + + cmp.id = hog->has_report_id ? id : 0; + + l = g_slist_find_custom(hog->reports, &cmp, report_cmp); + + return l ? l->data : NULL; +} + +static void output_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + if (status != 0) { + error("Write output report failed: %s", att_ecode2str(status)); + return; + } +} + +static void forward_report(struct uhid_event *ev, void *user_data) +{ + struct bt_hog *hog = user_data; + struct report *report; + void *data; + int size; + + report = find_report(hog, ev->u.output.rtype, ev->u.output.data[0]); + if (!report) + return; + + data = ev->u.output.data; + size = ev->u.output.size; + if (hog->has_report_id && size > 0) { + data++; + --size; + } + + DBG("Sending report type %d ID %d to handle 0x%X", report->type, + report->id, report->decl->value_handle); + + if (hog->attrib == NULL) + return; + + if (report->decl->properties & GATT_CHR_PROP_WRITE) + gatt_write_char(hog->attrib, report->decl->value_handle, + data, size, output_written_cb, hog); + else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP) + gatt_write_cmd(hog->attrib, report->decl->value_handle, + data, size, NULL, NULL); +} + +static void get_report(struct uhid_event *ev, void *user_data) +{ + struct bt_hog *hog = user_data; + struct report *report; + struct uhid_event rsp; + int err; + + memset(&rsp, 0, sizeof(rsp)); + rsp.type = UHID_FEATURE_ANSWER; + rsp.u.feature_answer.id = ev->u.feature.id; + + report = find_report(hog, ev->u.feature.rtype, ev->u.feature.rnum); + if (!report) { + rsp.u.feature_answer.err = ENOTSUP; + goto done; + } + + if (!report->value) { + rsp.u.feature_answer.err = EIO; + goto done; + } + + rsp.u.feature_answer.size = report->len; + memcpy(rsp.u.feature_answer.data, report->value, report->len); + +done: + err = bt_uhid_send(hog->uhid, &rsp); + if (err < 0) + error("bt_uhid_send: %s", strerror(-err)); +} + +static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len, + bool *is_long) +{ + if (!blen) + return false; + + *is_long = (buf[0] == 0xfe); + + if (*is_long) { + if (blen < 3) + return false; + + /* + * long item: + * byte 0 -> 0xFE + * byte 1 -> data size + * byte 2 -> tag + * + data + */ + + *len = buf[1] + 3; + } else { + uint8_t b_size; + + /* + * short item: + * byte 0[1..0] -> data size (=0, 1, 2, 4) + * byte 0[3..2] -> type + * byte 0[7..4] -> tag + * + data + */ + + b_size = buf[0] & 0x03; + *len = (b_size ? 1 << (b_size - 1) : 0) + 1; + } + + /* item length should be no more than input buffer length */ + return *len <= blen; +} + +static char *item2string(char *str, uint8_t *buf, uint8_t len) +{ + char *p = str; + int i; + + /* + * Since long item tags are not defined except for vendor ones, we + * just ensure that short items are printed properly (up to 5 bytes). + */ + for (i = 0; i < 6 && i < len; i++) + p += sprintf(p, " %02x", buf[i]); + + /* + * If there are some data left, just add continuation mark to indicate + * this. + */ + if (i < len) + sprintf(p, " ..."); + + return str; +} + +static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + struct bt_hog *hog = user_data; + uint8_t value[HOG_REPORT_MAP_MAX_SIZE]; + struct uhid_event ev; + ssize_t vlen; + char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */ + int i, err; + GError *gerr = NULL; + + if (status != 0) { + error("Report Map read failed: %s", att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, plen, value, sizeof(value)); + if (vlen < 0) { + error("ATT protocol error"); + return; + } + + DBG("Report MAP:"); + for (i = 0; i < vlen;) { + ssize_t ilen = 0; + bool long_item = false; + + if (get_descriptor_item_info(&value[i], vlen - i, &ilen, + &long_item)) { + /* Report ID is short item with prefix 100001xx */ + if (!long_item && (value[i] & 0xfc) == 0x84) + hog->has_report_id = TRUE; + + DBG("\t%s", item2string(itemstr, &value[i], ilen)); + + i += ilen; + } else { + error("Report Map parsing failed at %d", i); + + /* Just print remaining items at once and break */ + DBG("\t%s", item2string(itemstr, &value[i], vlen - i)); + break; + } + } + + /* create uHID device */ + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + + bt_io_get(g_attrib_get_channel(hog->attrib), &gerr, + BT_IO_OPT_SOURCE, ev.u.create.phys, + BT_IO_OPT_DEST, ev.u.create.uniq, + BT_IO_OPT_INVALID); + if (gerr) { + error("Failed to connection details: %s", gerr->message); + g_error_free(gerr); + return; + } + + strcpy((char *) ev.u.create.name, hog->name); + ev.u.create.vendor = hog->vendor; + ev.u.create.product = hog->product; + ev.u.create.version = hog->version; + ev.u.create.country = hog->bcountrycode; + ev.u.create.bus = BUS_BLUETOOTH; + ev.u.create.rd_data = value; + ev.u.create.rd_size = vlen; + + err = bt_uhid_send(hog->uhid, &ev); + if (err < 0) { + error("bt_uhid_send: %s", strerror(-err)); + return; + } + + bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog); + bt_uhid_register(hog->uhid, UHID_FEATURE, get_report, hog); +} + +static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + struct bt_hog *hog = user_data; + uint8_t value[HID_INFO_SIZE]; + ssize_t vlen; + + if (status != 0) { + error("HID Information read failed: %s", + att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, plen, value, sizeof(value)); + if (vlen != 4) { + error("ATT protocol error"); + return; + } + + hog->bcdhid = get_le16(&value[0]); + hog->bcountrycode = value[2]; + hog->flags = value[3]; + + DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X", + hog->bcdhid, hog->bcountrycode, hog->flags); +} + +static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + struct bt_hog *hog = user_data; + uint8_t value; + ssize_t vlen; + + if (status != 0) { + error("Protocol Mode characteristic read failed: %s", + att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, plen, &value, sizeof(value)); + if (vlen < 0) { + error("ATT protocol error"); + return; + } + + if (value == HOG_PROTO_MODE_BOOT) { + uint8_t nval = HOG_PROTO_MODE_REPORT; + + DBG("HoG is operating in Boot Procotol Mode"); + + gatt_write_cmd(hog->attrib, hog->proto_mode_handle, &nval, + sizeof(nval), NULL, NULL); + } else if (value == HOG_PROTO_MODE_REPORT) + DBG("HoG is operating in Report Protocol Mode"); +} + +static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct bt_hog *hog = user_data; + struct gatt_primary *primary = hog->primary; + bt_uuid_t report_uuid, report_map_uuid, info_uuid; + bt_uuid_t proto_mode_uuid, ctrlpt_uuid; + struct report *report; + GSList *l; + uint16_t info_handle = 0, proto_mode_handle = 0; + + if (status != 0) { + const char *str = att_ecode2str(status); + DBG("Discover all characteristics failed: %s", str); + return; + } + + bt_uuid16_create(&report_uuid, HOG_REPORT_UUID); + bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID); + bt_uuid16_create(&info_uuid, HOG_INFO_UUID); + bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID); + bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID); + + for (l = chars; l; l = g_slist_next(l)) { + struct gatt_char *chr, *next; + bt_uuid_t uuid; + uint16_t start, end; + + chr = l->data; + next = l->next ? l->next->data : NULL; + + DBG("0x%04x UUID: %s properties: %02x", + chr->handle, chr->uuid, chr->properties); + + bt_string_to_uuid(&uuid, chr->uuid); + + start = chr->value_handle + 1; + end = (next ? next->handle - 1 : primary->range.end); + + if (bt_uuid_cmp(&uuid, &report_uuid) == 0) { + report = report_new(hog, chr); + discover_report(hog->attrib, start, end, report); + } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) { + gatt_read_char(hog->attrib, chr->value_handle, + report_map_read_cb, hog); + discover_external(hog->attrib, start, end, hog); + } else if (bt_uuid_cmp(&uuid, &info_uuid) == 0) + info_handle = chr->value_handle; + else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0) + proto_mode_handle = chr->value_handle; + else if (bt_uuid_cmp(&uuid, &ctrlpt_uuid) == 0) + hog->ctrlpt_handle = chr->value_handle; + } + + if (proto_mode_handle) { + hog->proto_mode_handle = proto_mode_handle; + gatt_read_char(hog->attrib, proto_mode_handle, + proto_mode_read_cb, hog); + } + + if (info_handle) + gatt_read_char(hog->attrib, info_handle, info_read_cb, hog); +} + +static void report_free(void *data) +{ + struct report *report = data; + + g_free(report->value); + g_free(report->decl); + g_free(report); +} + +static void hog_free(void *data) +{ + struct bt_hog *hog = data; + + bt_hog_detach(hog); + + g_slist_free_full(hog->instances, hog_free); + + bt_scpp_unref(hog->scpp); + bt_dis_unref(hog->dis); + bt_bas_unref(hog->bas); + bt_uhid_unref(hog->uhid); + g_slist_free_full(hog->reports, report_free); + g_free(hog->name); + g_free(hog->primary); + g_free(hog); +} + +struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product, + uint16_t version, void *primary) +{ + struct bt_hog *hog; + + hog = g_try_new0(struct bt_hog, 1); + if (!hog) + return NULL; + + hog->uhid = bt_uhid_new_default(); + if (!hog->uhid) { + hog_free(hog); + return NULL; + } + + hog->name = g_strdup(name); + hog->vendor = vendor; + hog->product = product; + hog->version = version; + + if (primary) + hog->primary = g_memdup(primary, sizeof(*hog->primary)); + + return bt_hog_ref(hog); +} + +struct bt_hog *bt_hog_ref(struct bt_hog *hog) +{ + if (!hog) + return NULL; + + __sync_fetch_and_add(&hog->ref_count, 1); + + return hog; +} + +void bt_hog_unref(struct bt_hog *hog) +{ + if (!hog) + return; + + if (__sync_sub_and_fetch(&hog->ref_count, 1)) + return; + + hog_free(hog); +} + +static void find_included_cb(uint8_t status, GSList *services, void *user_data) +{ + struct bt_hog *hog = user_data; + struct gatt_included *include; + GSList *l; + + DBG(""); + + if (hog->primary) + return; + + if (status) { + const char *str = att_ecode2str(status); + DBG("Find included failed: %s", str); + return; + } + + if (!services) { + DBG("No included service found"); + return; + } + + for (l = services; l; l = l->next) { + include = l->data; + + if (strcmp(include->uuid, HOG_UUID) == 0) + break; + } + + if (!l) { + for (l = services; l; l = l->next) { + include = l->data; + + gatt_find_included(hog->attrib, include->range.start, + include->range.end, find_included_cb, hog); + } + return; + } + + hog->primary = g_new0(struct gatt_primary, 1); + memcpy(hog->primary->uuid, include->uuid, sizeof(include->uuid)); + memcpy(&hog->primary->range, &include->range, sizeof(include->range)); + + gatt_discover_char(hog->attrib, hog->primary->range.start, + hog->primary->range.end, NULL, + char_discovered_cb, hog); +} + +static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary) +{ + if (hog->scpp) { + bt_scpp_attach(hog->scpp, hog->attrib); + return; + } + + hog->scpp = bt_scpp_new(primary); + if (hog->scpp) + bt_scpp_attach(hog->scpp, hog->attrib); +} + +static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product, + uint16_t version, void *user_data) +{ + struct bt_hog *hog = user_data; + + hog->vendor = vendor; + hog->product = product; + hog->version = version; +} + +static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary) +{ + if (hog->dis) { + bt_dis_attach(hog->dis, hog->attrib); + return; + } + + hog->dis = bt_dis_new(primary); + if (hog->dis) { + bt_dis_set_notification(hog->dis, dis_notify, hog); + bt_dis_attach(hog->dis, hog->attrib); + } +} + +static void hog_attach_bas(struct bt_hog *hog, struct gatt_primary *primary) +{ + if (hog->bas) { + bt_bas_attach(hog->bas, hog->attrib); + return; + } + + hog->bas = bt_bas_new(primary); + if (hog->bas) + bt_bas_attach(hog->bas, hog->attrib); +} + +static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary) +{ + struct bt_hog *instance; + + if (!hog->primary) { + hog->primary = g_memdup(primary, sizeof(*primary)); + gatt_discover_char(hog->attrib, primary->range.start, + primary->range.end, NULL, + char_discovered_cb, hog); + return; + } + + instance = bt_hog_new(hog->name, hog->vendor, hog->product, + hog->version, primary); + if (!instance) + return; + + bt_hog_attach(instance, hog->attrib); + hog->instances = g_slist_append(hog->instances, instance); +} + +static void primary_cb(uint8_t status, GSList *services, void *user_data) +{ + struct bt_hog *hog = user_data; + struct gatt_primary *primary; + GSList *l; + + DBG(""); + + if (status) { + const char *str = att_ecode2str(status); + DBG("Discover primary failed: %s", str); + return; + } + + if (!services) { + DBG("No primary service found"); + return; + } + + for (l = services; l; l = l->next) { + primary = l->data; + + if (strcmp(primary->uuid, SCAN_PARAMETERS_UUID) == 0) { + hog_attach_scpp(hog, primary); + continue; + } + + if (strcmp(primary->uuid, DEVICE_INFORMATION_UUID) == 0) { + hog_attach_dis(hog, primary); + continue; + } + + if (strcmp(primary->uuid, BATTERY_UUID) == 0) { + hog_attach_bas(hog, primary); + continue; + } + + if (strcmp(primary->uuid, HOG_UUID) == 0) + hog_attach_hog(hog, primary); + } + + if (hog->primary) + return; + + for (l = services; l; l = l->next) { + primary = l->data; + + gatt_find_included(hog->attrib, primary->range.start, + primary->range.end, find_included_cb, + hog); + } +} + +bool bt_hog_attach(struct bt_hog *hog, void *gatt) +{ + struct gatt_primary *primary = hog->primary; + GSList *l; + + if (hog->attrib) + return false; + + hog->attrib = g_attrib_ref(gatt); + + if (!primary) { + gatt_discover_primary(hog->attrib, NULL, primary_cb, hog); + return true; + } + + if (hog->scpp) + bt_scpp_attach(hog->scpp, gatt); + + if (hog->dis) + bt_dis_attach(hog->dis, gatt); + + if (hog->bas) + bt_bas_attach(hog->bas, gatt); + + for (l = hog->instances; l; l = l->next) { + struct bt_hog *instance = l->data; + + bt_hog_attach(instance, gatt); + } + + if (hog->reports == NULL) { + gatt_discover_char(hog->attrib, primary->range.start, + primary->range.end, NULL, + char_discovered_cb, hog); + return true; + } + + for (l = hog->reports; l; l = l->next) { + struct report *r = l->data; + + r->notifyid = g_attrib_register(hog->attrib, + ATT_OP_HANDLE_NOTIFY, + r->decl->value_handle, + report_value_cb, r, NULL); + } + + return true; +} + +void bt_hog_detach(struct bt_hog *hog) +{ + GSList *l; + + if (!hog->attrib) + return; + + for (l = hog->instances; l; l = l->next) { + struct bt_hog *instance = l->data; + + bt_hog_detach(instance); + } + + for (l = hog->reports; l; l = l->next) { + struct report *r = l->data; + + if (r->notifyid > 0) { + g_attrib_unregister(hog->attrib, r->notifyid); + r->notifyid = 0; + } + } + + if (hog->scpp) + bt_scpp_detach(hog->scpp); + + if (hog->dis) + bt_dis_detach(hog->dis); + + if (hog->bas) + bt_bas_detach(hog->bas); + + g_attrib_unref(hog->attrib); + hog->attrib = NULL; +} + +int bt_hog_set_control_point(struct bt_hog *hog, bool suspend) +{ + uint8_t value = suspend ? 0x00 : 0x01; + + if (hog->attrib == NULL) + return -ENOTCONN; + + if (hog->ctrlpt_handle == 0) + return -ENOTSUP; + + gatt_write_cmd(hog->attrib, hog->ctrlpt_handle, &value, + sizeof(value), NULL, NULL); + + return 0; +} + +int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type) +{ + struct report *report; + GSList *l; + + if (!hog) + return -EINVAL; + + if (!hog->attrib) + return -ENOTCONN; + + report = find_report(hog, type, 0); + if (!report) + return -ENOTSUP; + + DBG("hog: Write report, handle 0x%X", report->decl->value_handle); + + if (report->decl->properties & GATT_CHR_PROP_WRITE) + gatt_write_char(hog->attrib, report->decl->value_handle, + data, size, output_written_cb, hog); + + if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP) + gatt_write_cmd(hog->attrib, report->decl->value_handle, + data, size, NULL, NULL); + + for (l = hog->instances; l; l = l->next) { + struct bt_hog *instance = l->data; + + bt_hog_send_report(instance, data, size, type); + } + + return 0; +} diff -Nru bluez-4.101/android/hog.h bluez-5.23/android/hog.h --- bluez-4.101/android/hog.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/hog.h 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct bt_hog; + +struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product, + uint16_t version, void *primary); + +struct bt_hog *bt_hog_ref(struct bt_hog *hog); +void bt_hog_unref(struct bt_hog *hog); + +bool bt_hog_attach(struct bt_hog *hog, void *gatt); +void bt_hog_detach(struct bt_hog *hog); + +int bt_hog_set_control_point(struct bt_hog *hog, bool suspend); +int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type); diff -Nru bluez-4.101/android/init.bluetooth.rc bluez-5.23/android/init.bluetooth.rc --- bluez-4.101/android/init.bluetooth.rc 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/init.bluetooth.rc 2014-03-25 20:53:41.000000000 +0000 @@ -0,0 +1,38 @@ +# required permissions +on boot + chown bluetooth bluetooth /data/misc/bluetooth + chown bluetooth bluetooth /dev/uhid + chown system bluetooth /dev/uinput + +# services +on property:bluetooth.start=daemon + setprop bluetooth.start none + start bluetoothd + +on property:bluetooth.stop=daemon + setprop bluetooth.stop none + stop bluetoothd + +on property:bluetooth.start=snoop + setprop bluetooth.start none + start bluetoothd-snoop + +on property:bluetooth.stop=snoop + setprop bluetooth.stop none + stop bluetoothd-snoop + +service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd + class main + # init does not yet support setting capabilities so run as root, + # bluetoothd drop uid to bluetooth with the right linux capabilities + group bluetooth + disabled + oneshot + +service bluetoothd-snoop /system/bin/logwrapper /system/bin/bluetoothd-snoop + class main + # init does not yet support setting capabilities so run as root, + # bluetoothd-snoop drops unneeded linux capabilities + group nobody + disabled + oneshot diff -Nru bluez-4.101/android/ipc.c bluez-5.23/android/ipc.c --- bluez-4.101/android/ipc.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/ipc.c 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,437 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipc-common.h" +#include "ipc.h" +#include "src/log.h" + +struct service_handler { + const struct ipc_handler *handler; + uint8_t size; +}; + +struct ipc { + struct service_handler *services; + int service_max; + + const char *path; + size_t size; + + GIOChannel *cmd_io; + guint cmd_watch; + + bool notifications; + GIOChannel *notif_io; + guint notif_watch; + + ipc_disconnect_cb disconnect_cb; + void *disconnect_cb_data; +}; + +static void ipc_disconnect(struct ipc *ipc, bool in_cleanup) +{ + if (ipc->cmd_watch) { + g_source_remove(ipc->cmd_watch); + ipc->cmd_watch = 0; + } + + if (ipc->cmd_io) { + g_io_channel_shutdown(ipc->cmd_io, TRUE, NULL); + g_io_channel_unref(ipc->cmd_io); + ipc->cmd_io = NULL; + } + + if (ipc->notif_watch) { + g_source_remove(ipc->notif_watch); + ipc->notif_watch = 0; + } + + if (ipc->notif_io) { + g_io_channel_shutdown(ipc->notif_io, TRUE, NULL); + g_io_channel_unref(ipc->notif_io); + ipc->notif_io = NULL; + } + + if (in_cleanup) + return; + + if (ipc->disconnect_cb) + ipc->disconnect_cb(ipc->disconnect_cb_data); +} + +static int ipc_handle_msg(struct service_handler *handlers, size_t max_index, + const void *buf, ssize_t len) +{ + const struct ipc_hdr *msg = buf; + const struct ipc_handler *handler; + + if (len < (ssize_t) sizeof(*msg)) { + DBG("message too small (%zd bytes)", len); + return -EBADMSG; + } + + if (len != (ssize_t) (sizeof(*msg) + msg->len)) { + DBG("message malformed (%zd bytes)", len); + return -EBADMSG; + } + + /* if service is valid */ + if (msg->service_id > max_index) { + DBG("unknown service (0x%x)", msg->service_id); + return -EOPNOTSUPP; + } + + /* if service is registered */ + if (!handlers[msg->service_id].handler) { + DBG("service not registered (0x%x)", msg->service_id); + return -EOPNOTSUPP; + } + + /* if opcode is valid */ + if (msg->opcode == IPC_OP_STATUS || + msg->opcode > handlers[msg->service_id].size) { + DBG("invalid opcode 0x%x for service 0x%x", msg->opcode, + msg->service_id); + return -EOPNOTSUPP; + } + + /* opcode is table offset + 1 */ + handler = &handlers[msg->service_id].handler[msg->opcode - 1]; + + /* if payload size is valid */ + if ((handler->var_len && handler->data_len > msg->len) || + (!handler->var_len && handler->data_len != msg->len)) { + DBG("invalid size for opcode 0x%x service 0x%x", + msg->opcode, msg->service_id); + return -EMSGSIZE; + } + + handler->handler(msg->payload, msg->len); + + return 0; +} + +static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct ipc *ipc = user_data; + + char buf[IPC_MTU]; + ssize_t ret; + int fd, err; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + info("IPC: command socket closed"); + + ipc->cmd_watch = 0; + goto fail; + } + + fd = g_io_channel_unix_get_fd(io); + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) { + error("IPC: command read failed (%s)", strerror(errno)); + goto fail; + } + + err = ipc_handle_msg(ipc->services, ipc->service_max, buf, ret); + if (err < 0) { + error("IPC: failed to handle message (%s)", strerror(-err)); + goto fail; + } + + return TRUE; + +fail: + ipc_disconnect(ipc, false); + + return FALSE; +} + +static gboolean notif_watch_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct ipc *ipc = user_data; + + info("IPC: notification socket closed"); + + ipc->notif_watch = 0; + + ipc_disconnect(ipc, false); + + return FALSE; +} + +static GIOChannel *ipc_connect(const char *path, size_t size, + GIOFunc connect_cb, void *user_data) +{ + struct sockaddr_un addr; + GIOCondition cond; + GIOChannel *io; + int sk; + + sk = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + error("IPC: failed to create socket: %d (%s)", errno, + strerror(errno)); + return NULL; + } + + io = g_io_channel_unix_new(sk); + + g_io_channel_set_close_on_unref(io, TRUE); + g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, path, size); + + connect(sk, (struct sockaddr *) &addr, sizeof(addr)); + + cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + g_io_add_watch(io, cond, connect_cb, user_data); + + return io; +} + +static gboolean notif_connect_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct ipc *ipc = user_data; + + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + error("IPC: notification socket connect failed"); + + ipc_disconnect(ipc, false); + + return FALSE; + } + + cond = G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + ipc->notif_watch = g_io_add_watch(io, cond, notif_watch_cb, ipc); + + cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc); + + info("IPC: successfully connected (with notifications)"); + + return FALSE; +} + +static gboolean cmd_connect_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct ipc *ipc = user_data; + + DBG(""); + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + error("IPC: command socket connect failed"); + ipc_disconnect(ipc, false); + + return FALSE; + } + + if (ipc->notifications) { + ipc->notif_io = ipc_connect(ipc->path, ipc->size, + notif_connect_cb, ipc); + if (!ipc->notif_io) + ipc_disconnect(ipc, false); + + return FALSE; + } + + cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + + ipc->cmd_watch = g_io_add_watch(ipc->cmd_io, cond, cmd_watch_cb, ipc); + + info("IPC: successfully connected (without notifications)"); + + return FALSE; +} + +struct ipc *ipc_init(const char *path, size_t size, int max_service_id, + bool notifications, + ipc_disconnect_cb cb, void *cb_data) +{ + struct ipc *ipc; + + ipc = g_new0(struct ipc, 1); + + ipc->services = g_new0(struct service_handler, max_service_id + 1); + ipc->service_max = max_service_id; + + ipc->path = path; + ipc->size = size; + + ipc->notifications = notifications; + + ipc->cmd_io = ipc_connect(path, size, cmd_connect_cb, ipc); + if (!ipc->cmd_io) { + g_free(ipc->services); + g_free(ipc); + return NULL; + } + + ipc->disconnect_cb = cb; + ipc->disconnect_cb_data = cb_data; + + return ipc; +} + +void ipc_cleanup(struct ipc *ipc) +{ + ipc_disconnect(ipc, true); + + g_free(ipc->services); + g_free(ipc); +} + +static void ipc_send(int sk, uint8_t service_id, uint8_t opcode, uint16_t len, + void *param, int fd) +{ + struct msghdr msg; + struct iovec iv[2]; + struct ipc_hdr m; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + + memset(&msg, 0, sizeof(msg)); + memset(&m, 0, sizeof(m)); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + + m.service_id = service_id; + m.opcode = opcode; + m.len = len; + + iv[0].iov_base = &m; + iv[0].iov_len = sizeof(m); + + iv[1].iov_base = param; + iv[1].iov_len = len; + + msg.msg_iov = iv; + msg.msg_iovlen = 2; + + if (fd >= 0) { + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + + /* Initialize the payload */ + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + } + + if (sendmsg(sk, &msg, 0) < 0) { + error("IPC send failed :%s", strerror(errno)); + + /* TODO disconnect IPC here when this function becomes static */ + raise(SIGTERM); + } +} + +void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint8_t status) +{ + struct ipc_status s; + int sk; + + sk = g_io_channel_unix_get_fd(ipc->cmd_io); + + if (status == IPC_STATUS_SUCCESS) { + ipc_send(sk, service_id, opcode, 0, NULL, -1); + return; + } + + s.code = status; + + ipc_send(sk, service_id, IPC_OP_STATUS, sizeof(s), &s, -1); +} + +void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param, int fd) +{ + ipc_send(g_io_channel_unix_get_fd(ipc->cmd_io), service_id, opcode, len, + param, fd); +} + +void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param) +{ + return ipc_send_notif_with_fd(ipc, service_id, opcode, len, param, -1); +} + +void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param, int fd) +{ + if (!ipc || !ipc->notif_io) + return; + + ipc_send(g_io_channel_unix_get_fd(ipc->notif_io), service_id, opcode, + len, param, fd); +} + +void ipc_register(struct ipc *ipc, uint8_t service, + const struct ipc_handler *handlers, uint8_t size) +{ + if (service > ipc->service_max) + return; + + ipc->services[service].handler = handlers; + ipc->services[service].size = size; +} + +void ipc_unregister(struct ipc *ipc, uint8_t service) +{ + if (service > ipc->service_max) + return; + + ipc->services[service].handler = NULL; + ipc->services[service].size = 0; +} diff -Nru bluez-4.101/android/ipc-common.h bluez-5.23/android/ipc-common.h --- bluez-4.101/android/ipc-common.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/ipc-common.h 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#define IPC_MTU 1024 + +#define IPC_STATUS_SUCCESS 0x00 + +struct ipc_hdr { + uint8_t service_id; + uint8_t opcode; + uint16_t len; + uint8_t payload[0]; +} __attribute__((packed)); + +#define IPC_OP_STATUS 0x00 +struct ipc_status { + uint8_t code; +} __attribute__((packed)); diff -Nru bluez-4.101/android/ipc.h bluez-5.23/android/ipc.h --- bluez-4.101/android/ipc.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/ipc.h 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct ipc_handler { + void (*handler) (const void *buf, uint16_t len); + bool var_len; + size_t data_len; +}; + +struct ipc; + +typedef void (*ipc_disconnect_cb) (void *data); + +struct ipc *ipc_init(const char *path, size_t size, int max_service_id, + bool notifications, + ipc_disconnect_cb cb, void *cb_data); +void ipc_cleanup(struct ipc *ipc); + +void ipc_send_rsp(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint8_t status); +void ipc_send_rsp_full(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param, int fd); +void ipc_send_notif(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param); +void ipc_send_notif_with_fd(struct ipc *ipc, uint8_t service_id, uint8_t opcode, + uint16_t len, void *param, int fd); + +void ipc_register(struct ipc *ipc, uint8_t service, + const struct ipc_handler *handlers, uint8_t size); +void ipc_unregister(struct ipc *ipc, uint8_t service); diff -Nru bluez-4.101/android/ipc-tester.c bluez-5.23/android/ipc-tester.c --- bluez-4.101/android/ipc-tester.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/ipc-tester.c 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,1262 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/mgmt.h" + +#include "src/shared/tester.h" +#include "src/shared/mgmt.h" +#include "src/shared/hciemu.h" + +#include "hal-msg.h" +#include "ipc-common.h" + +#include + +#define WAIT_FOR_SIGNAL_TIME 2 /* in seconds */ +#define EMULATOR_SIGNAL "emulator_started" + +struct test_data { + struct mgmt *mgmt; + uint16_t mgmt_index; + struct hciemu *hciemu; + enum hciemu_type hciemu_type; + pid_t bluetoothd_pid; + bool setup_done; +}; + +struct ipc_data { + void *buffer; + size_t len; +}; + +struct generic_data { + struct ipc_data ipc_data; + + unsigned int num_services; + int init_services[]; +}; + +struct regmod_msg { + struct ipc_hdr header; + struct hal_cmd_register_module cmd; +} __attribute__((packed)); + +#define CONNECT_TIMEOUT (5 * 1000) +#define SERVICE_NAME "bluetoothd" + +static char exec_dir[PATH_MAX]; + +static int cmd_sk = -1; +static int notif_sk = -1; + +static void read_info_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct mgmt_rp_read_info *rp = param; + char addr[18]; + uint16_t manufacturer; + uint32_t supported_settings, current_settings; + + tester_print("Read Info callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + ba2str(&rp->bdaddr, addr); + manufacturer = btohs(rp->manufacturer); + supported_settings = btohl(rp->supported_settings); + current_settings = btohl(rp->current_settings); + + tester_print(" Address: %s", addr); + tester_print(" Version: 0x%02x", rp->version); + tester_print(" Manufacturer: 0x%04x", manufacturer); + tester_print(" Supported settings: 0x%08x", supported_settings); + tester_print(" Current settings: 0x%08x", current_settings); + tester_print(" Class: 0x%02x%02x%02x", + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); + tester_print(" Name: %s", rp->name); + tester_print(" Short name: %s", rp->short_name); + + if (strcmp(hciemu_get_address(data->hciemu), addr)) { + tester_pre_setup_failed(); + return; + } + + tester_pre_setup_complete(); +} + +static void index_added_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Added callback"); + tester_print(" Index: 0x%04x", index); + + data->mgmt_index = index; + + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, + read_info_callback, NULL, NULL); +} + +static void index_removed_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Removed callback"); + tester_print(" Index: 0x%04x", index); + + if (index != data->mgmt_index) + return; + + mgmt_unregister_index(data->mgmt, data->mgmt_index); + + mgmt_unref(data->mgmt); + data->mgmt = NULL; + + tester_post_teardown_complete(); +} + +static void read_index_list_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Read Index List callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + index_added_callback, NULL, NULL); + + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + index_removed_callback, NULL, NULL); + + data->hciemu = hciemu_new(data->hciemu_type); + if (!data->hciemu) { + tester_warn("Failed to setup HCI emulation"); + tester_pre_setup_failed(); + return; + } + + tester_print("New hciemu instance created"); +} + +static void test_pre_setup(const void *data) +{ + struct test_data *test_data = tester_get_data(); + + if (!tester_use_debug()) + fclose(stderr); + + test_data->mgmt = mgmt_new_default(); + if (!test_data->mgmt) { + tester_warn("Failed to setup management interface"); + tester_pre_setup_failed(); + return; + } + + mgmt_send(test_data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, + NULL, read_index_list_callback, NULL, NULL); +} + +static void test_post_teardown(const void *data) +{ + struct test_data *test_data = tester_get_data(); + + if (test_data->hciemu) { + hciemu_unref(test_data->hciemu); + test_data->hciemu = NULL; + } +} + +static void bluetoothd_start(int hci_index) +{ + char prg_name[PATH_MAX]; + char index[8]; + char *prg_argv[4]; + + snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); + snprintf(index, sizeof(index), "%d", hci_index); + + prg_argv[0] = prg_name; + prg_argv[1] = "-i"; + prg_argv[2] = index; + prg_argv[3] = NULL; + + if (!tester_use_debug()) + fclose(stderr); + + execve(prg_argv[0], prg_argv, NULL); +} + +static void emulator(int pipe, int hci_index) +{ + static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; + char buf[1024]; + struct sockaddr_un addr; + struct timeval tv; + int fd; + ssize_t len; + + fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) + goto failed; + + tv.tv_sec = WAIT_FOR_SIGNAL_TIME; + tv.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to bind system socket"); + goto failed; + } + + len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL)); + + if (len != sizeof(EMULATOR_SIGNAL)) + goto failed; + + memset(buf, 0, sizeof(buf)); + + len = read(fd, buf, sizeof(buf)); + if (len <= 0 || strcmp(buf, "ctl.start=bluetoothd")) + goto failed; + + close(pipe); + close(fd); + return bluetoothd_start(hci_index); + +failed: + close(pipe); + if (fd >= 0) + close(fd); +} + +static int accept_connection(int sk) +{ + int err; + struct pollfd pfd; + int new_sk; + + memset(&pfd, 0 , sizeof(pfd)); + pfd.fd = sk; + pfd.events = POLLIN; + + err = poll(&pfd, 1, CONNECT_TIMEOUT); + if (err < 0) { + err = errno; + tester_warn("Failed to poll: %d (%s)", err, strerror(err)); + return -errno; + } + + if (err == 0) { + tester_warn("bluetoothd connect timeout"); + return -errno; + } + + new_sk = accept(sk, NULL, NULL); + if (new_sk < 0) { + err = errno; + tester_warn("Failed to accept socket: %d (%s)", + err, strerror(err)); + return -errno; + } + + return new_sk; +} + +static bool init_ipc(void) +{ + struct sockaddr_un addr; + + int sk; + int err; + + sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); + if (sk < 0) { + err = errno; + tester_warn("Failed to create socket: %d (%s)", err, + strerror(err)); + return false; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH)); + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + err = errno; + tester_warn("Failed to bind socket: %d (%s)", err, + strerror(err)); + close(sk); + return false; + } + + if (listen(sk, 2) < 0) { + err = errno; + tester_warn("Failed to listen on socket: %d (%s)", err, + strerror(err)); + close(sk); + return false; + } + + /* Start Android Bluetooth daemon service */ + if (property_set("ctl.start", SERVICE_NAME) < 0) { + tester_warn("Failed to start service %s", SERVICE_NAME); + close(sk); + return false; + } + + cmd_sk = accept_connection(sk); + if (cmd_sk < 0) { + close(sk); + return false; + } + + notif_sk = accept_connection(sk); + if (notif_sk < 0) { + close(sk); + close(cmd_sk); + cmd_sk = -1; + return false; + } + + tester_print("bluetoothd connected"); + + close(sk); + + return true; +} + +static void cleanup_ipc(void) +{ + if (cmd_sk < 0) + return; + + close(cmd_sk); + cmd_sk = -1; +} + +static gboolean check_for_daemon(gpointer user_data) +{ + int status; + struct test_data *data = user_data; + + if ((waitpid(data->bluetoothd_pid, &status, WNOHANG)) + != data->bluetoothd_pid) + return true; + + if (data->setup_done) { + if (WIFEXITED(status) && + (WEXITSTATUS(status) == EXIT_SUCCESS)) { + tester_test_passed(); + return false; + } + tester_test_failed(); + } else { + tester_setup_failed(); + test_post_teardown(data); + } + + tester_warn("Unexpected Daemon shutdown with status %d", status); + return false; +} + +static bool setup_module(int service_id) +{ + struct ipc_hdr response; + struct ipc_hdr expected_response; + + struct regmod_msg btmodule_msg = { + .header = { + .service_id = HAL_SERVICE_ID_CORE, + .opcode = HAL_OP_REGISTER_MODULE, + .len = sizeof(struct hal_cmd_register_module), + }, + .cmd = { + .service_id = service_id, + }, + }; + + if (write(cmd_sk, &btmodule_msg, sizeof(btmodule_msg)) < 0) + goto fail; + + if (read(cmd_sk, &response, sizeof(response)) < 0) + goto fail; + + expected_response = btmodule_msg.header; + expected_response.len = 0; + + if (memcmp(&response, &expected_response, sizeof(response)) == 0) + return true; + +fail: + tester_warn("Module registration failed."); + return false; +} + +static void setup(const void *data) +{ + const struct generic_data *generic_data = data; + struct test_data *test_data = tester_get_data(); + int signal_fd[2]; + char buf[1024]; + pid_t pid; + int len; + unsigned int i; + + if (pipe(signal_fd)) + goto failed; + + pid = fork(); + + if (pid < 0) { + close(signal_fd[0]); + close(signal_fd[1]); + goto failed; + } + + if (pid == 0) { + if (!tester_use_debug()) + fclose(stderr); + + close(signal_fd[0]); + emulator(signal_fd[1], test_data->mgmt_index); + exit(0); + } + + close(signal_fd[1]); + test_data->bluetoothd_pid = pid; + + len = read(signal_fd[0], buf, sizeof(buf)); + if (len <= 0 || (strcmp(buf, EMULATOR_SIGNAL))) { + close(signal_fd[0]); + goto failed; + } + + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, check_for_daemon, test_data, + NULL); + + if (!init_ipc()) { + tester_warn("Cannot initialize IPC mechanism!"); + goto failed; + } + tester_print("Will init %d services.", generic_data->num_services); + + for (i = 0; i < generic_data->num_services; i++) + if (!setup_module(generic_data->init_services[i])) { + cleanup_ipc(); + goto failed; + } + + test_data->setup_done = true; + + tester_setup_complete(); + return; + +failed: + g_idle_remove_by_data(test_data); + tester_setup_failed(); + test_post_teardown(data); +} + +static void teardown(const void *data) +{ + struct test_data *test_data = tester_get_data(); + + g_idle_remove_by_data(test_data); + cleanup_ipc(); + + if (test_data->bluetoothd_pid) + waitpid(test_data->bluetoothd_pid, NULL, 0); + + tester_teardown_complete(); +} + +static void ipc_send_tc(const void *data) +{ + const struct generic_data *generic_data = data; + const struct ipc_data *ipc_data = &generic_data->ipc_data; + + if (ipc_data->len) { + if (write(cmd_sk, ipc_data->buffer, ipc_data->len) < 0) + tester_test_failed(); + } +} + +#define service_data(args...) { args } + +#define gen_data(writelen, writebuf, servicelist...) \ + { \ + .ipc_data = { \ + .buffer = writebuf, \ + .len = writelen, \ + }, \ + .init_services = service_data(servicelist), \ + .num_services = sizeof((const int[]) \ + service_data(servicelist)) / \ + sizeof(int), \ + } + +#define test_generic(name, test, setup, teardown, buffer, writelen, \ + services...) \ + do { \ + struct test_data *user; \ + static const struct generic_data data = \ + gen_data(writelen, buffer, services); \ + user = g_malloc0(sizeof(struct test_data)); \ + if (!user) \ + break; \ + user->hciemu_type = HCIEMU_TYPE_BREDRLE; \ + tester_add_full(name, &data, test_pre_setup, setup, \ + test, teardown, test_post_teardown, \ + 3, user, g_free); \ + } while (0) + +#define test_opcode_valid(_name, _service, _opcode, _len, _servicelist...) \ + do { \ + static struct ipc_hdr hdr = { \ + .service_id = _service, \ + .opcode = _opcode, \ + .len = _len, \ + }; \ + \ + test_generic("Opcode out of range: "_name, \ + ipc_send_tc, setup, teardown, \ + &hdr, \ + sizeof(hdr), \ + _servicelist); \ + } while (0) + +struct vardata { + struct ipc_hdr hdr; + uint8_t buf[IPC_MTU]; +} __attribute__((packed)); + +#define test_datasize_valid(_name, _service, _opcode, _hlen, _addatasize, \ + _servicelist...) \ + do { \ + static struct vardata vdata = { \ + .hdr.service_id = _service, \ + .hdr.opcode = _opcode, \ + .hdr.len = (_hlen) + (_addatasize), \ + .buf = {}, \ + }; \ + test_generic("Data size "_name, \ + ipc_send_tc, setup, teardown, \ + &vdata, \ + sizeof(vdata.hdr) + (_hlen) + (_addatasize),\ + _servicelist); \ + } while (0) + +static struct regmod_msg register_bt_msg = { + .header = { + .service_id = HAL_SERVICE_ID_CORE, + .opcode = HAL_OP_REGISTER_MODULE, + .len = sizeof(struct hal_cmd_register_module), + }, + .cmd = { + .service_id = HAL_SERVICE_ID_BLUETOOTH, + }, +}; + +static struct regmod_msg register_bt_malformed_size_msg = { + .header = { + .service_id = HAL_SERVICE_ID_CORE, + .opcode = HAL_OP_REGISTER_MODULE, + /* wrong payload size declared */ + .len = sizeof(struct hal_cmd_register_module) - 1, + }, + .cmd = { + .service_id = HAL_SERVICE_ID_CORE, + }, +}; + +struct malformed_data3_struct { + struct regmod_msg valid_msg; + int redundant_data; +} __attribute__((packed)); + +static struct malformed_data3_struct malformed_data3_msg = { + /* valid register service message */ + .valid_msg = { + .header = { + .service_id = HAL_SERVICE_ID_CORE, + .opcode = HAL_OP_REGISTER_MODULE, + .len = sizeof(struct hal_cmd_register_module), + }, + .cmd = { + .service_id = HAL_SERVICE_ID_CORE, + }, + }, + /* plus redundant data */ + . redundant_data = 666, +}; + +static struct ipc_hdr enable_unknown_service_hdr = { + .service_id = HAL_SERVICE_ID_MAX + 1, + .opcode = HAL_OP_REGISTER_MODULE, + .len = 0, +}; + +static struct ipc_hdr enable_bt_service_hdr = { + .service_id = HAL_SERVICE_ID_BLUETOOTH, + .opcode = HAL_OP_ENABLE, + .len = 0, +}; + +struct bt_set_adapter_prop_data { + struct ipc_hdr hdr; + struct hal_cmd_set_adapter_prop prop; + + /* data placeholder for hal_cmd_set_adapter_prop.val[0] */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_set_adapter_prop)]; +} __attribute__((packed)); + +#define set_name "new name" + +static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, + .hdr.opcode = HAL_OP_SET_ADAPTER_PROP, + .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + + sizeof(set_name), + + .prop.type = HAL_PROP_ADAPTER_NAME, + /* declare wrong descriptor length */ + .prop.len = sizeof(set_name) + 1, + /* init prop.val[0] */ + .buf = set_name, +}; + +static struct bt_set_adapter_prop_data bt_set_adapter_prop_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, + .hdr.opcode = HAL_OP_SET_ADAPTER_PROP, + .hdr.len = sizeof(struct hal_cmd_set_adapter_prop) + + sizeof(set_name), + + .prop.type = HAL_PROP_ADAPTER_NAME, + /* declare wrong descriptor length */ + .prop.len = sizeof(set_name) - 1, + /* init prop.val[0] */ + .buf = set_name, +}; + +struct bt_set_remote_prop_data { + struct ipc_hdr hdr; + struct hal_cmd_set_remote_device_prop prop; + + /* data placeholder for hal_cmd_set_remote_device_prop.val[0] */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_set_remote_device_prop)]; +} __attribute__((packed)); + +static struct bt_set_remote_prop_data bt_set_remote_prop_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, + .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP, + .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) + + sizeof(set_name), + + .prop.bdaddr = {}, + .prop.type = HAL_PROP_DEVICE_NAME, + /* declare wrong descriptor length */ + .prop.len = sizeof(set_name) + 1, + .buf = set_name, +}; + +static struct bt_set_remote_prop_data bt_set_remote_prop_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_BLUETOOTH, + .hdr.opcode = HAL_OP_SET_REMOTE_DEVICE_PROP, + .hdr.len = sizeof(struct hal_cmd_set_remote_device_prop) + + sizeof(set_name), + + .prop.bdaddr = {}, + .prop.type = HAL_PROP_DEVICE_NAME, + /* declare wrong descriptor length */ + .prop.len = sizeof(set_name) - 1, + .buf = set_name, +}; + +struct hidhost_set_info_data { + struct ipc_hdr hdr; + struct hal_cmd_hidhost_set_info info; + + /* data placeholder for hal_cmd_hidhost_set_info.descr[0] field */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_hidhost_set_info)]; +} __attribute__((packed)); + +#define set_info_data "some descriptor" + +static struct hidhost_set_info_data hidhost_set_info_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SET_INFO, + .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) + + sizeof(set_info_data), + + /* declare wrong descriptor length */ + .info.descr_len = sizeof(set_info_data) + 1, + /* init .info.descr[0] */ + .buf = set_info_data, +}; + +static struct hidhost_set_info_data hidhost_set_info_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SET_INFO, + .hdr.len = sizeof(struct hal_cmd_hidhost_set_info) + + sizeof(set_info_data), + + /* declare wrong descriptor length */ + .info.descr_len = sizeof(set_info_data) - 1, + /* init .info.descr[0] */ + .buf = set_info_data, +}; + +struct hidhost_set_report_data { + struct ipc_hdr hdr; + struct hal_cmd_hidhost_set_report report; + + /* data placeholder for hal_cmd_hidhost_set_report.data[0] field */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_hidhost_set_report)]; +} __attribute__((packed)); + +#define set_rep_data "1234567890" + +static struct hidhost_set_report_data hidhost_set_report_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT, + .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) + + sizeof(set_rep_data), + + /* declare wrong descriptor length */ + .report.len = sizeof(set_rep_data) + 1, + /* init report.data[0] */ + .buf = set_rep_data, +}; + +static struct hidhost_set_report_data hidhost_set_report_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SET_REPORT, + .hdr.len = sizeof(struct hal_cmd_hidhost_set_report) + + sizeof(set_rep_data), + + /* declare wrong descriptor length */ + .report.len = sizeof(set_rep_data) - 1, + /* init report.data[0] */ + .buf = set_rep_data, +}; + +struct hidhost_send_data_data { + struct ipc_hdr hdr; + struct hal_cmd_hidhost_send_data hiddata; + + /* data placeholder for hal_cmd_hidhost_send_data.data[0] field */ + uint8_t buf[IPC_MTU - sizeof(struct ipc_hdr) - + sizeof(struct hal_cmd_hidhost_send_data)]; +} __attribute__((packed)); + +#define send_data_data "1234567890" + +static struct hidhost_send_data_data hidhost_send_data_overs = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA, + .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) + + sizeof(send_data_data), + + /* declare wrong descriptor length */ + .hiddata.len = sizeof(send_data_data) + 1, + /* init .hiddata.data[0] */ + .buf = send_data_data, +}; + +static struct hidhost_send_data_data hidhost_send_data_unders = { + .hdr.service_id = HAL_SERVICE_ID_HIDHOST, + .hdr.opcode = HAL_OP_HIDHOST_SEND_DATA, + .hdr.len = sizeof(struct hal_cmd_hidhost_send_data) + + sizeof(send_data_data), + + /* declare wrong descriptor length */ + .hiddata.len = sizeof(send_data_data) - 1, + /* init .hiddata.data[0] */ + .buf = send_data_data, +}; + +int main(int argc, char *argv[]) +{ + snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); + + tester_init(&argc, &argv); + + /* check general IPC errors */ + test_generic("Too small data", + ipc_send_tc, setup, teardown, + ®ister_bt_msg, 1); + + test_generic("Malformed data (wrong payload declared)", + ipc_send_tc, setup, teardown, + ®ister_bt_malformed_size_msg, + sizeof(register_bt_malformed_size_msg), + HAL_SERVICE_ID_BLUETOOTH); + + test_generic("Malformed data2 (undersized msg)", + ipc_send_tc, setup, teardown, + ®ister_bt_msg, + sizeof(register_bt_msg) - 1, + HAL_SERVICE_ID_BLUETOOTH); + + test_generic("Malformed data3 (oversized msg)", + ipc_send_tc, setup, teardown, + &malformed_data3_msg, + sizeof(malformed_data3_msg), + HAL_SERVICE_ID_BLUETOOTH); + + test_generic("Invalid service", + ipc_send_tc, setup, teardown, + &enable_unknown_service_hdr, + sizeof(enable_unknown_service_hdr), + HAL_SERVICE_ID_BLUETOOTH); + + test_generic("Enable unregistered service", + ipc_send_tc, setup, teardown, + &enable_bt_service_hdr, + sizeof(enable_bt_service_hdr)); + + /* check service handler's max opcode value */ + test_opcode_valid("CORE", HAL_SERVICE_ID_CORE, 0x03, 0); + + test_opcode_valid("BLUETOOTH", HAL_SERVICE_ID_BLUETOOTH, 0x15, 0, + HAL_SERVICE_ID_BLUETOOTH); + + test_opcode_valid("SOCK", HAL_SERVICE_ID_SOCKET, 0x03, 0, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + + test_opcode_valid("HIDHOST", HAL_SERVICE_ID_HIDHOST, 0x10, 0, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + + test_opcode_valid("PAN", HAL_SERVICE_ID_PAN, 0x05, 0, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + + test_opcode_valid("A2DP", HAL_SERVICE_ID_A2DP, 0x03, 0, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + + /* check for valid data size */ + test_datasize_valid("CORE Register+", HAL_SERVICE_ID_CORE, + HAL_OP_REGISTER_MODULE, + sizeof(struct hal_cmd_register_module), 1); + test_datasize_valid("CORE Register-", HAL_SERVICE_ID_CORE, + HAL_OP_REGISTER_MODULE, + sizeof(struct hal_cmd_register_module), -1); + test_datasize_valid("CORE Unregister+", HAL_SERVICE_ID_CORE, + HAL_OP_UNREGISTER_MODULE, + sizeof(struct hal_cmd_unregister_module), 1); + test_datasize_valid("CORE Unregister-", HAL_SERVICE_ID_CORE, + HAL_OP_UNREGISTER_MODULE, + sizeof(struct hal_cmd_unregister_module), -1); + + /* check for valid data size for BLUETOOTH */ + test_datasize_valid("BT Enable+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_ENABLE, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Disable+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DISABLE, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Adapter Props+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_ADAPTER_PROPS, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_ADAPTER_PROP, + sizeof(struct hal_cmd_get_adapter_prop), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_ADAPTER_PROP, + sizeof(struct hal_cmd_get_adapter_prop), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Set Adapter Prop+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_ADAPTER_PROP, + sizeof(struct hal_cmd_set_adapter_prop), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Set Adapter Prop-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_ADAPTER_PROP, + sizeof(struct hal_cmd_set_adapter_prop), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_generic("Data size BT Set Adapter Prop Vardata+", + ipc_send_tc, setup, teardown, + &bt_set_adapter_prop_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_set_adapter_prop) + + sizeof(set_name)), + HAL_SERVICE_ID_BLUETOOTH); + test_generic("Data size BT Set Adapter Prop Vardata+", + ipc_send_tc, setup, teardown, + &bt_set_adapter_prop_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_set_adapter_prop) + + sizeof(set_name)), + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Props+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROPS, + sizeof(struct hal_cmd_get_remote_device_props), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Props-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROPS, + sizeof(struct hal_cmd_get_remote_device_props), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Prop+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROP, + sizeof(struct hal_cmd_get_remote_device_prop), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Prop-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_DEVICE_PROP, + sizeof(struct hal_cmd_get_remote_device_prop), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Set Remote Prop+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_REMOTE_DEVICE_PROP, + sizeof(struct hal_cmd_set_remote_device_prop), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Set Remote Prop-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SET_REMOTE_DEVICE_PROP, + sizeof(struct hal_cmd_set_remote_device_prop), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_generic("Data size BT Set Remote Prop Vardata+", + ipc_send_tc, setup, teardown, + &bt_set_remote_prop_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_set_remote_device_prop) + + sizeof(set_name)), + HAL_SERVICE_ID_BLUETOOTH); + test_generic("Data size BT Set Remote Prop Vardata-", + ipc_send_tc, setup, teardown, + &bt_set_remote_prop_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_set_remote_device_prop) + + sizeof(set_name)), + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote SV Rec+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICE_REC, + sizeof(struct hal_cmd_get_remote_service_rec), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote SV Rec-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICE_REC, + sizeof(struct hal_cmd_get_remote_service_rec), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Services+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICES, + sizeof(struct hal_cmd_get_remote_services), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Get Remote Services-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_GET_REMOTE_SERVICES, + sizeof(struct hal_cmd_get_remote_services), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Start Discovery+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_START_DISCOVERY, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Cancel Discovery+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CANCEL_DISCOVERY, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Create Bond+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CREATE_BOND, + sizeof(struct hal_cmd_create_bond), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Create Bond-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CREATE_BOND, + sizeof(struct hal_cmd_create_bond), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Remove Bond+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_REMOVE_BOND, + sizeof(struct hal_cmd_remove_bond), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Remove Bond-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_REMOVE_BOND, + sizeof(struct hal_cmd_remove_bond), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Cancel Bond+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CANCEL_BOND, + sizeof(struct hal_cmd_cancel_bond), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Cancel Bond-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_CANCEL_BOND, + sizeof(struct hal_cmd_cancel_bond), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Pin Reply+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_PIN_REPLY, + sizeof(struct hal_cmd_pin_reply), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT Pin Reply-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_PIN_REPLY, + sizeof(struct hal_cmd_pin_reply), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT SSP Reply+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SSP_REPLY, + sizeof(struct hal_cmd_ssp_reply), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT SSP Reply-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_SSP_REPLY, + sizeof(struct hal_cmd_ssp_reply), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT DUT Mode Conf+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DUT_MODE_CONF, + sizeof(struct hal_cmd_dut_mode_conf), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT DUT Mode Conf-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DUT_MODE_CONF, + sizeof(struct hal_cmd_dut_mode_conf), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT DUT Mode Send+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DUT_MODE_SEND, + sizeof(struct hal_cmd_dut_mode_send), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT DUT Mode Send-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_DUT_MODE_SEND, + sizeof(struct hal_cmd_dut_mode_send), -1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT LE Test+", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_LE_TEST_MODE, + sizeof(struct hal_cmd_le_test_mode), 1, + HAL_SERVICE_ID_BLUETOOTH); + test_datasize_valid("BT LE Test-", HAL_SERVICE_ID_BLUETOOTH, + HAL_OP_LE_TEST_MODE, + sizeof(struct hal_cmd_le_test_mode), -1, + HAL_SERVICE_ID_BLUETOOTH); + + /* check for valid data size for SOCK */ + test_datasize_valid("SOCKET Listen+", HAL_SERVICE_ID_SOCKET, + HAL_OP_SOCKET_LISTEN, + sizeof(struct hal_cmd_socket_listen), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + test_datasize_valid("SOCKET Listen-", HAL_SERVICE_ID_SOCKET, + HAL_OP_SOCKET_LISTEN, + sizeof(struct hal_cmd_socket_listen), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + test_datasize_valid("SOCKET Connect+", HAL_SERVICE_ID_SOCKET, + HAL_OP_SOCKET_CONNECT, + sizeof(struct hal_cmd_socket_connect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + test_datasize_valid("SOCKET Connect-", HAL_SERVICE_ID_SOCKET, + HAL_OP_SOCKET_CONNECT, + sizeof(struct hal_cmd_socket_connect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_SOCKET); + + /* check for valid data size for HID Host */ + test_datasize_valid("HIDHOST Connect+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_CONNECT, + sizeof(struct hal_cmd_hidhost_connect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Connect-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_CONNECT, + sizeof(struct hal_cmd_hidhost_connect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Disconnect+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_DISCONNECT, + sizeof(struct hal_cmd_hidhost_disconnect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Disconnect-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_DISCONNECT, + sizeof(struct hal_cmd_hidhost_disconnect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Virt. Unplug+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_VIRTUAL_UNPLUG, + sizeof(struct hal_cmd_hidhost_virtual_unplug), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Virt. Unplug-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_VIRTUAL_UNPLUG, + sizeof(struct hal_cmd_hidhost_virtual_unplug), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Info+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_INFO, + sizeof(struct hal_cmd_hidhost_set_info), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Info-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_INFO, + sizeof(struct hal_cmd_hidhost_set_info), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Set Info Vardata+", + ipc_send_tc, setup, teardown, + &hidhost_set_info_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_set_info) + + sizeof(set_info_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Set Info Vardata-", + ipc_send_tc, setup, teardown, + &hidhost_set_info_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_set_info) + + sizeof(set_info_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Get Protocol+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_PROTOCOL, + sizeof(struct hal_cmd_hidhost_get_protocol), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Get Protocol-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_PROTOCOL, + sizeof(struct hal_cmd_hidhost_get_protocol), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Protocol+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_PROTOCOL, + sizeof(struct hal_cmd_hidhost_set_protocol), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Protocol-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_PROTOCOL, + sizeof(struct hal_cmd_hidhost_set_protocol), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Get Report+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_REPORT, + sizeof(struct hal_cmd_hidhost_get_report), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Get Report-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_GET_REPORT, + sizeof(struct hal_cmd_hidhost_get_report), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Report+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_REPORT, + sizeof(struct hal_cmd_hidhost_set_report), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Set Report-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SET_REPORT, + sizeof(struct hal_cmd_hidhost_set_report), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Set Report Vardata+", + ipc_send_tc, setup, teardown, + &hidhost_set_report_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_set_report) + + sizeof(set_rep_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Set Report Vardata-", + ipc_send_tc, setup, teardown, + &hidhost_set_report_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_set_report) + + sizeof(set_rep_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Send Data+", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SEND_DATA, + sizeof(struct hal_cmd_hidhost_send_data), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_datasize_valid("HIDHOST Send Data-", HAL_SERVICE_ID_HIDHOST, + HAL_OP_HIDHOST_SEND_DATA, + sizeof(struct hal_cmd_hidhost_send_data), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Send Vardata+", + ipc_send_tc, setup, teardown, + &hidhost_send_data_overs, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_send_data) + + sizeof(send_data_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + test_generic("Data size HIDHOST Send Vardata-", + ipc_send_tc, setup, teardown, + &hidhost_send_data_unders, + (sizeof(struct ipc_hdr) + + sizeof(struct hal_cmd_hidhost_send_data) + + sizeof(send_data_data)), + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_HIDHOST); + + /* check for valid data size for PAN */ + test_datasize_valid("PAN Enable+", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_ENABLE, + sizeof(struct hal_cmd_pan_enable), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Enable-", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_ENABLE, + sizeof(struct hal_cmd_pan_enable), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Get Role+", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_GET_ROLE, + 0, 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Connect+", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_CONNECT, + sizeof(struct hal_cmd_pan_connect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Connect-", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_CONNECT, + sizeof(struct hal_cmd_pan_connect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Disconnect+", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_DISCONNECT, + sizeof(struct hal_cmd_pan_disconnect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + test_datasize_valid("PAN Disconnect-", HAL_SERVICE_ID_PAN, + HAL_OP_PAN_DISCONNECT, + sizeof(struct hal_cmd_pan_disconnect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_PAN); + + /* check for valid data size for A2DP */ + test_datasize_valid("A2DP Connect+", HAL_SERVICE_ID_A2DP, + HAL_OP_A2DP_CONNECT, + sizeof(struct hal_cmd_a2dp_connect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + test_datasize_valid("A2DP Connect-", HAL_SERVICE_ID_A2DP, + HAL_OP_A2DP_CONNECT, + sizeof(struct hal_cmd_a2dp_connect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + test_datasize_valid("A2DP Disconnect+", HAL_SERVICE_ID_A2DP, + HAL_OP_A2DP_DISCONNECT, + sizeof(struct hal_cmd_a2dp_disconnect), 1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + test_datasize_valid("A2DP Disconnect-", HAL_SERVICE_ID_A2DP, + HAL_OP_A2DP_DISCONNECT, + sizeof(struct hal_cmd_a2dp_disconnect), -1, + HAL_SERVICE_ID_BLUETOOTH, HAL_SERVICE_ID_A2DP); + + return tester_run(); +} diff -Nru bluez-4.101/android/main.c bluez-5.23/android/main.c --- bluez-4.101/android/main.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/main.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,562 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#if defined(ANDROID) +#include +#include +#endif + +#include + +#include "src/log.h" +#include "src/sdpd.h" + +#include "lib/bluetooth.h" + +#include "ipc-common.h" +#include "ipc.h" +#include "bluetooth.h" +#include "socket.h" +#include "hidhost.h" +#include "hal-msg.h" +#include "a2dp.h" +#include "pan.h" +#include "avrcp.h" +#include "handsfree.h" +#include "gatt.h" +#include "health.h" + +#define STARTUP_GRACE_SECONDS 5 +#define SHUTDOWN_GRACE_SECONDS 10 + +static guint bluetooth_start_timeout = 0; + +static bdaddr_t adapter_bdaddr; + +static GMainLoop *event_loop; + +static struct ipc *hal_ipc = NULL; + +static bool services[HAL_SERVICE_ID_MAX + 1] = { false }; + +static void service_register(const void *buf, uint16_t len) +{ + const struct hal_cmd_register_module *m = buf; + uint8_t status; + + if (m->service_id > HAL_SERVICE_ID_MAX || services[m->service_id]) { + status = HAL_STATUS_FAILED; + goto failed; + } + + switch (m->service_id) { + case HAL_SERVICE_ID_BLUETOOTH: + if (!bt_bluetooth_register(hal_ipc, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_SOCKET: + bt_socket_register(hal_ipc, &adapter_bdaddr, m->mode); + + break; + case HAL_SERVICE_ID_HIDHOST: + if (!bt_hid_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_A2DP: + if (!bt_a2dp_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_PAN: + if (!bt_pan_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_AVRCP: + if (!bt_avrcp_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_HANDSFREE: + if (!bt_handsfree_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_GATT: + if (!bt_gatt_register(hal_ipc, &adapter_bdaddr)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + case HAL_SERVICE_ID_HEALTH: + if (!bt_health_register(hal_ipc, &adapter_bdaddr, m->mode)) { + status = HAL_STATUS_FAILED; + goto failed; + } + + break; + default: + DBG("service %u not supported", m->service_id); + status = HAL_STATUS_FAILED; + goto failed; + } + + services[m->service_id] = true; + + status = HAL_STATUS_SUCCESS; + + info("Service ID=%u registered", m->service_id); + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_REGISTER_MODULE, + status); +} + +static void service_unregister(const void *buf, uint16_t len) +{ + const struct hal_cmd_unregister_module *m = buf; + uint8_t status; + + if (m->service_id > HAL_SERVICE_ID_MAX || !services[m->service_id]) { + status = HAL_STATUS_FAILED; + goto failed; + } + + switch (m->service_id) { + case HAL_SERVICE_ID_BLUETOOTH: + bt_bluetooth_unregister(); + break; + case HAL_SERVICE_ID_SOCKET: + bt_socket_unregister(); + break; + case HAL_SERVICE_ID_HIDHOST: + bt_hid_unregister(); + break; + case HAL_SERVICE_ID_A2DP: + bt_a2dp_unregister(); + break; + case HAL_SERVICE_ID_PAN: + bt_pan_unregister(); + break; + case HAL_SERVICE_ID_AVRCP: + bt_avrcp_unregister(); + break; + case HAL_SERVICE_ID_HANDSFREE: + bt_handsfree_unregister(); + break; + case HAL_SERVICE_ID_GATT: + bt_gatt_unregister(); + break; + case HAL_SERVICE_ID_HEALTH: + bt_health_unregister(); + break; + default: + /* + * This would indicate bug in HAL, as unregister should not be + * called in init failed + */ + DBG("service %u not supported", m->service_id); + status = HAL_STATUS_FAILED; + goto failed; + } + + services[m->service_id] = false; + + status = HAL_STATUS_SUCCESS; + + info("Service ID=%u unregistered", m->service_id); + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_CORE, HAL_OP_UNREGISTER_MODULE, + status); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_REGISTER_MODULE */ + { service_register, false, sizeof(struct hal_cmd_register_module) }, + /* HAL_OP_UNREGISTER_MODULE */ + { service_unregister, false, sizeof(struct hal_cmd_unregister_module) }, +}; + +static void bluetooth_stopped(void) +{ + g_main_loop_quit(event_loop); +} + +static gboolean quit_eventloop(gpointer user_data) +{ + g_main_loop_quit(event_loop); + return FALSE; +} + +static void stop_bluetooth(void) +{ + static bool __stop = false; + + if (__stop) + return; + + __stop = true; + + if (!bt_bluetooth_stop(bluetooth_stopped)) { + g_main_loop_quit(event_loop); + return; + } + + g_timeout_add_seconds(SHUTDOWN_GRACE_SECONDS, quit_eventloop, NULL); +} + +static void ipc_disconnected(void *data) +{ + stop_bluetooth(); +} + +static void adapter_ready(int err, const bdaddr_t *addr) +{ + if (err < 0) { + error("Adapter initialization failed: %s", strerror(-err)); + exit(EXIT_FAILURE); + } + + bacpy(&adapter_bdaddr, addr); + + if (bluetooth_start_timeout > 0) { + g_source_remove(bluetooth_start_timeout); + bluetooth_start_timeout = 0; + } + + info("Adapter initialized"); + + hal_ipc = ipc_init(BLUEZ_HAL_SK_PATH, sizeof(BLUEZ_HAL_SK_PATH), + HAL_SERVICE_ID_MAX, true, + ipc_disconnected, NULL); + if (!hal_ipc) { + error("Failed to initialize IPC"); + exit(EXIT_FAILURE); + } + + ipc_register(hal_ipc, HAL_SERVICE_ID_CORE, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); +} + +static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + static bool __terminated = false; + struct signalfd_siginfo si; + ssize_t result; + int fd; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) + return FALSE; + + fd = g_io_channel_unix_get_fd(channel); + + result = read(fd, &si, sizeof(si)); + if (result != sizeof(si)) + return FALSE; + + switch (si.ssi_signo) { + case SIGINT: + case SIGTERM: + if (!__terminated) { + info("Terminating"); + stop_bluetooth(); + } + + __terminated = true; + break; + } + + return TRUE; +} + +static guint setup_signalfd(void) +{ + GIOChannel *channel; + guint source; + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { + perror("Failed to set signal mask"); + return 0; + } + + fd = signalfd(-1, &mask, 0); + if (fd < 0) { + perror("Failed to create signal descriptor"); + return 0; + } + + channel = g_io_channel_unix_new(fd); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +static gboolean option_version = FALSE; +static gint option_index = -1; +static gboolean option_dbg = FALSE; +static gboolean option_mgmt_dbg = FALSE; + +static GOptionEntry options[] = { + { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, + "Show version information and exit", NULL }, + { "index", 'i', 0, G_OPTION_ARG_INT, &option_index, + "Use specified controller", "INDEX"}, + { "debug", 'd', 0, G_OPTION_ARG_NONE, &option_dbg, + "Enable debug logs", NULL}, + { "mgmt-debug", 0, 0, G_OPTION_ARG_NONE, &option_mgmt_dbg, + "Enable mgmt debug logs", NULL}, + + { NULL } +}; + +static void cleanup_services(void) +{ + int i; + + DBG(""); + + for (i = HAL_SERVICE_ID_MAX; i > HAL_SERVICE_ID_CORE; i--) { + if (!services[i]) + continue; + + switch (i) { + case HAL_SERVICE_ID_BLUETOOTH: + bt_bluetooth_unregister(); + break; + case HAL_SERVICE_ID_SOCKET: + bt_socket_unregister(); + break; + case HAL_SERVICE_ID_HIDHOST: + bt_hid_unregister(); + break; + case HAL_SERVICE_ID_A2DP: + bt_a2dp_unregister(); + break; + case HAL_SERVICE_ID_AVRCP: + bt_avrcp_unregister(); + break; + case HAL_SERVICE_ID_PAN: + bt_pan_unregister(); + break; + case HAL_SERVICE_ID_HANDSFREE: + bt_handsfree_unregister(); + break; + case HAL_SERVICE_ID_GATT: + bt_gatt_unregister(); + break; + case HAL_SERVICE_ID_HEALTH: + bt_health_unregister(); + break; + } + + services[i] = false; + } +} + +static bool set_capabilities(void) +{ +#if defined(ANDROID) + struct __user_cap_header_struct header; + struct __user_cap_data_struct cap; + + header.version = _LINUX_CAPABILITY_VERSION; + header.pid = 0; + + /* + * CAP_NET_ADMIN: Allow use of MGMT interface + * CAP_NET_BIND_SERVICE: Allow use of privileged PSM + * CAP_NET_RAW: Allow use of bnep ioctl calls + */ + cap.effective = cap.permitted = + CAP_TO_MASK(CAP_NET_RAW) | + CAP_TO_MASK(CAP_NET_ADMIN) | + CAP_TO_MASK(CAP_NET_BIND_SERVICE); + cap.inheritable = 0; + + /* don't clear capabilities when dropping root */ + if (prctl(PR_SET_KEEPCAPS, 1) < 0) { + error("%s: prctl(): %s", __func__, strerror(errno)); + return false; + } + + /* Android bluetooth user UID=1002 */ + if (setuid(1002) < 0) { + error("%s: setuid(): %s", __func__, strerror(errno)); + return false; + } + + /* TODO: Move to cap_set_proc once bionic support it */ + if (capset(&header, &cap) < 0) { + error("%s: capset(): %s", __func__, strerror(errno)); + return false; + } + + /* TODO: Move to cap_get_proc once bionic support it */ + if (capget(&header, &cap) < 0) { + error("%s: capget(): %s", __func__, strerror(errno)); + return false; + } + + DBG("Caps: eff: 0x%x, perm: 0x%x, inh: 0x%x", cap.effective, + cap.permitted, cap.inheritable); + +#endif + return true; +} + +int main(int argc, char *argv[]) +{ + GOptionContext *context; + GError *err = NULL; + guint signal; + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, options, NULL); + + if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) { + if (err != NULL) { + g_printerr("%s\n", err->message); + g_error_free(err); + } else + g_printerr("An unknown error occurred\n"); + + exit(EXIT_FAILURE); + } + + g_option_context_free(context); + + if (option_version == TRUE) { + printf("%s\n", VERSION); + exit(EXIT_SUCCESS); + } + + signal = setup_signalfd(); + if (!signal) + return EXIT_FAILURE; + + if (option_dbg || option_mgmt_dbg) + __btd_log_init("*", 0); + else + __btd_log_init(NULL, 0); + + if (!set_capabilities()) { + __btd_log_cleanup(); + g_source_remove(signal); + return EXIT_FAILURE; + } + + bluetooth_start_timeout = g_timeout_add_seconds(STARTUP_GRACE_SECONDS, + quit_eventloop, NULL); + if (bluetooth_start_timeout == 0) { + error("Failed to init startup timeout"); + __btd_log_cleanup(); + g_source_remove(signal); + return EXIT_FAILURE; + } + + if (!bt_bluetooth_start(option_index, option_mgmt_dbg, adapter_ready)) { + __btd_log_cleanup(); + g_source_remove(bluetooth_start_timeout); + g_source_remove(signal); + return EXIT_FAILURE; + } + + /* Use params: mtu = 0, flags = 0 */ + start_sdp_server(0, 0); + + DBG("Entering main loop"); + + event_loop = g_main_loop_new(NULL, FALSE); + + g_main_loop_run(event_loop); + + g_source_remove(signal); + + if (bluetooth_start_timeout > 0) + g_source_remove(bluetooth_start_timeout); + + cleanup_services(); + + stop_sdp_server(); + bt_bluetooth_cleanup(); + g_main_loop_unref(event_loop); + + /* If no adapter was initialized, hal_ipc is NULL */ + if (hal_ipc) { + ipc_unregister(hal_ipc, HAL_SERVICE_ID_CORE); + ipc_cleanup(hal_ipc); + } + + info("Exit"); + + __btd_log_cleanup(); + + return EXIT_SUCCESS; +} diff -Nru bluez-4.101/android/Makefile.am bluez-5.23/android/Makefile.am --- bluez-4.101/android/Makefile.am 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/Makefile.am 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,338 @@ +if ANDROID + +AM_CFLAGS += -DANDROID_VERSION=0xFFFFFF + +android_plugindir = $(abs_top_srcdir)/android/.libs + +noinst_PROGRAMS += android/system-emulator + +android_system_emulator_SOURCES = android/system-emulator.c \ + monitor/mainloop.h monitor/mainloop.c + +noinst_PROGRAMS += android/bluetoothd-snoop + +android_bluetoothd_snoop_SOURCES = android/bluetoothd-snoop.c \ + monitor/mainloop.h monitor/mainloop.c \ + src/shared/btsnoop.h src/shared/btsnoop.c + +noinst_PROGRAMS += android/bluetoothd + +android_bluetoothd_SOURCES = android/main.c \ + src/log.c \ + android/hal-msg.h \ + android/audio-msg.h \ + android/sco-msg.h \ + android/utils.h \ + src/sdpd-database.c src/sdpd-server.c \ + src/sdpd-service.c src/sdpd-request.c \ + src/uuid-helper.h src/uuid-helper.c \ + src/eir.h src/eir.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/ringbuf.h src/shared/ringbuf.c \ + src/shared/hfp.h src/shared/hfp.c \ + src/shared/gatt-db.h src/shared/gatt-db.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/uhid.h src/shared/uhid.c \ + android/bluetooth.h android/bluetooth.c \ + android/hidhost.h android/hidhost.c \ + android/scpp.h android/scpp.c \ + android/dis.h android/dis.c \ + android/bas.h android/bas.c \ + android/hog.h android/hog.c \ + android/ipc-common.h \ + android/ipc.h android/ipc.c \ + android/avdtp.h android/avdtp.c \ + android/a2dp.h android/a2dp.c \ + android/avctp.h android/avctp.c \ + android/avrcp.h android/avrcp.c \ + android/avrcp-lib.h android/avrcp-lib.c \ + android/socket.h android/socket.c \ + android/pan.h android/pan.c \ + android/handsfree.h android/handsfree.c \ + android/gatt.h android/gatt.c \ + android/health.h android/health.c \ + android/mcap-lib.h android/mcap-lib.c \ + attrib/att.c attrib/att.h \ + attrib/gatt.c attrib/gatt.h \ + attrib/gattrib.c attrib/gattrib.h \ + btio/btio.h btio/btio.c \ + src/sdp-client.h src/sdp-client.c \ + profiles/network/bnep.h profiles/network/bnep.c + +android_bluetoothd_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +plugin_LTLIBRARIES += android/bluetooth.default.la + +android_bluetooth_default_la_SOURCES = android/hal.h android/hal-bluetooth.c \ + android/hal-socket.c \ + android/hal-hidhost.c \ + android/hal-health.c \ + android/hal-pan.c \ + android/hal-a2dp.c \ + android/hal-avrcp.c \ + android/hal-handsfree.c \ + android/hal-gatt.c \ + android/hardware/bluetooth.h \ + android/hardware/bt_av.h \ + android/hardware/bt_gatt.h \ + android/hardware/bt_gatt_client.h \ + android/hardware/bt_gatt_server.h \ + android/hardware/bt_gatt_types.h \ + android/hardware/bt_hf.h \ + android/hardware/bt_hh.h \ + android/hardware/bt_hl.h \ + android/hardware/bt_pan.h \ + android/hardware/bt_rc.h \ + android/hardware/bt_sock.h \ + android/hardware/hardware.h \ + android/cutils/properties.h \ + android/ipc-common.h \ + android/hal-log.h \ + android/hal-ipc.h android/hal-ipc.c \ + android/hal-utils.h android/hal-utils.c + +android_bluetooth_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android +android_bluetooth_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ + -no-undefined + +noinst_PROGRAMS += android/mcaptest + +android_mcaptest_SOURCES = android/mcaptest.c \ + src/log.h src/log.c \ + btio/btio.h btio/btio.c \ + android/mcap-lib.h android/mcap-lib.c + +android_mcaptest_CFLAGS = $(AM_CFLAGS) + +android_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +noinst_PROGRAMS += android/avdtptest + +android_avdtptest_SOURCES = android/avdtptest.c \ + src/log.h src/log.c \ + btio/btio.h btio/btio.c \ + android/avdtp.h android/avdtp.c + +android_avdtptest_CFLAGS = $(AM_CFLAGS) + +android_avdtptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +noinst_PROGRAMS += android/haltest + +android_haltest_SOURCES = android/client/haltest.c \ + android/client/pollhandler.h \ + android/client/pollhandler.c \ + android/client/terminal.h \ + android/client/terminal.c \ + android/client/history.h \ + android/client/history.c \ + android/client/tabcompletion.c \ + android/client/if-main.h \ + android/client/if-av.c \ + android/client/if-rc.c \ + android/client/if-bt.c \ + android/client/if-gatt.c \ + android/client/if-hf.c \ + android/client/if-hh.c \ + android/client/if-pan.c \ + android/client/if-hl.c \ + android/client/if-sock.c \ + android/client/if-audio.c \ + android/client/if-sco.c \ + android/hardware/hardware.c \ + android/hal-utils.h android/hal-utils.c + +android_haltest_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ + -DPLUGINDIR=\""$(android_plugindir)"\" + +android_haltest_LDFLAGS = -pthread -ldl -lm + +noinst_PROGRAMS += android/android-tester + +android_android_tester_SOURCES = emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c \ + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c \ + monitor/rfcomm.h \ + android/hardware/hardware.c \ + android/tester-bluetooth.c \ + android/tester-socket.c \ + android/tester-hidhost.c \ + android/tester-pan.c \ + android/tester-hdp.c \ + android/tester-a2dp.c \ + android/tester-avrcp.c \ + android/tester-gatt.c \ + android/tester-main.h android/tester-main.c + +android_android_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ + -DPLUGINDIR=\""$(android_plugindir)"\" + +android_android_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +android_android_tester_LDFLAGS = -pthread -ldl + +noinst_PROGRAMS += android/ipc-tester + +android_ipc_tester_SOURCES = emulator/btdev.h emulator/btdev.c \ + emulator/bthost.h emulator/bthost.c \ + emulator/smp.c \ + src/shared/crypto.h src/shared/crypto.c \ + src/shared/io.h src/shared/io-glib.c \ + src/shared/queue.h src/shared/queue.c \ + src/shared/util.h src/shared/util.c \ + src/shared/mgmt.h src/shared/mgmt.c \ + src/shared/hciemu.h src/shared/hciemu.c \ + src/shared/tester.h src/shared/tester.c \ + src/shared/timeout.h src/shared/timeout-glib.c \ + android/hal-utils.h android/hal-utils.c \ + android/ipc-common.h android/ipc-tester.c + +android_ipc_tester_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android + +android_ipc_tester_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ + +android_audio_a2dp_default_la_SOURCES = android/audio-msg.h \ + android/hal-msg.h \ + android/hal-audio.h \ + android/hal-audio.c \ + android/hal-audio-sbc.c \ + android/hal-audio-aptx.c \ + android/hardware/audio.h \ + android/hardware/audio_effect.h \ + android/hardware/hardware.h \ + android/system/audio.h \ + src/shared/queue.h \ + src/shared/queue.c + +android_audio_sco_default_la_SOURCES = android/hal-log.h \ + android/sco-msg.h \ + android/hal-sco.c \ + android/hardware/audio.h \ + android/hardware/audio_effect.h \ + android/hardware/hardware.h \ + android/audio_utils/resampler.c \ + android/audio_utils/resampler.h \ + android/system/audio.h + +android_audio_sco_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android + +android_audio_sco_default_la_LIBADD = @SPEEXDSP_LIBS@ + +android_audio_sco_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ + -no-undefined -lrt + +plugin_LTLIBRARIES += android/audio.sco.default.la + +unit_tests += android/test-ipc + +android_test_ipc_SOURCES = android/test-ipc.c \ + src/shared/util.h src/shared/util.c \ + src/log.h src/log.c \ + android/ipc-common.h \ + android/ipc.c android/ipc.h +android_test_ipc_LDADD = @GLIB_LIBS@ + +plugin_LTLIBRARIES += android/audio.a2dp.default.la + +android_audio_a2dp_default_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/android \ + @SBC_CFLAGS@ + +android_audio_a2dp_default_la_LIBADD = @SBC_LIBS@ + +android_audio_a2dp_default_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ + -no-undefined -pthread -lrt + +endif + +EXTRA_DIST += android/Android.mk android/README \ + android/init.bluetooth.rc \ + android/hal-ipc-api.txt \ + android/audio-ipc-api.txt \ + android/cts.txt \ + android/pics-rfcomm.txt \ + android/pics-spp.txt \ + android/pics-sdp.txt \ + android/pics-l2cap.txt \ + android/pics-gap.txt \ + android/pics-did.txt \ + android/pics-hid.txt \ + android/pics-pan.txt \ + android/pics-opp.txt \ + android/pics-map.txt \ + android/pics-pbap.txt \ + android/pics-a2dp.txt \ + android/pics-avctp.txt \ + android/pics-avrcp.txt \ + android/pics-hsp.txt \ + android/pics-hfp.txt \ + android/pics-gatt.txt \ + android/pics-mcap.txt \ + android/pics-hdp.txt \ + android/pics-iopt.txt \ + android/pics-sm.txt \ + android/pics-mps.txt \ + android/pics-hogp.txt \ + android/pics-scpp.txt \ + android/pics-dis.txt \ + android/pics-avdtp.txt \ + android/pixit-l2cap.txt \ + android/pixit-gap.txt \ + android/pixit-did.txt \ + android/pixit-hid.txt \ + android/pixit-pan.txt \ + android/pixit-opp.txt \ + android/pixit-map.txt \ + android/pixit-pbap.txt \ + android/pixit-a2dp.txt \ + android/pixit-avctp.txt \ + android/pixit-avrcp.txt \ + android/pixit-hsp.txt \ + android/pixit-hfp.txt \ + android/pixit-gatt.txt \ + android/pixit-mcap.txt \ + android/pixit-hdp.txt \ + android/pixit-iopt.txt \ + android/pixit-sm.txt \ + android/pixit-mps.txt \ + android/pixit-hogp.txt \ + android/pixit-scpp.txt \ + android/pixit-dis.txt \ + android/pixit-rfcomm.txt \ + android/pixit-spp.txt \ + android/pixit-avdtp.txt \ + android/pts-rfcomm.txt \ + android/pts-spp.txt \ + android/pts-l2cap.txt \ + android/pts-gap.txt \ + android/pts-did.txt \ + android/pts-hid.txt \ + android/pts-pan.txt \ + android/pts-opp.txt \ + android/pts-map.txt \ + android/pts-a2dp.txt \ + android/pts-avrcp.txt \ + android/pts-avctp.txt \ + android/pts-pbap.txt \ + android/pts-hfp.txt \ + android/pts-gatt.txt \ + android/pts-hsp.txt \ + android/pts-iopt.txt \ + android/pts-hdp.txt \ + android/pts-mcap.txt \ + android/pts-mps.txt \ + android/pts-sm.txt \ + android/pts-hogp.txt \ + android/pts-scpp.txt \ + android/pts-dis.txt \ + android/pts-avdtp.txt diff -Nru bluez-4.101/android/mcap-lib.c bluez-5.23/android/mcap-lib.c --- bluez-4.101/android/mcap-lib.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/mcap-lib.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,3181 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. + * Copyright (C) 2010 Signove + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "bluetooth/l2cap.h" +#include "btio/btio.h" +#include "src/log.h" + +#include "mcap-lib.h" + +#define MCAP_BTCLOCK_HALF (MCAP_BTCLOCK_FIELD / 2) +#define CLK CLOCK_MONOTONIC + +#define MCAP_CSP_ERROR g_quark_from_static_string("mcap-csp-error-quark") +#define MAX_RETRIES 10 +#define SAMPLE_COUNT 20 + +#define RESPONSE_TIMER 6 /* seconds */ +#define MAX_CACHED 10 /* 10 devices */ + +#define MCAP_ERROR g_quark_from_static_string("mcap-error-quark") + +#define RELEASE_TIMER(__mcl) do { \ + if (__mcl->tid) { \ + g_source_remove(__mcl->tid); \ + __mcl->tid = 0; \ + } \ +} while(0) + +struct mcap_csp { + uint64_t base_tmstamp; /* CSP base timestamp */ + struct timespec base_time; /* CSP base time when timestamp set */ + guint local_caps; /* CSP-Master: have got remote caps */ + guint remote_caps; /* CSP-Slave: remote master got caps */ + guint rem_req_acc; /* CSP-Slave: accuracy required by master */ + guint ind_expected; /* CSP-Master: indication expected */ + uint8_t csp_req; /* CSP-Master: Request control flag */ + guint ind_timer; /* CSP-Slave: indication timer */ + guint set_timer; /* CSP-Slave: delayed set timer */ + void *set_data; /* CSP-Slave: delayed set data */ + void *csp_priv_data; /* CSP-Master: In-flight request data */ +}; + +struct mcap_sync_cap_cbdata { + mcap_sync_cap_cb cb; + gpointer user_data; +}; + +struct mcap_sync_set_cbdata { + mcap_sync_set_cb cb; + gpointer user_data; +}; + +struct csp_caps { + int ts_acc; /* timestamp accuracy */ + int ts_res; /* timestamp resolution */ + int latency; /* Read BT clock latency */ + int preempt_thresh; /* Preemption threshold for latency */ + int syncleadtime_ms; /* SyncLeadTime in ms */ +}; + +struct sync_set_data { + uint8_t update; + uint32_t sched_btclock; + uint64_t timestamp; + int ind_freq; + gboolean role; +}; + +struct connect_mcl { + struct mcap_mcl *mcl; /* MCL for this operation */ + mcap_mcl_connect_cb connect_cb; /* Connect callback */ + GDestroyNotify destroy; /* Destroy callback */ + gpointer user_data; /* Callback user data */ +}; + +typedef union { + mcap_mdl_operation_cb op; + mcap_mdl_operation_conf_cb op_conf; + mcap_mdl_notify_cb notify; +} mcap_cb_type; + +struct mcap_mdl_op_cb { + struct mcap_mdl *mdl; /* MDL for this operation */ + mcap_cb_type cb; /* Operation callback */ + GDestroyNotify destroy; /* Destroy callback */ + gpointer user_data; /* Callback user data */ +}; + +/* MCAP finite state machine functions */ +static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); +static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); +static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t l); + +static void (*proc_req[])(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) = { + proc_req_connected, + proc_req_pending, + proc_req_active +}; + +static gboolean csp_caps_initialized = FALSE; +struct csp_caps _caps; + +static void mcap_cache_mcl(struct mcap_mcl *mcl); + +static void default_mdl_connected_cb(struct mcap_mdl *mdl, gpointer data) +{ + DBG("MCAP Unmanaged mdl connection"); +} + +static void default_mdl_closed_cb(struct mcap_mdl *mdl, gpointer data) +{ + DBG("MCAP Unmanaged mdl closed"); +} + +static void default_mdl_deleted_cb(struct mcap_mdl *mdl, gpointer data) +{ + DBG("MCAP Unmanaged mdl deleted"); +} + +static void default_mdl_aborted_cb(struct mcap_mdl *mdl, gpointer data) +{ + DBG("MCAP Unmanaged mdl aborted"); +} + +static uint8_t default_mdl_conn_req_cb(struct mcap_mcl *mcl, + uint8_t mdepid, uint16_t mdlid, + uint8_t *conf, gpointer data) +{ + DBG("MCAP mdl remote connection aborted"); + /* Due to this callback isn't managed this request won't be supported */ + return MCAP_REQUEST_NOT_SUPPORTED; +} + +static uint8_t default_mdl_reconn_req_cb(struct mcap_mdl *mdl, + gpointer data) +{ + DBG("MCAP mdl remote reconnection aborted"); + /* Due to this callback isn't managed this request won't be supported */ + return MCAP_REQUEST_NOT_SUPPORTED; +} + +static void set_default_cb(struct mcap_mcl *mcl) +{ + if (!mcl->cb) + mcl->cb = g_new0(struct mcap_mdl_cb, 1); + + mcl->cb->mdl_connected = default_mdl_connected_cb; + mcl->cb->mdl_closed = default_mdl_closed_cb; + mcl->cb->mdl_deleted = default_mdl_deleted_cb; + mcl->cb->mdl_aborted = default_mdl_aborted_cb; + mcl->cb->mdl_conn_req = default_mdl_conn_req_cb; + mcl->cb->mdl_reconn_req = default_mdl_reconn_req_cb; +} + +static char *error2str(uint8_t rc) +{ + switch (rc) { + case MCAP_SUCCESS: + return "Success"; + case MCAP_INVALID_OP_CODE: + return "Invalid Op Code"; + case MCAP_INVALID_PARAM_VALUE: + return "Invalid Parameter Value"; + case MCAP_INVALID_MDEP: + return "Invalid MDEP"; + case MCAP_MDEP_BUSY: + return "MDEP Busy"; + case MCAP_INVALID_MDL: + return "Invalid MDL"; + case MCAP_MDL_BUSY: + return "MDL Busy"; + case MCAP_INVALID_OPERATION: + return "Invalid Operation"; + case MCAP_RESOURCE_UNAVAILABLE: + return "Resource Unavailable"; + case MCAP_UNSPECIFIED_ERROR: + return "Unspecified Error"; + case MCAP_REQUEST_NOT_SUPPORTED: + return "Request Not Supported"; + case MCAP_CONFIGURATION_REJECTED: + return "Configuration Rejected"; + default: + return "Unknown Response Code"; + } +} + +static gboolean mcap_send_std_opcode(struct mcap_mcl *mcl, void *cmd, + uint32_t size, GError **err) +{ + if (mcl->state == MCL_IDLE) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, + "MCL is not connected"); + return FALSE; + } + + if (mcl->req != MCL_AVAILABLE) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_RESOURCE_UNAVAILABLE, + "Pending request"); + return FALSE; + } + + if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_REQUEST_NOT_SUPPORTED, + "Remote does not support standard opcodes"); + return FALSE; + } + + if (mcl->state == MCL_PENDING) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_OPERATION, + "Not Std Op. Codes can be sent in PENDING State"); + return FALSE; + } + + if (mcap_send_data(g_io_channel_unix_get_fd(mcl->cc), cmd, size) < 0) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, + "Command can't be sent, write error"); + return FALSE; + } + + mcl->lcmd = cmd; + mcl->req = MCL_WAITING_RSP; + + return TRUE; +} + +static void update_mcl_state(struct mcap_mcl *mcl) +{ + GSList *l; + struct mcap_mdl *mdl; + + if (mcl->state == MCL_PENDING) + return; + + for (l = mcl->mdls; l; l = l->next) { + mdl = l->data; + + if (mdl->state == MDL_CONNECTED) { + mcl->state = MCL_ACTIVE; + return; + } + } + + mcl->state = MCL_CONNECTED; +} + +static void shutdown_mdl(struct mcap_mdl *mdl) +{ + mdl->state = MDL_CLOSED; + + if (mdl->wid) { + g_source_remove(mdl->wid); + mdl->wid = 0; + } + + if (mdl->dc) { + g_io_channel_shutdown(mdl->dc, TRUE, NULL); + g_io_channel_unref(mdl->dc); + mdl->dc = NULL; + } +} + +static void free_mdl(struct mcap_mdl *mdl) +{ + if (!mdl) + return; + + mcap_mcl_unref(mdl->mcl); + g_free(mdl); +} + +static int cmp_mdl_state(gconstpointer a, gconstpointer b) +{ + const struct mcap_mdl *mdl = a; + const MDLState *st = b; + + if (mdl->state == *st) + return 0; + else if (mdl->state < *st) + return -1; + else + return 1; +} + +static void free_mcap_mdl_op(struct mcap_mdl_op_cb *op) +{ + if (op->destroy) + op->destroy(op->user_data); + + if (op->mdl) + mcap_mdl_unref(op->mdl); + + g_free(op); +} + +static void free_mcl_priv_data(struct mcap_mcl *mcl) +{ + free_mcap_mdl_op(mcl->priv_data); + mcl->priv_data = NULL; +} + +static void mcap_notify_error(struct mcap_mcl *mcl, GError *err) +{ + struct mcap_mdl_op_cb *con = mcl->priv_data; + struct mcap_mdl *mdl; + MDLState st; + GSList *l; + + if (!con || !mcl->lcmd) + return; + + switch (mcl->lcmd[0]) { + case MCAP_MD_CREATE_MDL_REQ: + st = MDL_WAITING; + l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); + mdl = l->data; + mcl->mdls = g_slist_remove(mcl->mdls, mdl); + mcap_mdl_unref(mdl); + update_mcl_state(mcl); + con->cb.op_conf(NULL, 0, err, con->user_data); + break; + case MCAP_MD_ABORT_MDL_REQ: + st = MDL_WAITING; + l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); + shutdown_mdl(l->data); + update_mcl_state(mcl); + con->cb.notify(err, con->user_data); + break; + case MCAP_MD_DELETE_MDL_REQ: + for (l = mcl->mdls; l; l = l->next) { + mdl = l->data; + if (mdl->state == MDL_DELETING) + mdl->state = (mdl->dc) ? MDL_CONNECTED : + MDL_CLOSED; + } + update_mcl_state(mcl); + con->cb.notify(err, con->user_data); + break; + case MCAP_MD_RECONNECT_MDL_REQ: + st = MDL_WAITING; + l = g_slist_find_custom(mcl->mdls, &st, cmp_mdl_state); + shutdown_mdl(l->data); + update_mcl_state(mcl); + con->cb.op(NULL, err, con->user_data); + break; + } + + free_mcl_priv_data(mcl); + g_free(mcl->lcmd); + mcl->lcmd = NULL; +} + +int mcap_send_data(int sock, const void *buf, uint32_t size) +{ + const uint8_t *buf_b = buf; + uint32_t sent = 0; + + while (sent < size) { + int n = write(sock, buf_b + sent, size - sent); + if (n < 0) + return -1; + sent += n; + } + + return 0; +} + +static int mcap_send_cmd(struct mcap_mcl *mcl, uint8_t oc, uint8_t rc, + uint16_t mdl, uint8_t *data, size_t len) +{ + mcap_rsp *cmd; + int sock, sent; + + if (mcl->cc == NULL) + return -1; + + sock = g_io_channel_unix_get_fd(mcl->cc); + + cmd = g_malloc(sizeof(mcap_rsp) + len); + cmd->op = oc; + cmd->rc = rc; + cmd->mdl = htons(mdl); + + if (data && len > 0) + memcpy(cmd->data, data, len); + + sent = mcap_send_data(sock, cmd, sizeof(mcap_rsp) + len); + g_free(cmd); + + return sent; +} + +static struct mcap_mdl *get_mdl(struct mcap_mcl *mcl, uint16_t mdlid) +{ + GSList *l; + struct mcap_mdl *mdl; + + for (l = mcl->mdls; l; l = l->next) { + mdl = l->data; + if (mdlid == mdl->mdlid) + return mdl; + } + + return NULL; +} + +static uint16_t generate_mdlid(struct mcap_mcl *mcl) +{ + uint16_t mdlid = mcl->next_mdl; + struct mcap_mdl *mdl; + + do { + mdl = get_mdl(mcl, mdlid); + if (!mdl) { + mcl->next_mdl = (mdlid % MCAP_MDLID_FINAL) + 1; + return mdlid; + } else + mdlid = (mdlid % MCAP_MDLID_FINAL) + 1; + } while (mdlid != mcl->next_mdl); + + /* No more mdlids availables */ + return 0; +} + +static mcap_md_req *create_req(uint8_t op, uint16_t mdl_id) +{ + mcap_md_req *req_cmd; + + req_cmd = g_new0(mcap_md_req, 1); + + req_cmd->op = op; + req_cmd->mdl = htons(mdl_id); + + return req_cmd; +} + +static mcap_md_create_mdl_req *create_mdl_req(uint16_t mdl_id, uint8_t mdep, + uint8_t conf) +{ + mcap_md_create_mdl_req *req_mdl; + + req_mdl = g_new0(mcap_md_create_mdl_req, 1); + + req_mdl->op = MCAP_MD_CREATE_MDL_REQ; + req_mdl->mdl = htons(mdl_id); + req_mdl->mdep = mdep; + req_mdl->conf = conf; + + return req_mdl; +} + +static int compare_mdl(gconstpointer a, gconstpointer b) +{ + const struct mcap_mdl *mdla = a; + const struct mcap_mdl *mdlb = b; + + if (mdla->mdlid == mdlb->mdlid) + return 0; + else if (mdla->mdlid < mdlb->mdlid) + return -1; + else + return 1; +} + +static gboolean wait_response_timer(gpointer data) +{ + struct mcap_mcl *mcl = data; + + GError *gerr = NULL; + + RELEASE_TIMER(mcl); + + g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED, + "Timeout waiting response"); + + mcap_notify_error(mcl, gerr); + + g_error_free(gerr); + mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); + mcap_cache_mcl(mcl); + + return FALSE; +} + +gboolean mcap_create_mdl(struct mcap_mcl *mcl, + uint8_t mdepid, + uint8_t conf, + mcap_mdl_operation_conf_cb connect_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err) +{ + struct mcap_mdl *mdl; + struct mcap_mdl_op_cb *con; + mcap_md_create_mdl_req *cmd; + uint16_t id; + + id = generate_mdlid(mcl); + if (!id) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, + "Not more mdlids available"); + return FALSE; + } + + mdl = g_new0(struct mcap_mdl, 1); + mdl->mcl = mcap_mcl_ref(mcl); + mdl->mdlid = id; + mdl->mdep_id = mdepid; + mdl->state = MDL_WAITING; + + con = g_new0(struct mcap_mdl_op_cb, 1); + con->mdl = mcap_mdl_ref(mdl); + con->cb.op_conf = connect_cb; + con->destroy = destroy; + con->user_data = user_data; + + cmd = create_mdl_req(id, mdepid, conf); + if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_create_mdl_req), + err)) { + mcap_mdl_unref(con->mdl); + g_free(con); + g_free(cmd); + return FALSE; + } + + mcl->state = MCL_ACTIVE; + mcl->priv_data = con; + + mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl), + compare_mdl); + mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, + mcl); + return TRUE; +} + +gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl, + mcap_mdl_operation_cb reconnect_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err) +{ + struct mcap_mdl_op_cb *con; + struct mcap_mcl *mcl = mdl->mcl; + mcap_md_req *cmd; + + if (mdl->state != MDL_CLOSED) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, + "MDL is not closed"); + return FALSE; + } + + cmd = create_req(MCAP_MD_RECONNECT_MDL_REQ, mdl->mdlid); + if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { + g_free(cmd); + return FALSE; + } + + mdl->state = MDL_WAITING; + + con = g_new0(struct mcap_mdl_op_cb, 1); + con->mdl = mcap_mdl_ref(mdl); + con->cb.op = reconnect_cb; + con->destroy = destroy; + con->user_data = user_data; + + mcl->state = MCL_ACTIVE; + mcl->priv_data = con; + + mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, + mcl); + return TRUE; +} + +static gboolean send_delete_req(struct mcap_mcl *mcl, + struct mcap_mdl_op_cb *con, + uint16_t mdlid, + GError **err) +{ + mcap_md_req *cmd; + + cmd = create_req(MCAP_MD_DELETE_MDL_REQ, mdlid); + if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { + g_free(cmd); + return FALSE; + } + + mcl->priv_data = con; + + mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, + mcl); + return TRUE; +} + +gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl, + mcap_mdl_notify_cb delete_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err) +{ + GSList *l; + struct mcap_mdl *mdl; + struct mcap_mdl_op_cb *con; + + DBG("MCL in state: %d", mcl->state); + if (!mcl->mdls) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, + "There are not MDLs created"); + return FALSE; + } + + for (l = mcl->mdls; l; l = l->next) { + mdl = l->data; + if (mdl->state != MDL_WAITING) + mdl->state = MDL_DELETING; + } + + con = g_new0(struct mcap_mdl_op_cb, 1); + con->mdl = NULL; + con->cb.notify = delete_cb; + con->destroy = destroy; + con->user_data = user_data; + + + if (!send_delete_req(mcl, con, MCAP_ALL_MDLIDS, err)) { + g_free(con); + return FALSE; + } + + return TRUE; +} + +gboolean mcap_delete_mdl(struct mcap_mdl *mdl, mcap_mdl_notify_cb delete_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err) +{ + struct mcap_mcl *mcl= mdl->mcl; + struct mcap_mdl_op_cb *con; + GSList *l; + + l = g_slist_find(mcl->mdls, mdl); + + if (!l) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL, + "%s" , error2str(MCAP_INVALID_MDEP)); + return FALSE; + } + + if (mdl->state == MDL_WAITING) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, + "Mdl is not created"); + return FALSE; + } + + mdl->state = MDL_DELETING; + + con = g_new0(struct mcap_mdl_op_cb, 1); + con->mdl = mcap_mdl_ref(mdl); + con->cb.notify = delete_cb; + con->destroy = destroy; + con->user_data = user_data; + + if (!send_delete_req(mcl, con, mdl->mdlid, err)) { + mcap_mdl_unref(con->mdl); + g_free(con); + return FALSE; + } + + return TRUE; +} + +gboolean mcap_mdl_abort(struct mcap_mdl *mdl, mcap_mdl_notify_cb abort_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err) +{ + struct mcap_mdl_op_cb *con; + struct mcap_mcl *mcl = mdl->mcl; + mcap_md_req *cmd; + + if (mdl->state != MDL_WAITING) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_FAILED, + "Mdl in invalid state"); + return FALSE; + } + + cmd = create_req(MCAP_MD_ABORT_MDL_REQ, mdl->mdlid); + if (!mcap_send_std_opcode(mcl, cmd, sizeof(mcap_md_req), err)) { + g_free(cmd); + return FALSE; + } + + con = g_new0(struct mcap_mdl_op_cb, 1); + con->mdl = mcap_mdl_ref(mdl); + con->cb.notify = abort_cb; + con->destroy = destroy; + con->user_data = user_data; + + mcl->priv_data = con; + mcl->tid = g_timeout_add_seconds(RESPONSE_TIMER, wait_response_timer, + mcl); + return TRUE; +} + +static struct mcap_mcl *find_mcl(GSList *list, const bdaddr_t *addr) +{ + struct mcap_mcl *mcl; + + for (; list; list = list->next) { + mcl = list->data; + + if (!bacmp(&mcl->addr, addr)) + return mcl; + } + + return NULL; +} + +int mcap_mdl_get_fd(struct mcap_mdl *mdl) +{ + if (!mdl || mdl->state != MDL_CONNECTED) + return -ENOTCONN; + + return g_io_channel_unix_get_fd(mdl->dc); +} + +uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl) +{ + if (!mdl) + return MCAP_MDLID_RESERVED; + + return mdl->mdlid; +} + +static void close_mcl(struct mcap_mcl *mcl, gboolean cache_requested) +{ + gboolean save = ((!(mcl->ctrl & MCAP_CTRL_FREE)) && cache_requested); + + RELEASE_TIMER(mcl); + + if (mcl->cc) { + g_io_channel_shutdown(mcl->cc, TRUE, NULL); + g_io_channel_unref(mcl->cc); + mcl->cc = NULL; + } + + if (mcl->wid) { + g_source_remove(mcl->wid); + mcl->wid = 0; + } + + if (mcl->lcmd) { + g_free(mcl->lcmd); + mcl->lcmd = NULL; + } + + if (mcl->priv_data) + free_mcl_priv_data(mcl); + + g_slist_foreach(mcl->mdls, (GFunc) shutdown_mdl, NULL); + + mcap_sync_stop(mcl); + + mcl->state = MCL_IDLE; + + if (save) + return; + + g_slist_foreach(mcl->mdls, (GFunc) mcap_mdl_unref, NULL); + g_slist_free(mcl->mdls); + mcl->mdls = NULL; +} + +static void mcap_mcl_shutdown(struct mcap_mcl *mcl) +{ + close_mcl(mcl, TRUE); +} + +static void mcap_mcl_release(struct mcap_mcl *mcl) +{ + close_mcl(mcl, FALSE); +} + +static void mcap_cache_mcl(struct mcap_mcl *mcl) +{ + GSList *l; + struct mcap_mcl *last; + int len; + + if (mcl->ctrl & MCAP_CTRL_CACHED) + return; + + mcl->mi->mcls = g_slist_remove(mcl->mi->mcls, mcl); + + if (mcl->ctrl & MCAP_CTRL_NOCACHE) { + mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); + mcap_mcl_release(mcl); + mcap_mcl_unref(mcl); + return; + } + + DBG("Caching MCL"); + + len = g_slist_length(mcl->mi->cached); + if (len == MAX_CACHED) { + /* Remove the latest cached mcl */ + l = g_slist_last(mcl->mi->cached); + last = l->data; + mcl->mi->cached = g_slist_remove(mcl->mi->cached, last); + last->ctrl &= ~MCAP_CTRL_CACHED; + if (last->ctrl & MCAP_CTRL_CONN) { + /* + * We have to release this MCL if connection is not + * successful + */ + last->ctrl |= MCAP_CTRL_FREE; + } else { + mcap_mcl_release(last); + last->mi->mcl_uncached_cb(last, last->mi->user_data); + } + mcap_mcl_unref(last); + } + + mcl->mi->cached = g_slist_prepend(mcl->mi->cached, mcl); + mcl->ctrl |= MCAP_CTRL_CACHED; + mcap_mcl_shutdown(mcl); +} + +static void mcap_uncache_mcl(struct mcap_mcl *mcl) +{ + if (!(mcl->ctrl & MCAP_CTRL_CACHED)) + return; + + DBG("Got MCL from cache"); + + mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); + mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, mcl); + mcl->ctrl &= ~MCAP_CTRL_CACHED; + mcl->ctrl &= ~MCAP_CTRL_FREE; +} + +void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache) +{ + if (!mcl) + return; + + if (mcl->ctrl & MCAP_CTRL_FREE) { + mcap_mcl_release(mcl); + return; + } + + if (!cache) + mcl->ctrl |= MCAP_CTRL_NOCACHE; + + if (mcl->cc) { + g_io_channel_shutdown(mcl->cc, TRUE, NULL); + g_io_channel_unref(mcl->cc); + mcl->cc = NULL; + mcl->state = MCL_IDLE; + } else if ((mcl->ctrl & MCAP_CTRL_CACHED) && + (mcl->ctrl & MCAP_CTRL_NOCACHE)) { + mcl->ctrl &= ~MCAP_CTRL_CACHED; + mcl->mi->cached = g_slist_remove(mcl->mi->cached, mcl); + mcap_mcl_release(mcl); + mcap_mcl_unref(mcl); + } +} + +struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl) +{ + mcl->ref++; + + DBG("mcap_mcl_ref(%p): ref=%d", mcl, mcl->ref); + + return mcl; +} + +void mcap_mcl_unref(struct mcap_mcl *mcl) +{ + mcl->ref--; + + DBG("mcap_mcl_unref(%p): ref=%d", mcl, mcl->ref); + + if (mcl->ref > 0) + return; + + mcap_mcl_release(mcl); + mcap_instance_unref(mcl->mi); + g_free(mcl->cb); + g_free(mcl); +} + +static gboolean parse_set_opts(struct mcap_mdl_cb *mdl_cb, GError **err, + McapMclCb cb1, va_list args) +{ + McapMclCb cb = cb1; + struct mcap_mdl_cb *c; + + c = g_new0(struct mcap_mdl_cb, 1); + + while (cb != MCAP_MDL_CB_INVALID) { + switch (cb) { + case MCAP_MDL_CB_CONNECTED: + c->mdl_connected = va_arg(args, mcap_mdl_event_cb); + break; + case MCAP_MDL_CB_CLOSED: + c->mdl_closed = va_arg(args, mcap_mdl_event_cb); + break; + case MCAP_MDL_CB_DELETED: + c->mdl_deleted = va_arg(args, mcap_mdl_event_cb); + break; + case MCAP_MDL_CB_ABORTED: + c->mdl_aborted = va_arg(args, mcap_mdl_event_cb); + break; + case MCAP_MDL_CB_REMOTE_CONN_REQ: + c->mdl_conn_req = va_arg(args, + mcap_remote_mdl_conn_req_cb); + break; + case MCAP_MDL_CB_REMOTE_RECONN_REQ: + c->mdl_reconn_req = va_arg(args, + mcap_remote_mdl_reconn_req_cb); + break; + default: + g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, + "Unknown option %d", cb); + g_free(c); + return FALSE; + } + cb = va_arg(args, int); + } + + /* Set new callbacks */ + if (c->mdl_connected) + mdl_cb->mdl_connected = c->mdl_connected; + if (c->mdl_closed) + mdl_cb->mdl_closed = c->mdl_closed; + if (c->mdl_deleted) + mdl_cb->mdl_deleted = c->mdl_deleted; + if (c->mdl_aborted) + mdl_cb->mdl_aborted = c->mdl_aborted; + if (c->mdl_conn_req) + mdl_cb->mdl_conn_req = c->mdl_conn_req; + if (c->mdl_reconn_req) + mdl_cb->mdl_reconn_req = c->mdl_reconn_req; + + g_free(c); + + return TRUE; +} + +gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data, + GError **gerr, McapMclCb cb1, ...) +{ + va_list args; + gboolean ret; + + va_start(args, cb1); + ret = parse_set_opts(mcl->cb, gerr, cb1, args); + va_end(args); + + if (!ret) + return FALSE; + + mcl->cb->user_data = user_data; + return TRUE; +} + +void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr) +{ + bacpy(addr, &mcl->addr); +} + +static void mcap_del_mdl(gpointer elem, gpointer user_data) +{ + struct mcap_mdl *mdl = elem; + gboolean notify = *(gboolean *) user_data; + + if (notify) + mdl->mcl->cb->mdl_deleted(mdl, mdl->mcl->cb->user_data); + + shutdown_mdl(mdl); + mcap_mdl_unref(mdl); +} + +static gboolean check_cmd_req_length(struct mcap_mcl *mcl, void *cmd, + uint32_t rlen, uint32_t explen, uint8_t rspcod) +{ + mcap_md_req *req; + uint16_t mdl_id; + + if (rlen != explen) { + if (rlen >= sizeof(mcap_md_req)) { + req = cmd; + mdl_id = ntohs(req->mdl); + } else { + /* We can't get mdlid */ + mdl_id = MCAP_MDLID_RESERVED; + } + mcap_send_cmd(mcl, rspcod, MCAP_INVALID_PARAM_VALUE, mdl_id, + NULL, 0); + return FALSE; + } + return TRUE; +} + +static void process_md_create_mdl_req(struct mcap_mcl *mcl, void *cmd, + uint32_t len) +{ + mcap_md_create_mdl_req *req; + struct mcap_mdl *mdl; + uint16_t mdl_id; + uint8_t mdep_id; + uint8_t cfga, conf; + uint8_t rsp; + + if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_create_mdl_req), + MCAP_MD_CREATE_MDL_RSP)) + return; + + req = cmd; + mdl_id = ntohs(req->mdl); + if (mdl_id < MCAP_MDLID_INITIAL || mdl_id > MCAP_MDLID_FINAL) { + mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDL, + mdl_id, NULL, 0); + return; + } + + mdep_id = req->mdep; + if (mdep_id > MCAP_MDEPID_FINAL) { + mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_INVALID_MDEP, + mdl_id, NULL, 0); + return; + } + + mdl = get_mdl(mcl, mdl_id); + if (mdl && (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING )) { + /* + * Creation request arrives for a MDL that is being managed + * at current moment + */ + mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_MDL_BUSY, + mdl_id, NULL, 0); + return; + } + + cfga = conf = req->conf; + /* Callback to upper layer */ + rsp = mcl->cb->mdl_conn_req(mcl, mdep_id, mdl_id, &conf, + mcl->cb->user_data); + if (mcl->state == MCL_IDLE) { + /* MCL has been closed int the callback */ + return; + } + + if (cfga != 0 && cfga != conf) { + /* + * Remote device set default configuration but upper profile + * has changed it. Protocol Error: force closing the MCL by + * remote device using UNSPECIFIED_ERROR response + */ + mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, + MCAP_UNSPECIFIED_ERROR, mdl_id, NULL, 0); + return; + } + if (rsp != MCAP_SUCCESS) { + mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, rsp, mdl_id, + NULL, 0); + return; + } + + if (!mdl) { + mdl = g_new0(struct mcap_mdl, 1); + mdl->mcl = mcap_mcl_ref(mcl); + mdl->mdlid = mdl_id; + mcl->mdls = g_slist_insert_sorted(mcl->mdls, mcap_mdl_ref(mdl), + compare_mdl); + } else if (mdl->state == MDL_CONNECTED) { + /* + * MCAP specification says that we should close the MCL if + * it is open when we receive a MD_CREATE_MDL_REQ + */ + shutdown_mdl(mdl); + } + + mdl->mdep_id = mdep_id; + mdl->state = MDL_WAITING; + + mcl->state = MCL_PENDING; + mcap_send_cmd(mcl, MCAP_MD_CREATE_MDL_RSP, MCAP_SUCCESS, mdl_id, + &conf, 1); +} + +static void process_md_reconnect_mdl_req(struct mcap_mcl *mcl, void *cmd, + uint32_t len) +{ + mcap_md_req *req; + struct mcap_mdl *mdl; + uint16_t mdl_id; + uint8_t rsp; + + if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), + MCAP_MD_RECONNECT_MDL_RSP)) + return; + + req = cmd; + mdl_id = ntohs(req->mdl); + + mdl = get_mdl(mcl, mdl_id); + if (!mdl) { + mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_INVALID_MDL, + mdl_id, NULL, 0); + return; + } else if (mdl->state == MDL_WAITING || mdl->state == MDL_DELETING ) { + /* + * Creation request arrives for a MDL that is being managed + * at current moment + */ + mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_MDL_BUSY, + mdl_id, NULL, 0); + return; + } + + /* Callback to upper layer */ + rsp = mcl->cb->mdl_reconn_req(mdl, mcl->cb->user_data); + if (mcl->state == MCL_IDLE) + return; + + if (rsp != MCAP_SUCCESS) { + mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, rsp, mdl_id, + NULL, 0); + return; + } + + if (mdl->state == MDL_CONNECTED) + shutdown_mdl(mdl); + + mdl->state = MDL_WAITING; + mcl->state = MCL_PENDING; + mcap_send_cmd(mcl, MCAP_MD_RECONNECT_MDL_RSP, MCAP_SUCCESS, mdl_id, + NULL, 0); +} + +static void process_md_abort_mdl_req(struct mcap_mcl *mcl, void *cmd, + uint32_t len) +{ + mcap_md_req *req; + GSList *l; + struct mcap_mdl *mdl, *abrt; + uint16_t mdl_id; + + if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), + MCAP_MD_ABORT_MDL_RSP)) + return; + + req = cmd; + mdl_id = ntohs(req->mdl); + mcl->state = MCL_CONNECTED; + abrt = NULL; + for (l = mcl->mdls; l; l = l->next) { + mdl = l->data; + if (mdl_id == mdl->mdlid && mdl->state == MDL_WAITING) { + abrt = mdl; + if (mcl->state != MCL_CONNECTED) + break; + continue; + } + if (mdl->state == MDL_CONNECTED && mcl->state != MCL_ACTIVE) + mcl->state = MCL_ACTIVE; + + if (abrt && mcl->state == MCL_ACTIVE) + break; + } + + if (!abrt) { + mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_INVALID_MDL, + mdl_id, NULL, 0); + return; + } + + mcl->cb->mdl_aborted(abrt, mcl->cb->user_data); + abrt->state = MDL_CLOSED; + mcap_send_cmd(mcl, MCAP_MD_ABORT_MDL_RSP, MCAP_SUCCESS, mdl_id, + NULL, 0); +} + +static void process_md_delete_mdl_req(struct mcap_mcl *mcl, void *cmd, + uint32_t len) +{ + mcap_md_req *req; + struct mcap_mdl *mdl, *aux; + uint16_t mdlid; + gboolean notify; + GSList *l; + + if (!check_cmd_req_length(mcl, cmd, len, sizeof(mcap_md_req), + MCAP_MD_DELETE_MDL_RSP)) + return; + + req = cmd; + mdlid = ntohs(req->mdl); + if (mdlid == MCAP_ALL_MDLIDS) { + notify = TRUE; + g_slist_foreach(mcl->mdls, mcap_del_mdl, ¬ify); + g_slist_free(mcl->mdls); + mcl->mdls = NULL; + mcl->state = MCL_CONNECTED; + goto resp; + } + + if (mdlid < MCAP_MDLID_INITIAL || mdlid > MCAP_MDLID_FINAL) { + mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, + mdlid, NULL, 0); + return; + } + + for (l = mcl->mdls, mdl = NULL; l; l = l->next) { + aux = l->data; + if (aux->mdlid == mdlid) { + mdl = aux; + break; + } + } + + if (!mdl || mdl->state == MDL_WAITING) { + mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_INVALID_MDL, + mdlid, NULL, 0); + return; + } + + mcl->mdls = g_slist_remove(mcl->mdls, mdl); + update_mcl_state(mcl); + notify = TRUE; + mcap_del_mdl(mdl, ¬ify); + +resp: + mcap_send_cmd(mcl, MCAP_MD_DELETE_MDL_RSP, MCAP_SUCCESS, mdlid, + NULL, 0); +} + +static void invalid_req_state(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + uint16_t mdlr; + + error("Invalid cmd received (op code = %d) in state %d", cmd[0], + mcl->state); + /* + * Get previously mdlid sent to generate an appropriate + * response if it is possible + */ + mdlr = len < sizeof(mcap_md_req) ? MCAP_MDLID_RESERVED : + ntohs(((mcap_md_req *) cmd)->mdl); + mcap_send_cmd(mcl, cmd[0]+1, MCAP_INVALID_OPERATION, mdlr, NULL, 0); +} + +/* Function used to process commands depending of MCL state */ +static void proc_req_connected(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + switch (cmd[0]) { + case MCAP_MD_CREATE_MDL_REQ: + process_md_create_mdl_req(mcl, cmd, len); + break; + case MCAP_MD_RECONNECT_MDL_REQ: + process_md_reconnect_mdl_req(mcl, cmd, len); + break; + case MCAP_MD_DELETE_MDL_REQ: + process_md_delete_mdl_req(mcl, cmd, len); + break; + default: + invalid_req_state(mcl, cmd, len); + } +} + +static void proc_req_pending(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + if (cmd[0] == MCAP_MD_ABORT_MDL_REQ) + process_md_abort_mdl_req(mcl, cmd, len); + else + invalid_req_state(mcl, cmd, len); +} + +static void proc_req_active(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + switch (cmd[0]) { + case MCAP_MD_CREATE_MDL_REQ: + process_md_create_mdl_req(mcl, cmd, len); + break; + case MCAP_MD_RECONNECT_MDL_REQ: + process_md_reconnect_mdl_req(mcl, cmd, len); + break; + case MCAP_MD_DELETE_MDL_REQ: + process_md_delete_mdl_req(mcl, cmd, len); + break; + default: + invalid_req_state(mcl, cmd, len); + } +} + +/* Function used to process replies */ +static gboolean check_err_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, + uint32_t rlen, uint32_t len, GError **gerr) +{ + mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd; + int err = MCAP_ERROR_FAILED; + gboolean close = FALSE; + char *msg; + + if (rsp->op == MCAP_ERROR_RSP) { + msg = "MCAP_ERROR_RSP received"; + close = FALSE; + goto fail; + } + + /* Check if the response matches with the last request */ + if (rlen < sizeof(mcap_rsp) || (mcl->lcmd[0] + 1) != rsp->op) { + msg = "Protocol error"; + close = FALSE; + goto fail; + } + + if (rlen < len) { + msg = "Protocol error"; + close = FALSE; + goto fail; + } + + if (rsp->mdl != cmdlast->mdl) { + msg = "MDLID received doesn't match with MDLID sent"; + close = TRUE; + goto fail; + } + + if (rsp->rc == MCAP_REQUEST_NOT_SUPPORTED) { + msg = "Remote does not support opcodes"; + mcl->ctrl &= ~MCAP_CTRL_STD_OP; + goto fail; + } + + if (rsp->rc == MCAP_UNSPECIFIED_ERROR) { + msg = "Unspecified error"; + close = TRUE; + goto fail; + } + + if (rsp->rc != MCAP_SUCCESS) { + msg = error2str(rsp->rc); + err = rsp->rc; + goto fail; + } + + return FALSE; + +fail: + g_set_error(gerr, MCAP_ERROR, err, "%s", msg); + return close; +} + +static gboolean process_md_create_mdl_rsp(struct mcap_mcl *mcl, + mcap_rsp *rsp, uint32_t len) +{ + mcap_md_create_mdl_req *cmdlast = (mcap_md_create_mdl_req *) mcl->lcmd; + struct mcap_mdl_op_cb *conn = mcl->priv_data; + mcap_mdl_operation_conf_cb connect_cb = conn->cb.op_conf; + gpointer user_data = conn->user_data; + struct mcap_mdl *mdl = conn->mdl; + uint8_t conf = cmdlast->conf; + gboolean close; + GError *gerr = NULL; + + close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp) + 1, &gerr); + g_free(mcl->lcmd); + mcl->lcmd = NULL; + mcl->req = MCL_AVAILABLE; + + if (gerr) + goto fail; + + /* Check if preferences changed */ + if (conf != 0x00 && rsp->data[0] != conf) { + g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_FAILED, + "Configuration changed"); + close = TRUE; + goto fail; + } + + connect_cb(mdl, rsp->data[0], gerr, user_data); + return close; + +fail: + connect_cb(NULL, 0, gerr, user_data); + mcl->mdls = g_slist_remove(mcl->mdls, mdl); + mcap_mdl_unref(mdl); + g_error_free(gerr); + update_mcl_state(mcl); + return close; +} + +static gboolean process_md_reconnect_mdl_rsp(struct mcap_mcl *mcl, + mcap_rsp *rsp, uint32_t len) +{ + struct mcap_mdl_op_cb *reconn = mcl->priv_data; + mcap_mdl_operation_cb reconn_cb = reconn->cb.op; + gpointer user_data = reconn->user_data; + struct mcap_mdl *mdl = reconn->mdl; + GError *gerr = NULL; + gboolean close; + + close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); + + g_free(mcl->lcmd); + mcl->lcmd = NULL; + mcl->req = MCL_AVAILABLE; + + reconn_cb(mdl, gerr, user_data); + if (!gerr) + return close; + + g_error_free(gerr); + shutdown_mdl(mdl); + update_mcl_state(mcl); + + if (rsp->rc != MCAP_INVALID_MDL) + return close; + + /* Remove cached mdlid */ + mcl->mdls = g_slist_remove(mcl->mdls, mdl); + mcl->cb->mdl_deleted(mdl, mcl->cb->user_data); + mcap_mdl_unref(mdl); + + return close; +} + +static gboolean process_md_abort_mdl_rsp(struct mcap_mcl *mcl, + mcap_rsp *rsp, uint32_t len) +{ + struct mcap_mdl_op_cb *abrt = mcl->priv_data; + mcap_mdl_notify_cb abrt_cb = abrt->cb.notify; + gpointer user_data = abrt->user_data; + struct mcap_mdl *mdl = abrt->mdl; + GError *gerr = NULL; + gboolean close; + + close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); + + g_free(mcl->lcmd); + mcl->lcmd = NULL; + mcl->req = MCL_AVAILABLE; + + abrt_cb(gerr, user_data); + shutdown_mdl(mdl); + + if (len >= sizeof(mcap_rsp) && rsp->rc == MCAP_INVALID_MDL) { + mcl->mdls = g_slist_remove(mcl->mdls, mdl); + mcl->cb->mdl_deleted(mdl, mcl->cb->user_data); + mcap_mdl_unref(mdl); + } + + if (gerr) + g_error_free(gerr); + + update_mcl_state(mcl); + + return close; +} + +static void restore_mdl(gpointer elem, gpointer data) +{ + struct mcap_mdl *mdl = elem; + + if (mdl->state == MDL_DELETING) { + if (mdl->dc) + mdl->state = MDL_CONNECTED; + else + mdl->state = MDL_CLOSED; + } else if (mdl->state == MDL_CLOSED) + mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data); +} + +static void check_mdl_del_err(struct mcap_mdl *mdl, mcap_rsp *rsp) +{ + if (rsp->rc != MCAP_ERROR_INVALID_MDL) { + restore_mdl(mdl, NULL); + return; + } + + /* MDL does not exist in remote side, we can delete it */ + mdl->mcl->mdls = g_slist_remove(mdl->mcl->mdls, mdl); + mcap_mdl_unref(mdl); +} + +static gboolean process_md_delete_mdl_rsp(struct mcap_mcl *mcl, mcap_rsp *rsp, + uint32_t len) +{ + struct mcap_mdl_op_cb *del = mcl->priv_data; + struct mcap_mdl *mdl = del->mdl; + mcap_mdl_notify_cb deleted_cb = del->cb.notify; + gpointer user_data = del->user_data; + mcap_md_req *cmdlast = (mcap_md_req *) mcl->lcmd; + uint16_t mdlid = ntohs(cmdlast->mdl); + GError *gerr = NULL; + gboolean close; + gboolean notify = FALSE; + + close = check_err_rsp(mcl, rsp, len, sizeof(mcap_rsp), &gerr); + + g_free(mcl->lcmd); + mcl->lcmd = NULL; + mcl->req = MCL_AVAILABLE; + + if (gerr) { + if (mdl) + check_mdl_del_err(mdl, rsp); + else + g_slist_foreach(mcl->mdls, restore_mdl, NULL); + deleted_cb(gerr, user_data); + g_error_free(gerr); + return close; + } + + if (mdlid == MCAP_ALL_MDLIDS) { + g_slist_foreach(mcl->mdls, mcap_del_mdl, ¬ify); + g_slist_free(mcl->mdls); + mcl->mdls = NULL; + mcl->state = MCL_CONNECTED; + } else { + mcl->mdls = g_slist_remove(mcl->mdls, mdl); + update_mcl_state(mcl); + mcap_del_mdl(mdl, ¬ify); + } + + deleted_cb(gerr, user_data); + + return close; +} + +static void post_process_rsp(struct mcap_mcl *mcl, struct mcap_mdl_op_cb *op) +{ + if (mcl->priv_data != op) { + /* + * Queued MCAP request in some callback. + * We should not delete the mcl private data + */ + free_mcap_mdl_op(op); + } else { + /* + * This is not a queued request. It's safe + * delete the mcl private data here. + */ + free_mcl_priv_data(mcl); + } +} + +static void proc_response(struct mcap_mcl *mcl, void *buf, uint32_t len) +{ + struct mcap_mdl_op_cb *op = mcl->priv_data; + mcap_rsp *rsp = buf; + gboolean close; + + RELEASE_TIMER(mcl); + + switch (mcl->lcmd[0] + 1) { + case MCAP_MD_CREATE_MDL_RSP: + close = process_md_create_mdl_rsp(mcl, rsp, len); + post_process_rsp(mcl, op); + break; + case MCAP_MD_RECONNECT_MDL_RSP: + close = process_md_reconnect_mdl_rsp(mcl, rsp, len); + post_process_rsp(mcl, op); + break; + case MCAP_MD_ABORT_MDL_RSP: + close = process_md_abort_mdl_rsp(mcl, rsp, len); + post_process_rsp(mcl, op); + break; + case MCAP_MD_DELETE_MDL_RSP: + close = process_md_delete_mdl_rsp(mcl, rsp, len); + post_process_rsp(mcl, op); + break; + default: + DBG("Unknown cmd response received (op code = %d)", rsp->op); + close = TRUE; + break; + } + + if (close) { + mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); + mcap_cache_mcl(mcl); + } +} + +static void proc_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + GError *gerr = NULL; + + if (cmd[0] > MCAP_MD_SYNC_INFO_IND || + (cmd[0] > MCAP_MD_DELETE_MDL_RSP && + cmd[0] < MCAP_MD_SYNC_CAP_REQ)) { + error("Unknown cmd received (op code = %d)", cmd[0]); + mcap_send_cmd(mcl, MCAP_ERROR_RSP, MCAP_INVALID_OP_CODE, + MCAP_MDLID_RESERVED, NULL, 0); + return; + } + + if (cmd[0] >= MCAP_MD_SYNC_CAP_REQ && + cmd[0] <= MCAP_MD_SYNC_INFO_IND) { + proc_sync_cmd(mcl, cmd, len); + return; + } + + if (!(mcl->ctrl & MCAP_CTRL_STD_OP)) { + /* In case the remote device doesn't work correctly */ + error("Remote device does not support opcodes, cmd ignored"); + return; + } + + if (mcl->req == MCL_WAITING_RSP) { + if (cmd[0] & 0x01) { + /* Request arrived when a response is expected */ + if (mcl->role == MCL_INITIATOR) + /* ignore */ + return; + /* Initiator will ignore our last request */ + RELEASE_TIMER(mcl); + mcl->req = MCL_AVAILABLE; + g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_REQ_IGNORED, + "Initiator sent a request with more priority"); + mcap_notify_error(mcl, gerr); + proc_req[mcl->state](mcl, cmd, len); + return; + } + proc_response(mcl, cmd, len); + } else if (cmd[0] & 0x01) + proc_req[mcl->state](mcl, cmd, len); +} + +static gboolean mdl_event_cb(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + + struct mcap_mdl *mdl = data; + gboolean notify; + + DBG("Close MDL %d", mdl->mdlid); + + notify = (mdl->state == MDL_CONNECTED); + shutdown_mdl(mdl); + + update_mcl_state(mdl->mcl); + + if (notify) { + /*Callback to upper layer */ + mdl->mcl->cb->mdl_closed(mdl, mdl->mcl->cb->user_data); + } + + return FALSE; +} + +static void mcap_connect_mdl_cb(GIOChannel *chan, GError *conn_err, + gpointer data) +{ + struct mcap_mdl_op_cb *con = data; + struct mcap_mdl *mdl = con->mdl; + mcap_mdl_operation_cb cb = con->cb.op; + gpointer user_data = con->user_data; + + DBG("mdl connect callback"); + + if (conn_err) { + DBG("ERROR: mdl connect callback"); + mdl->state = MDL_CLOSED; + g_io_channel_unref(mdl->dc); + mdl->dc = NULL; + cb(mdl, conn_err, user_data); + return; + } + + mdl->state = MDL_CONNECTED; + mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT, + G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) mdl_event_cb, + mcap_mdl_ref(mdl), + (GDestroyNotify) mcap_mdl_unref); + + cb(mdl, conn_err, user_data); +} + +gboolean mcap_connect_mdl(struct mcap_mdl *mdl, uint8_t mode, + uint16_t dcpsm, + mcap_mdl_operation_cb connect_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err) +{ + struct mcap_mdl_op_cb *con; + + if (mdl->state != MDL_WAITING) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_MDL, + "%s", error2str(MCAP_INVALID_MDL)); + return FALSE; + } + + if ((mode != L2CAP_MODE_ERTM) && (mode != L2CAP_MODE_STREAMING)) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, + "Invalid MDL configuration"); + return FALSE; + } + + con = g_new0(struct mcap_mdl_op_cb, 1); + con->mdl = mcap_mdl_ref(mdl); + con->cb.op = connect_cb; + con->destroy = destroy; + con->user_data = user_data; + + mdl->dc = bt_io_connect(mcap_connect_mdl_cb, con, + (GDestroyNotify) free_mcap_mdl_op, err, + BT_IO_OPT_SOURCE_BDADDR, &mdl->mcl->mi->src, + BT_IO_OPT_DEST_BDADDR, &mdl->mcl->addr, + BT_IO_OPT_PSM, dcpsm, + BT_IO_OPT_MTU, MCAP_DC_MTU, + BT_IO_OPT_SEC_LEVEL, mdl->mcl->mi->sec, + BT_IO_OPT_MODE, mode, + BT_IO_OPT_INVALID); + if (!mdl->dc) { + DBG("MDL Connection error"); + mdl->state = MDL_CLOSED; + mcap_mdl_unref(con->mdl); + g_free(con); + return FALSE; + } + + return TRUE; +} + +static gboolean mcl_control_cb(GIOChannel *chan, GIOCondition cond, + gpointer data) +{ + GError *gerr = NULL; + struct mcap_mcl *mcl = data; + int sk, len; + uint8_t buf[MCAP_CC_MTU]; + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) + goto fail; + + sk = g_io_channel_unix_get_fd(chan); + len = read(sk, buf, sizeof(buf)); + if (len < 0) + goto fail; + + proc_cmd(mcl, buf, (uint32_t) len); + return TRUE; + +fail: + if (mcl->state != MCL_IDLE) { + if (mcl->req == MCL_WAITING_RSP) { + /* notify error in pending callback */ + g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_MCL_CLOSED, + "MCL closed"); + mcap_notify_error(mcl, gerr); + g_error_free(gerr); + } + mcl->mi->mcl_disconnected_cb(mcl, mcl->mi->user_data); + } + mcap_cache_mcl(mcl); + return FALSE; +} + +static void mcap_connect_mcl_cb(GIOChannel *chan, GError *conn_err, + gpointer user_data) +{ + char dstaddr[18]; + struct connect_mcl *con = user_data; + struct mcap_mcl *aux, *mcl = con->mcl; + mcap_mcl_connect_cb connect_cb = con->connect_cb; + gpointer data = con->user_data; + GError *gerr = NULL; + + mcl->ctrl &= ~MCAP_CTRL_CONN; + + if (conn_err) { + if (mcl->ctrl & MCAP_CTRL_FREE) { + mcap_mcl_release(mcl); + mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data); + } + connect_cb(NULL, conn_err, data); + return; + } + + ba2str(&mcl->addr, dstaddr); + + aux = find_mcl(mcl->mi->mcls, &mcl->addr); + if (aux) { + /* Double MCL connection case */ + error("MCL error: Device %s is already connected", dstaddr); + g_set_error(&gerr, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS, + "MCL %s is already connected", dstaddr); + connect_cb(NULL, gerr, data); + g_error_free(gerr); + return; + } + + mcl->state = MCL_CONNECTED; + mcl->role = MCL_INITIATOR; + mcl->req = MCL_AVAILABLE; + mcl->ctrl |= MCAP_CTRL_STD_OP; + + mcap_sync_init(mcl); + + if (mcl->ctrl & MCAP_CTRL_CACHED) + mcap_uncache_mcl(mcl); + else { + mcl->ctrl &= ~MCAP_CTRL_FREE; + mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, + mcap_mcl_ref(mcl)); + } + + mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) mcl_control_cb, + mcap_mcl_ref(mcl), + (GDestroyNotify) mcap_mcl_unref); + connect_cb(mcl, gerr, data); +} + +static void set_mdl_properties(GIOChannel *chan, struct mcap_mdl *mdl) +{ + struct mcap_mcl *mcl = mdl->mcl; + + mdl->state = MDL_CONNECTED; + mdl->dc = g_io_channel_ref(chan); + mdl->wid = g_io_add_watch_full(mdl->dc, G_PRIORITY_DEFAULT, + G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) mdl_event_cb, + mcap_mdl_ref(mdl), + (GDestroyNotify) mcap_mdl_unref); + + mcl->state = MCL_ACTIVE; + mcl->cb->mdl_connected(mdl, mcl->cb->user_data); +} + +static void mcl_io_destroy(gpointer data) +{ + struct connect_mcl *con = data; + + mcap_mcl_unref(con->mcl); + if (con->destroy) + con->destroy(con->user_data); + g_free(con); +} + +gboolean mcap_create_mcl(struct mcap_instance *mi, + const bdaddr_t *addr, + uint16_t ccpsm, + mcap_mcl_connect_cb connect_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err) +{ + struct mcap_mcl *mcl; + struct connect_mcl *con; + + mcl = find_mcl(mi->mcls, addr); + if (mcl) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_ALREADY_EXISTS, + "MCL is already connected."); + return FALSE; + } + + mcl = find_mcl(mi->cached, addr); + if (!mcl) { + mcl = g_new0(struct mcap_mcl, 1); + mcl->mi = mcap_instance_ref(mi); + mcl->state = MCL_IDLE; + bacpy(&mcl->addr, addr); + set_default_cb(mcl); + mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1; + } + + mcl->ctrl |= MCAP_CTRL_CONN; + + con = g_new0(struct connect_mcl, 1); + con->mcl = mcap_mcl_ref(mcl); + con->connect_cb = connect_cb; + con->destroy = destroy; + con->user_data = user_data; + + mcl->cc = bt_io_connect(mcap_connect_mcl_cb, con, + mcl_io_destroy, err, + BT_IO_OPT_SOURCE_BDADDR, &mi->src, + BT_IO_OPT_DEST_BDADDR, addr, + BT_IO_OPT_PSM, ccpsm, + BT_IO_OPT_MTU, MCAP_CC_MTU, + BT_IO_OPT_SEC_LEVEL, mi->sec, + BT_IO_OPT_MODE, L2CAP_MODE_ERTM, + BT_IO_OPT_INVALID); + if (!mcl->cc) { + mcl->ctrl &= ~MCAP_CTRL_CONN; + if (mcl->ctrl & MCAP_CTRL_FREE) { + mcap_mcl_release(mcl); + mcl->mi->mcl_uncached_cb(mcl, mcl->mi->user_data); + } + mcap_mcl_unref(con->mcl); + g_free(con); + return FALSE; + } + + return TRUE; +} + +static void connect_dc_event_cb(GIOChannel *chan, GError *gerr, + gpointer user_data) +{ + struct mcap_instance *mi = user_data; + struct mcap_mcl *mcl; + struct mcap_mdl *mdl; + GError *err = NULL; + bdaddr_t dst; + GSList *l; + + if (gerr) + return; + + bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + goto drop; + } + + mcl = find_mcl(mi->mcls, &dst); + if (!mcl || mcl->state != MCL_PENDING) + goto drop; + + for (l = mcl->mdls; l; l = l->next) { + mdl = l->data; + if (mdl->state == MDL_WAITING) { + set_mdl_properties(chan, mdl); + return; + } + } + +drop: + g_io_channel_shutdown(chan, TRUE, NULL); +} + +static void set_mcl_conf(GIOChannel *chan, struct mcap_mcl *mcl) +{ + gboolean reconn; + + mcl->state = MCL_CONNECTED; + mcl->role = MCL_ACCEPTOR; + mcl->req = MCL_AVAILABLE; + mcl->cc = g_io_channel_ref(chan); + mcl->ctrl |= MCAP_CTRL_STD_OP; + + mcap_sync_init(mcl); + + reconn = (mcl->ctrl & MCAP_CTRL_CACHED); + if (reconn) + mcap_uncache_mcl(mcl); + else + mcl->mi->mcls = g_slist_prepend(mcl->mi->mcls, + mcap_mcl_ref(mcl)); + + mcl->wid = g_io_add_watch_full(mcl->cc, G_PRIORITY_DEFAULT, + G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, + (GIOFunc) mcl_control_cb, + mcap_mcl_ref(mcl), + (GDestroyNotify) mcap_mcl_unref); + + /* Callback to report new MCL */ + if (reconn) + mcl->mi->mcl_reconnected_cb(mcl, mcl->mi->user_data); + else + mcl->mi->mcl_connected_cb(mcl, mcl->mi->user_data); +} + +static void connect_mcl_event_cb(GIOChannel *chan, GError *gerr, + gpointer user_data) +{ + struct mcap_instance *mi = user_data; + struct mcap_mcl *mcl; + bdaddr_t dst; + char address[18], srcstr[18]; + GError *err = NULL; + + if (gerr) + return; + + bt_io_get(chan, &err, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST, address, + BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + goto drop; + } + + ba2str(&mi->src, srcstr); + mcl = find_mcl(mi->mcls, &dst); + if (mcl) { + error("Control channel already created with %s on adapter %s", + address, srcstr); + goto drop; + } + + mcl = find_mcl(mi->cached, &dst); + if (!mcl) { + mcl = g_new0(struct mcap_mcl, 1); + mcl->mi = mcap_instance_ref(mi); + bacpy(&mcl->addr, &dst); + set_default_cb(mcl); + mcl->next_mdl = (rand() % MCAP_MDLID_FINAL) + 1; + } + + set_mcl_conf(chan, mcl); + + return; +drop: + g_io_channel_shutdown(chan, TRUE, NULL); +} + +struct mcap_instance *mcap_create_instance(const bdaddr_t *src, + BtIOSecLevel sec, + uint16_t ccpsm, + uint16_t dcpsm, + mcap_mcl_event_cb mcl_connected, + mcap_mcl_event_cb mcl_reconnected, + mcap_mcl_event_cb mcl_disconnected, + mcap_mcl_event_cb mcl_uncached, + mcap_info_ind_event_cb mcl_sync_info_ind, + gpointer user_data, + GError **gerr) +{ + struct mcap_instance *mi; + + if (sec < BT_IO_SEC_MEDIUM) { + g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, + "Security level can't be minor of %d", + BT_IO_SEC_MEDIUM); + return NULL; + } + + if (!(mcl_connected && mcl_reconnected && + mcl_disconnected && mcl_uncached)) { + g_set_error(gerr, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, + "The callbacks can't be null"); + return NULL; + } + + mi = g_new0(struct mcap_instance, 1); + + bacpy(&mi->src, src); + + mi->sec = sec; + mi->mcl_connected_cb = mcl_connected; + mi->mcl_reconnected_cb = mcl_reconnected; + mi->mcl_disconnected_cb = mcl_disconnected; + mi->mcl_uncached_cb = mcl_uncached; + mi->mcl_sync_infoind_cb = mcl_sync_info_ind; + mi->user_data = user_data; + mi->csp_enabled = FALSE; + + /* Listen incoming connections in control channel */ + mi->ccio = bt_io_listen(connect_mcl_event_cb, NULL, mi, + NULL, gerr, + BT_IO_OPT_SOURCE_BDADDR, &mi->src, + BT_IO_OPT_PSM, ccpsm, + BT_IO_OPT_MTU, MCAP_CC_MTU, + BT_IO_OPT_SEC_LEVEL, sec, + BT_IO_OPT_MODE, L2CAP_MODE_ERTM, + BT_IO_OPT_INVALID); + if (!mi->ccio) { + error("%s", (*gerr)->message); + g_free(mi); + return NULL; + } + + /* Listen incoming connections in data channels */ + mi->dcio = bt_io_listen(connect_dc_event_cb, NULL, mi, + NULL, gerr, + BT_IO_OPT_SOURCE_BDADDR, &mi->src, + BT_IO_OPT_PSM, dcpsm, + BT_IO_OPT_MTU, MCAP_DC_MTU, + BT_IO_OPT_SEC_LEVEL, sec, + BT_IO_OPT_INVALID); + if (!mi->dcio) { + g_io_channel_shutdown(mi->ccio, TRUE, NULL); + g_io_channel_unref(mi->ccio); + mi->ccio = NULL; + error("%s", (*gerr)->message); + g_free(mi); + return NULL; + } + + /* Initialize random seed to generate mdlids for this instance */ + srand(time(NULL)); + + return mcap_instance_ref(mi); +} + +void mcap_release_instance(struct mcap_instance *mi) +{ + GSList *l; + + if (!mi) + return; + + if (mi->ccio) { + g_io_channel_shutdown(mi->ccio, TRUE, NULL); + g_io_channel_unref(mi->ccio); + mi->ccio = NULL; + } + + if (mi->dcio) { + g_io_channel_shutdown(mi->dcio, TRUE, NULL); + g_io_channel_unref(mi->dcio); + mi->dcio = NULL; + } + + for (l = mi->mcls; l; l = l->next) { + mcap_mcl_release(l->data); + mcap_mcl_unref(l->data); + } + + g_slist_free(mi->mcls); + mi->mcls = NULL; + + for (l = mi->cached; l; l = l->next) { + mcap_mcl_release(l->data); + mcap_mcl_unref(l->data); + } + + g_slist_free(mi->cached); + mi->cached = NULL; +} + +struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi) +{ + mi->ref++; + + DBG("mcap_instance_ref(%p): ref=%d", mi, mi->ref); + + return mi; +} + +void mcap_instance_unref(struct mcap_instance *mi) +{ + mi->ref--; + + DBG("mcap_instance_unref(%p): ref=%d", mi, mi->ref); + + if (mi->ref > 0) + return; + + mcap_release_instance(mi); + g_free(mi); +} + +uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err) +{ + uint16_t lpsm; + + if (!(mi && mi->ccio)) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, + "Invalid MCAP instance"); + return 0; + } + + if (!bt_io_get(mi->ccio, err, BT_IO_OPT_PSM, &lpsm, BT_IO_OPT_INVALID)) + return 0; + + return lpsm; +} + +uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err) +{ + uint16_t lpsm; + + if (!(mi && mi->dcio)) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, + "Invalid MCAP instance"); + return 0; + } + + if (!bt_io_get(mi->dcio, err, BT_IO_OPT_PSM, &lpsm, BT_IO_OPT_INVALID)) + return 0; + + return lpsm; +} + +gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode, + GError **err) +{ + if (!(mi && mi->dcio)) { + g_set_error(err, MCAP_ERROR, MCAP_ERROR_INVALID_ARGS, + "Invalid MCAP instance"); + return FALSE; + } + + return bt_io_set(mi->dcio, err, BT_IO_OPT_MODE, mode, + BT_IO_OPT_INVALID); +} + +struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl) +{ + mdl->ref++; + + DBG("mcap_mdl_ref(%p): ref=%d", mdl, mdl->ref); + + return mdl; +} + +void mcap_mdl_unref(struct mcap_mdl *mdl) +{ + mdl->ref--; + + DBG("mcap_mdl_unref(%p): ref=%d", mdl, mdl->ref); + + if (mdl->ref > 0) + return; + + free_mdl(mdl); +} + + +static int send_sync_cmd(struct mcap_mcl *mcl, const void *buf, uint32_t size) +{ + int sock; + + if (mcl->cc == NULL) + return -1; + + sock = g_io_channel_unix_get_fd(mcl->cc); + return mcap_send_data(sock, buf, size); +} + +static int send_unsupported_cap_req(struct mcap_mcl *mcl) +{ + mcap_md_sync_cap_rsp *cmd; + int sent; + + cmd = g_new0(mcap_md_sync_cap_rsp, 1); + cmd->op = MCAP_MD_SYNC_CAP_RSP; + cmd->rc = MCAP_REQUEST_NOT_SUPPORTED; + + sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); + g_free(cmd); + + return sent; +} + +static int send_unsupported_set_req(struct mcap_mcl *mcl) +{ + mcap_md_sync_set_rsp *cmd; + int sent; + + cmd = g_new0(mcap_md_sync_set_rsp, 1); + cmd->op = MCAP_MD_SYNC_SET_RSP; + cmd->rc = MCAP_REQUEST_NOT_SUPPORTED; + + sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); + g_free(cmd); + + return sent; +} + +static void reset_tmstamp(struct mcap_csp *csp, struct timespec *base_time, + uint64_t new_tmstamp) +{ + csp->base_tmstamp = new_tmstamp; + if (base_time) + csp->base_time = *base_time; + else + clock_gettime(CLK, &csp->base_time); +} + +void mcap_sync_init(struct mcap_mcl *mcl) +{ + if (!mcl->mi->csp_enabled) { + mcl->csp = NULL; + return; + } + + mcl->csp = g_new0(struct mcap_csp, 1); + + mcl->csp->rem_req_acc = 10000; /* safe divisor */ + mcl->csp->set_data = NULL; + mcl->csp->csp_priv_data = NULL; + + reset_tmstamp(mcl->csp, NULL, 0); +} + +void mcap_sync_stop(struct mcap_mcl *mcl) +{ + if (!mcl->csp) + return; + + if (mcl->csp->ind_timer) + g_source_remove(mcl->csp->ind_timer); + + if (mcl->csp->set_timer) + g_source_remove(mcl->csp->set_timer); + + if (mcl->csp->set_data) + g_free(mcl->csp->set_data); + + if (mcl->csp->csp_priv_data) + g_free(mcl->csp->csp_priv_data); + + mcl->csp->ind_timer = 0; + mcl->csp->set_timer = 0; + mcl->csp->set_data = NULL; + mcl->csp->csp_priv_data = NULL; + + g_free(mcl->csp); + mcl->csp = NULL; +} + +static uint64_t time_us(struct timespec *tv) +{ + return tv->tv_sec * 1000000ll + tv->tv_nsec / 1000ll; +} + +static int64_t bt2us(int bt) +{ + return bt * 312.5; +} + +static int bt2ms(int bt) +{ + return bt * 312.5 / 1000; +} + +static int btoffset(uint32_t btclk1, uint32_t btclk2) +{ + int offset = btclk2 - btclk1; + + if (offset <= -MCAP_BTCLOCK_HALF) + offset += MCAP_BTCLOCK_FIELD; + else if (offset > MCAP_BTCLOCK_HALF) + offset -= MCAP_BTCLOCK_FIELD; + + return offset; +} + +static int btdiff(uint32_t btclk1, uint32_t btclk2) +{ + return btoffset(btclk1, btclk2); +} + +static gboolean valid_btclock(uint32_t btclk) +{ + return btclk <= MCAP_BTCLOCK_MAX; +} + +/* This call may fail; either deal with retry or use read_btclock_retry */ +static gboolean read_btclock(struct mcap_mcl *mcl, uint32_t *btclock, + uint16_t *btaccuracy) +{ + /* + * FIXME: btd_adapter_read_clock(...) always return FALSE, current + * code doesn't support CSP (Clock Synchronization Protocol). To avoid + * build dependancy on struct 'btd_adapter', removing this code. + */ + + return FALSE; +} + +static gboolean read_btclock_retry(struct mcap_mcl *mcl, uint32_t *btclock, + uint16_t *btaccuracy) +{ + int retries = 5; + + while (--retries >= 0) { + if (read_btclock(mcl, btclock, btaccuracy)) + return TRUE; + DBG("CSP: retrying to read bt clock..."); + } + + return FALSE; +} + +static gboolean get_btrole(struct mcap_mcl *mcl) +{ + int sock, flags; + socklen_t len; + + if (mcl->cc == NULL) + return -1; + + sock = g_io_channel_unix_get_fd(mcl->cc); + len = sizeof(flags); + + if (getsockopt(sock, SOL_L2CAP, L2CAP_LM, &flags, &len)) + DBG("CSP: could not read role"); + + return flags & L2CAP_LM_MASTER; +} + +uint64_t mcap_get_timestamp(struct mcap_mcl *mcl, + struct timespec *given_time) +{ + struct timespec now; + uint64_t tmstamp; + + if (!mcl->csp) + return MCAP_TMSTAMP_DONTSET; + + if (given_time) + now = *given_time; + else + if (clock_gettime(CLK, &now) < 0) + return MCAP_TMSTAMP_DONTSET; + + tmstamp = time_us(&now) - time_us(&mcl->csp->base_time) + + mcl->csp->base_tmstamp; + + return tmstamp; +} + +uint32_t mcap_get_btclock(struct mcap_mcl *mcl) +{ + uint32_t btclock; + uint16_t accuracy; + + if (!mcl->csp) + return MCAP_BTCLOCK_IMMEDIATE; + + if (!read_btclock_retry(mcl, &btclock, &accuracy)) + btclock = 0xffffffff; + + return btclock; +} + +static gboolean initialize_caps(struct mcap_mcl *mcl) +{ + struct timespec t1, t2; + int latencies[SAMPLE_COUNT]; + int latency, avg, dev; + uint32_t btclock; + uint16_t btaccuracy; + int i; + int retries; + + clock_getres(CLK, &t1); + + _caps.ts_res = time_us(&t1); + if (_caps.ts_res < 1) + _caps.ts_res = 1; + + _caps.ts_acc = 20; /* ppm, estimated */ + + /* A little exercise before measuing latency */ + clock_gettime(CLK, &t1); + read_btclock_retry(mcl, &btclock, &btaccuracy); + + /* Read clock a number of times and measure latency */ + avg = 0; + i = 0; + retries = MAX_RETRIES; + while (i < SAMPLE_COUNT && retries > 0) { + clock_gettime(CLK, &t1); + if (!read_btclock(mcl, &btclock, &btaccuracy)) { + retries--; + continue; + } + clock_gettime(CLK, &t2); + + latency = time_us(&t2) - time_us(&t1); + latencies[i] = latency; + avg += latency; + i++; + } + + if (retries <= 0) + return FALSE; + + /* Calculate average and deviation */ + avg /= SAMPLE_COUNT; + dev = 0; + for (i = 0; i < SAMPLE_COUNT; ++i) + dev += abs(latencies[i] - avg); + dev /= SAMPLE_COUNT; + + /* Calculate corrected average, without 'freak' latencies */ + latency = 0; + for (i = 0; i < SAMPLE_COUNT; ++i) { + if (latencies[i] > (avg + dev * 6)) + latency += avg; + else + latency += latencies[i]; + } + latency /= SAMPLE_COUNT; + + _caps.latency = latency; + _caps.preempt_thresh = latency * 4; + _caps.syncleadtime_ms = latency * 50 / 1000; + + csp_caps_initialized = TRUE; + return TRUE; +} + +static struct csp_caps *caps(struct mcap_mcl *mcl) +{ + if (!csp_caps_initialized) + if (!initialize_caps(mcl)) { + /* Temporary failure in reading BT clock */ + return NULL; + } + + return &_caps; +} + +static int send_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t rspcode, + uint8_t btclockres, uint16_t synclead, + uint16_t tmstampres, uint16_t tmstampacc) +{ + mcap_md_sync_cap_rsp *rsp; + int sent; + + rsp = g_new0(mcap_md_sync_cap_rsp, 1); + + rsp->op = MCAP_MD_SYNC_CAP_RSP; + rsp->rc = rspcode; + + rsp->btclock = btclockres; + rsp->sltime = htons(synclead); + rsp->timestnr = htons(tmstampres); + rsp->timestna = htons(tmstampacc); + + sent = send_sync_cmd(mcl, rsp, sizeof(*rsp)); + g_free(rsp); + + return sent; +} + +static void proc_sync_cap_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + mcap_md_sync_cap_req *req; + uint16_t required_accuracy; + uint16_t our_accuracy; + uint32_t btclock; + uint16_t btres; + + if (len != sizeof(mcap_md_sync_cap_req)) { + send_sync_cap_rsp(mcl, MCAP_INVALID_PARAM_VALUE, + 0, 0, 0, 0); + return; + } + + if (!caps(mcl)) { + send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, + 0, 0, 0, 0); + return; + } + + req = (mcap_md_sync_cap_req *) cmd; + required_accuracy = ntohs(req->timest); + our_accuracy = caps(mcl)->ts_acc; + btres = 0; + + if (required_accuracy < our_accuracy || required_accuracy < 1) { + send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, + 0, 0, 0, 0); + return; + } + + if (!read_btclock_retry(mcl, &btclock, &btres)) { + send_sync_cap_rsp(mcl, MCAP_RESOURCE_UNAVAILABLE, + 0, 0, 0, 0); + return; + } + + mcl->csp->remote_caps = 1; + mcl->csp->rem_req_acc = required_accuracy; + + send_sync_cap_rsp(mcl, MCAP_SUCCESS, btres, + caps(mcl)->syncleadtime_ms, + caps(mcl)->ts_res, our_accuracy); +} + +static int send_sync_set_rsp(struct mcap_mcl *mcl, uint8_t rspcode, + uint32_t btclock, uint64_t timestamp, + uint16_t tmstampres) +{ + mcap_md_sync_set_rsp *rsp; + int sent; + + rsp = g_new0(mcap_md_sync_set_rsp, 1); + + rsp->op = MCAP_MD_SYNC_SET_RSP; + rsp->rc = rspcode; + rsp->btclock = htonl(btclock); + rsp->timestst = hton64(timestamp); + rsp->timestsa = htons(tmstampres); + + sent = send_sync_cmd(mcl, rsp, sizeof(*rsp)); + g_free(rsp); + + return sent; +} + +static gboolean get_all_clocks(struct mcap_mcl *mcl, uint32_t *btclock, + struct timespec *base_time, + uint64_t *timestamp) +{ + int latency; + int retry = 5; + uint16_t btres; + struct timespec t0; + + if (!caps(mcl)) + return FALSE; + + latency = caps(mcl)->preempt_thresh + 1; + + while (latency > caps(mcl)->preempt_thresh && --retry >= 0) { + + if (clock_gettime(CLK, &t0) < 0) + return FALSE; + + if (!read_btclock(mcl, btclock, &btres)) + continue; + + if (clock_gettime(CLK, base_time) < 0) + return FALSE; + + /* + * Tries to detect preemption between clock_gettime + * and read_btclock by measuring transaction time + */ + latency = time_us(base_time) - time_us(&t0); + } + + if (retry < 0) + return FALSE; + + *timestamp = mcap_get_timestamp(mcl, base_time); + + return TRUE; +} + +static gboolean sync_send_indication(gpointer user_data) +{ + struct mcap_mcl *mcl; + mcap_md_sync_info_ind *cmd; + uint32_t btclock; + uint64_t tmstamp; + struct timespec base_time; + int sent; + + if (!user_data) + return FALSE; + + btclock = 0; + mcl = user_data; + + if (!caps(mcl)) + return FALSE; + + if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) + return FALSE; + + cmd = g_new0(mcap_md_sync_info_ind, 1); + + cmd->op = MCAP_MD_SYNC_INFO_IND; + cmd->btclock = htonl(btclock); + cmd->timestst = hton64(tmstamp); + cmd->timestsa = htons(caps(mcl)->latency); + + sent = send_sync_cmd(mcl, cmd, sizeof(*cmd)); + g_free(cmd); + + return !sent; +} + +static gboolean proc_sync_set_req_phase2(gpointer user_data) +{ + struct mcap_mcl *mcl; + struct sync_set_data *data; + uint8_t update; + uint32_t sched_btclock; + uint64_t new_tmstamp; + int ind_freq; + int role; + uint32_t btclock; + uint64_t tmstamp; + struct timespec base_time; + uint16_t tmstampacc; + gboolean reset; + int delay; + + if (!user_data) + return FALSE; + + mcl = user_data; + + if (!mcl->csp->set_data) + return FALSE; + + btclock = 0; + data = mcl->csp->set_data; + update = data->update; + sched_btclock = data->sched_btclock; + new_tmstamp = data->timestamp; + ind_freq = data->ind_freq; + role = data->role; + + if (!caps(mcl)) { + send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); + return FALSE; + } + + if (!get_all_clocks(mcl, &btclock, &base_time, &tmstamp)) { + send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); + return FALSE; + } + + if (get_btrole(mcl) != role) { + send_sync_set_rsp(mcl, MCAP_INVALID_OPERATION, 0, 0, 0); + return FALSE; + } + + reset = (new_tmstamp != MCAP_TMSTAMP_DONTSET); + + if (reset) { + if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE) { + delay = bt2us(btdiff(sched_btclock, btclock)); + if (delay >= 0 || ((new_tmstamp - delay) > 0)) { + new_tmstamp += delay; + DBG("CSP: reset w/ delay %dus, compensated", + delay); + } else + DBG("CSP: reset w/ delay %dus, uncompensated", + delay); + } + + reset_tmstamp(mcl->csp, &base_time, new_tmstamp); + tmstamp = new_tmstamp; + } + + tmstampacc = caps(mcl)->latency + caps(mcl)->ts_acc; + + if (mcl->csp->ind_timer) { + g_source_remove(mcl->csp->ind_timer); + mcl->csp->ind_timer = 0; + } + + if (update) { + int when = ind_freq + caps(mcl)->syncleadtime_ms; + mcl->csp->ind_timer = g_timeout_add(when, + sync_send_indication, + mcl); + } + + send_sync_set_rsp(mcl, MCAP_SUCCESS, btclock, tmstamp, tmstampacc); + + /* First indication after set is immediate */ + if (update) + sync_send_indication(mcl); + + return FALSE; +} + +static void proc_sync_set_req(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + mcap_md_sync_set_req *req; + uint32_t sched_btclock, cur_btclock; + uint16_t btres; + uint8_t update; + uint64_t timestamp; + struct sync_set_data *set_data; + int phase2_delay, ind_freq, when; + + if (len != sizeof(mcap_md_sync_set_req)) { + send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); + return; + } + + req = (mcap_md_sync_set_req *) cmd; + sched_btclock = ntohl(req->btclock); + update = req->timestui; + timestamp = ntoh64(req->timestst); + cur_btclock = 0; + + if (sched_btclock != MCAP_BTCLOCK_IMMEDIATE && + !valid_btclock(sched_btclock)) { + send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); + return; + } + + if (update > 1) { + send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); + return; + } + + if (!mcl->csp->remote_caps) { + /* Remote side did not ask our capabilities yet */ + send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, 0, 0, 0); + return; + } + + if (!caps(mcl)) { + send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); + return; + } + + if (!read_btclock_retry(mcl, &cur_btclock, &btres)) { + send_sync_set_rsp(mcl, MCAP_UNSPECIFIED_ERROR, 0, 0, 0); + return; + } + + if (sched_btclock == MCAP_BTCLOCK_IMMEDIATE) + phase2_delay = 0; + else { + phase2_delay = btdiff(cur_btclock, sched_btclock); + + if (phase2_delay < 0) { + /* can not reset in the past tense */ + send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, + 0, 0, 0); + return; + } + + /* Convert to miliseconds */ + phase2_delay = bt2ms(phase2_delay); + + if (phase2_delay > 61*1000) { + /* More than 60 seconds in the future */ + send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, + 0, 0, 0); + return; + } else if (phase2_delay < caps(mcl)->latency / 1000) { + /* Too fast for us to do in time */ + send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, + 0, 0, 0); + return; + } + } + + if (update) { + /* + * Indication frequency: required accuracy divided by ours + * Converted to milisseconds + */ + ind_freq = (1000 * mcl->csp->rem_req_acc) / caps(mcl)->ts_acc; + + if (ind_freq < MAX(caps(mcl)->latency * 2 / 1000, 100)) { + /* Too frequent, we can't handle */ + send_sync_set_rsp(mcl, MCAP_INVALID_PARAM_VALUE, + 0, 0, 0); + return; + } + + DBG("CSP: indication every %dms", ind_freq); + } else + ind_freq = 0; + + if (mcl->csp->ind_timer) { + /* Old indications are no longer sent */ + g_source_remove(mcl->csp->ind_timer); + mcl->csp->ind_timer = 0; + } + + if (!mcl->csp->set_data) + mcl->csp->set_data = g_new0(struct sync_set_data, 1); + + set_data = (struct sync_set_data *) mcl->csp->set_data; + + set_data->update = update; + set_data->sched_btclock = sched_btclock; + set_data->timestamp = timestamp; + set_data->ind_freq = ind_freq; + set_data->role = get_btrole(mcl); + + /* + * TODO is there some way to schedule a call based directly on + * a BT clock value, instead of this estimation that uses + * the SO clock? + */ + + if (phase2_delay > 0) { + when = phase2_delay + caps(mcl)->syncleadtime_ms; + mcl->csp->set_timer = g_timeout_add(when, + proc_sync_set_req_phase2, + mcl); + } else + proc_sync_set_req_phase2(mcl); + + /* First indication is immediate */ + if (update) + sync_send_indication(mcl); +} + +static void proc_sync_cap_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + mcap_md_sync_cap_rsp *rsp; + uint8_t mcap_err; + uint8_t btclockres; + uint16_t synclead; + uint16_t tmstampres; + uint16_t tmstampacc; + struct mcap_sync_cap_cbdata *cbdata; + mcap_sync_cap_cb cb; + gpointer user_data; + + if (mcl->csp->csp_req != MCAP_MD_SYNC_CAP_REQ) { + DBG("CSP: got unexpected cap respose"); + return; + } + + if (!mcl->csp->csp_priv_data) { + DBG("CSP: no priv data for cap respose"); + return; + } + + cbdata = mcl->csp->csp_priv_data; + cb = cbdata->cb; + user_data = cbdata->user_data; + g_free(cbdata); + + mcl->csp->csp_priv_data = NULL; + mcl->csp->csp_req = 0; + + if (len != sizeof(mcap_md_sync_cap_rsp)) { + DBG("CSP: got corrupted cap respose"); + return; + } + + rsp = (mcap_md_sync_cap_rsp *) cmd; + mcap_err = rsp->rc; + btclockres = rsp->btclock; + synclead = ntohs(rsp->sltime); + tmstampres = ntohs(rsp->timestnr); + tmstampacc = ntohs(rsp->timestna); + + if (!mcap_err) + mcl->csp->local_caps = TRUE; + + cb(mcl, mcap_err, btclockres, synclead, tmstampres, tmstampacc, NULL, + user_data); +} + +static void proc_sync_set_rsp(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + mcap_md_sync_set_rsp *rsp; + uint8_t mcap_err; + uint32_t btclock; + uint64_t timestamp; + uint16_t accuracy; + struct mcap_sync_set_cbdata *cbdata; + mcap_sync_set_cb cb; + gpointer user_data; + + if (mcl->csp->csp_req != MCAP_MD_SYNC_SET_REQ) { + DBG("CSP: got unexpected set respose"); + return; + } + + if (!mcl->csp->csp_priv_data) { + DBG("CSP: no priv data for set respose"); + return; + } + + cbdata = mcl->csp->csp_priv_data; + cb = cbdata->cb; + user_data = cbdata->user_data; + g_free(cbdata); + + mcl->csp->csp_priv_data = NULL; + mcl->csp->csp_req = 0; + + if (len != sizeof(mcap_md_sync_set_rsp)) { + DBG("CSP: got corrupted set respose"); + return; + } + + rsp = (mcap_md_sync_set_rsp *) cmd; + mcap_err = rsp->rc; + btclock = ntohl(rsp->btclock); + timestamp = ntoh64(rsp->timestst); + accuracy = ntohs(rsp->timestsa); + + if (!mcap_err && !valid_btclock(btclock)) + mcap_err = MCAP_ERROR_INVALID_ARGS; + + cb(mcl, mcap_err, btclock, timestamp, accuracy, NULL, user_data); +} + +static void proc_sync_info_ind(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + mcap_md_sync_info_ind *req; + struct sync_info_ind_data data; + uint32_t btclock; + + if (!mcl->csp->ind_expected) { + DBG("CSP: received unexpected info indication"); + return; + } + + if (len != sizeof(mcap_md_sync_info_ind)) + return; + + req = (mcap_md_sync_info_ind *) cmd; + + btclock = ntohl(req->btclock); + + if (!valid_btclock(btclock)) + return; + + data.btclock = btclock; + data.timestamp = ntoh64(req->timestst); + data.accuracy = ntohs(req->timestsa); + + if (mcl->mi->mcl_sync_infoind_cb) + mcl->mi->mcl_sync_infoind_cb(mcl, &data); +} + +void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len) +{ + if (!mcl->mi->csp_enabled || !mcl->csp) { + switch (cmd[0]) { + case MCAP_MD_SYNC_CAP_REQ: + send_unsupported_cap_req(mcl); + break; + case MCAP_MD_SYNC_SET_REQ: + send_unsupported_set_req(mcl); + break; + } + return; + } + + switch (cmd[0]) { + case MCAP_MD_SYNC_CAP_REQ: + proc_sync_cap_req(mcl, cmd, len); + break; + case MCAP_MD_SYNC_CAP_RSP: + proc_sync_cap_rsp(mcl, cmd, len); + break; + case MCAP_MD_SYNC_SET_REQ: + proc_sync_set_req(mcl, cmd, len); + break; + case MCAP_MD_SYNC_SET_RSP: + proc_sync_set_rsp(mcl, cmd, len); + break; + case MCAP_MD_SYNC_INFO_IND: + proc_sync_info_ind(mcl, cmd, len); + break; + } +} + +void mcap_sync_cap_req(struct mcap_mcl *mcl, uint16_t reqacc, + mcap_sync_cap_cb cb, gpointer user_data, + GError **err) +{ + struct mcap_sync_cap_cbdata *cbdata; + mcap_md_sync_cap_req *cmd; + + if (!mcl->mi->csp_enabled || !mcl->csp) { + g_set_error(err, + MCAP_CSP_ERROR, + MCAP_ERROR_RESOURCE_UNAVAILABLE, + "CSP not enabled for the instance"); + return; + } + + if (mcl->csp->csp_req) { + g_set_error(err, + MCAP_CSP_ERROR, + MCAP_ERROR_RESOURCE_UNAVAILABLE, + "Pending CSP request"); + return; + } + + mcl->csp->csp_req = MCAP_MD_SYNC_CAP_REQ; + cmd = g_new0(mcap_md_sync_cap_req, 1); + + cmd->op = MCAP_MD_SYNC_CAP_REQ; + cmd->timest = htons(reqacc); + + cbdata = g_new0(struct mcap_sync_cap_cbdata, 1); + cbdata->cb = cb; + cbdata->user_data = user_data; + mcl->csp->csp_priv_data = cbdata; + + send_sync_cmd(mcl, cmd, sizeof(*cmd)); + + g_free(cmd); +} + +void mcap_sync_set_req(struct mcap_mcl *mcl, uint8_t update, uint32_t btclock, + uint64_t timestamp, mcap_sync_set_cb cb, + gpointer user_data, GError **err) +{ + mcap_md_sync_set_req *cmd; + struct mcap_sync_set_cbdata *cbdata; + + if (!mcl->mi->csp_enabled || !mcl->csp) { + g_set_error(err, + MCAP_CSP_ERROR, + MCAP_ERROR_RESOURCE_UNAVAILABLE, + "CSP not enabled for the instance"); + return; + } + + if (!mcl->csp->local_caps) { + g_set_error(err, + MCAP_CSP_ERROR, + MCAP_ERROR_RESOURCE_UNAVAILABLE, + "Did not get CSP caps from slave yet"); + return; + } + + if (mcl->csp->csp_req) { + g_set_error(err, + MCAP_CSP_ERROR, + MCAP_ERROR_RESOURCE_UNAVAILABLE, + "Pending CSP request"); + return; + } + + mcl->csp->csp_req = MCAP_MD_SYNC_SET_REQ; + cmd = g_new0(mcap_md_sync_set_req, 1); + + cmd->op = MCAP_MD_SYNC_SET_REQ; + cmd->timestui = update; + cmd->btclock = htonl(btclock); + cmd->timestst = hton64(timestamp); + + mcl->csp->ind_expected = update; + + cbdata = g_new0(struct mcap_sync_set_cbdata, 1); + cbdata->cb = cb; + cbdata->user_data = user_data; + mcl->csp->csp_priv_data = cbdata; + + send_sync_cmd(mcl, cmd, sizeof(*cmd)); + + g_free(cmd); +} + +void mcap_enable_csp(struct mcap_instance *mi) +{ + mi->csp_enabled = TRUE; +} + +void mcap_disable_csp(struct mcap_instance *mi) +{ + mi->csp_enabled = FALSE; +} diff -Nru bluez-4.101/android/mcap-lib.h bluez-5.23/android/mcap-lib.h --- bluez-4.101/android/mcap-lib.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/mcap-lib.h 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,437 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2010 GSyC/LibreSoft, Universidad Rey Juan Carlos. + * Copyright (C) 2010 Signove + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#define MCAP_VERSION 0x0100 /* current version 01.00 */ + +/* bytes to get MCAP Supported Procedures */ +#define MCAP_SUP_PROC 0x06 + +/* maximum transmission unit for channels */ +#define MCAP_CC_MTU 48 +#define MCAP_DC_MTU 65535 + +/* MCAP Standard Op Codes */ +#define MCAP_ERROR_RSP 0x00 +#define MCAP_MD_CREATE_MDL_REQ 0x01 +#define MCAP_MD_CREATE_MDL_RSP 0x02 +#define MCAP_MD_RECONNECT_MDL_REQ 0x03 +#define MCAP_MD_RECONNECT_MDL_RSP 0x04 +#define MCAP_MD_ABORT_MDL_REQ 0x05 +#define MCAP_MD_ABORT_MDL_RSP 0x06 +#define MCAP_MD_DELETE_MDL_REQ 0x07 +#define MCAP_MD_DELETE_MDL_RSP 0x08 + +/* MCAP Clock Sync Op Codes */ +#define MCAP_MD_SYNC_CAP_REQ 0x11 +#define MCAP_MD_SYNC_CAP_RSP 0x12 +#define MCAP_MD_SYNC_SET_REQ 0x13 +#define MCAP_MD_SYNC_SET_RSP 0x14 +#define MCAP_MD_SYNC_INFO_IND 0x15 + +/* MCAP Response codes */ +#define MCAP_SUCCESS 0x00 +#define MCAP_INVALID_OP_CODE 0x01 +#define MCAP_INVALID_PARAM_VALUE 0x02 +#define MCAP_INVALID_MDEP 0x03 +#define MCAP_MDEP_BUSY 0x04 +#define MCAP_INVALID_MDL 0x05 +#define MCAP_MDL_BUSY 0x06 +#define MCAP_INVALID_OPERATION 0x07 +#define MCAP_RESOURCE_UNAVAILABLE 0x08 +#define MCAP_UNSPECIFIED_ERROR 0x09 +#define MCAP_REQUEST_NOT_SUPPORTED 0x0A +#define MCAP_CONFIGURATION_REJECTED 0x0B + +/* MDL IDs */ +#define MCAP_MDLID_RESERVED 0x0000 +#define MCAP_MDLID_INITIAL 0x0001 +#define MCAP_MDLID_FINAL 0xFEFF +#define MCAP_ALL_MDLIDS 0xFFFF + +/* MDEP IDs */ +#define MCAP_MDEPID_INITIAL 0x00 +#define MCAP_MDEPID_FINAL 0x7F + +/* CSP special values */ +#define MCAP_BTCLOCK_IMMEDIATE 0xffffffffUL +#define MCAP_TMSTAMP_DONTSET 0xffffffffffffffffULL +#define MCAP_BTCLOCK_MAX 0x0fffffff +#define MCAP_BTCLOCK_FIELD (MCAP_BTCLOCK_MAX + 1) + +#define MCAP_CTRL_CACHED 0x01 /* MCL is cached */ +#define MCAP_CTRL_STD_OP 0x02 /* Support for standard op codes */ +#define MCAP_CTRL_SYNC_OP 0x04 /* Support for synchronization commands */ +#define MCAP_CTRL_CONN 0x08 /* MCL is in connecting process */ +#define MCAP_CTRL_FREE 0x10 /* MCL is marked as releasable */ +#define MCAP_CTRL_NOCACHE 0x20 /* MCL is marked as not cacheable */ + +/* + * MCAP Request Packet Format + */ + +typedef struct { + uint8_t op; + uint16_t mdl; + uint8_t mdep; + uint8_t conf; +} __attribute__ ((packed)) mcap_md_create_mdl_req; + +typedef struct { + uint8_t op; + uint16_t mdl; +} __attribute__ ((packed)) mcap_md_req; + +/* MCAP Response Packet Format */ + +typedef struct { + uint8_t op; + uint8_t rc; + uint16_t mdl; + uint8_t data[0]; +} __attribute__ ((packed)) mcap_rsp; + +/* MCAP Clock Synchronization Protocol */ + +typedef struct { + uint8_t op; + uint16_t timest; +} __attribute__ ((packed)) mcap_md_sync_cap_req; + +typedef struct { + uint8_t op; + uint8_t rc; +} __attribute__ ((packed)) mcap_md_sync_rsp; + +typedef struct { + uint8_t op; + uint8_t rc; + uint8_t btclock; + uint16_t sltime; + uint16_t timestnr; + uint16_t timestna; +} __attribute__ ((packed)) mcap_md_sync_cap_rsp; + +typedef struct { + uint8_t op; + uint8_t timestui; + uint32_t btclock; + uint64_t timestst; +} __attribute__ ((packed)) mcap_md_sync_set_req; + +typedef struct { + int8_t op; + uint8_t rc; + uint32_t btclock; + uint64_t timestst; + uint16_t timestsa; +} __attribute__ ((packed)) mcap_md_sync_set_rsp; + +typedef struct { + uint8_t op; + uint32_t btclock; + uint64_t timestst; + uint16_t timestsa; +} __attribute__ ((packed)) mcap_md_sync_info_ind; + +typedef enum { +/* MCAP Error Response Codes */ + MCAP_ERROR_INVALID_OP_CODE = 1, + MCAP_ERROR_INVALID_PARAM_VALUE, + MCAP_ERROR_INVALID_MDEP, + MCAP_ERROR_MDEP_BUSY, + MCAP_ERROR_INVALID_MDL, + MCAP_ERROR_MDL_BUSY, + MCAP_ERROR_INVALID_OPERATION, + MCAP_ERROR_RESOURCE_UNAVAILABLE, + MCAP_ERROR_UNSPECIFIED_ERROR, + MCAP_ERROR_REQUEST_NOT_SUPPORTED, + MCAP_ERROR_CONFIGURATION_REJECTED, +/* MCAP Internal Errors */ + MCAP_ERROR_INVALID_ARGS, + MCAP_ERROR_ALREADY_EXISTS, + MCAP_ERROR_REQ_IGNORED, + MCAP_ERROR_MCL_CLOSED, + MCAP_ERROR_FAILED +} McapError; + +typedef enum { + MCAP_MDL_CB_INVALID, + MCAP_MDL_CB_CONNECTED, /* mcap_mdl_event_cb */ + MCAP_MDL_CB_CLOSED, /* mcap_mdl_event_cb */ + MCAP_MDL_CB_DELETED, /* mcap_mdl_event_cb */ + MCAP_MDL_CB_ABORTED, /* mcap_mdl_event_cb */ + MCAP_MDL_CB_REMOTE_CONN_REQ, /* mcap_remote_mdl_conn_req_cb */ + MCAP_MDL_CB_REMOTE_RECONN_REQ /* mcap_remote_mdl_reconn_req_cb */ +} McapMclCb; + +typedef enum { + MCL_CONNECTED, + MCL_PENDING, + MCL_ACTIVE, + MCL_IDLE +} MCLState; + +typedef enum { + MCL_ACCEPTOR, + MCL_INITIATOR +} MCLRole; + +typedef enum { + MCL_AVAILABLE, + MCL_WAITING_RSP +} MCAPCtrl; + +typedef enum { + MDL_WAITING, + MDL_CONNECTED, + MDL_DELETING, + MDL_CLOSED +} MDLState; + +struct mcap_csp; +struct mcap_mdl_op_cb; +struct mcap_instance; +struct mcap_mcl; +struct mcap_mdl; +struct sync_info_ind_data; + +/************ Callbacks ************/ + +/* MDL callbacks */ + +typedef void (* mcap_mdl_event_cb) (struct mcap_mdl *mdl, gpointer data); +typedef void (* mcap_mdl_operation_conf_cb) (struct mcap_mdl *mdl, uint8_t conf, + GError *err, gpointer data); +typedef void (* mcap_mdl_operation_cb) (struct mcap_mdl *mdl, GError *err, + gpointer data); +typedef void (* mcap_mdl_notify_cb) (GError *err, gpointer data); + +/* Next function should return an MCAP appropriate response code */ +typedef uint8_t (* mcap_remote_mdl_conn_req_cb) (struct mcap_mcl *mcl, + uint8_t mdepid, uint16_t mdlid, + uint8_t *conf, gpointer data); +typedef uint8_t (* mcap_remote_mdl_reconn_req_cb) (struct mcap_mdl *mdl, + gpointer data); + +/* MCL callbacks */ + +typedef void (* mcap_mcl_event_cb) (struct mcap_mcl *mcl, gpointer data); +typedef void (* mcap_mcl_connect_cb) (struct mcap_mcl *mcl, GError *err, + gpointer data); + +/* CSP callbacks */ + +typedef void (* mcap_info_ind_event_cb) (struct mcap_mcl *mcl, + struct sync_info_ind_data *data); + +typedef void (* mcap_sync_cap_cb) (struct mcap_mcl *mcl, + uint8_t mcap_err, + uint8_t btclockres, + uint16_t synclead, + uint16_t tmstampres, + uint16_t tmstampacc, + GError *err, + gpointer data); + +typedef void (* mcap_sync_set_cb) (struct mcap_mcl *mcl, + uint8_t mcap_err, + uint32_t btclock, + uint64_t timestamp, + uint16_t accuracy, + GError *err, + gpointer data); + +struct mcap_mdl_cb { + mcap_mdl_event_cb mdl_connected; /* Remote device has created a MDL */ + mcap_mdl_event_cb mdl_closed; /* Remote device has closed a MDL */ + mcap_mdl_event_cb mdl_deleted; /* Remote device requested deleting a MDL */ + mcap_mdl_event_cb mdl_aborted; /* Remote device aborted the mdl creation */ + mcap_remote_mdl_conn_req_cb mdl_conn_req; /* Remote device requested creating a MDL */ + mcap_remote_mdl_reconn_req_cb mdl_reconn_req; /* Remote device requested reconnecting a MDL */ + gpointer user_data; /* User data */ +}; + +struct mcap_instance { + bdaddr_t src; /* Source address */ + GIOChannel *ccio; /* Control Channel IO */ + GIOChannel *dcio; /* Data Channel IO */ + GSList *mcls; /* MCAP instance list */ + GSList *cached; /* List with all cached MCLs (MAX_CACHED macro) */ + BtIOSecLevel sec; /* Security level */ + mcap_mcl_event_cb mcl_connected_cb; /* New MCL connected */ + mcap_mcl_event_cb mcl_reconnected_cb; /* Old MCL has been reconnected */ + mcap_mcl_event_cb mcl_disconnected_cb; /* MCL disconnected */ + mcap_mcl_event_cb mcl_uncached_cb; /* MCL has been removed from MCAP cache */ + mcap_info_ind_event_cb mcl_sync_infoind_cb; /* (CSP Master) Received info indication */ + gpointer user_data; /* Data to be provided in callbacks */ + int ref; /* Reference counter */ + + gboolean csp_enabled; /* CSP: functionality enabled */ +}; + +struct mcap_mcl { + struct mcap_instance *mi; /* MCAP instance where this MCL belongs */ + bdaddr_t addr; /* Device address */ + GIOChannel *cc; /* MCAP Control Channel IO */ + guint wid; /* MCL Watcher id */ + GSList *mdls; /* List of Data Channels shorted by mdlid */ + MCLState state; /* Current MCL State */ + MCLRole role; /* Initiator or acceptor of this MCL */ + MCAPCtrl req; /* Request control flag */ + struct mcap_mdl_op_cb *priv_data; /* Temporal data to manage responses */ + struct mcap_mdl_cb *cb; /* MDL callbacks */ + guint tid; /* Timer id for waiting for a response */ + uint8_t *lcmd; /* Last command sent */ + int ref; /* References counter */ + uint8_t ctrl; /* MCL control flag */ + uint16_t next_mdl; /* id used to create next MDL */ + struct mcap_csp *csp; /* CSP control structure */ +}; + +struct mcap_mdl { + struct mcap_mcl *mcl; /* MCL where this MDL belongs */ + GIOChannel *dc; /* MCAP Data Channel IO */ + guint wid; /* MDL Watcher id */ + uint16_t mdlid; /* MDL id */ + uint8_t mdep_id; /* MCAP Data End Point */ + MDLState state; /* MDL state */ + int ref; /* References counter */ +}; + +struct sync_info_ind_data { + uint32_t btclock; + uint64_t timestamp; + uint16_t accuracy; +}; + +/************ Operations ************/ + +/* MDL operations */ + +gboolean mcap_create_mdl(struct mcap_mcl *mcl, + uint8_t mdepid, + uint8_t conf, + mcap_mdl_operation_conf_cb connect_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err); +gboolean mcap_reconnect_mdl(struct mcap_mdl *mdl, + mcap_mdl_operation_cb reconnect_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err); +gboolean mcap_delete_all_mdls(struct mcap_mcl *mcl, + mcap_mdl_notify_cb delete_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err); +gboolean mcap_delete_mdl(struct mcap_mdl *mdl, + mcap_mdl_notify_cb delete_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err); +gboolean mcap_connect_mdl(struct mcap_mdl *mdl, + uint8_t mode, + uint16_t dcpsm, + mcap_mdl_operation_cb connect_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err); +gboolean mcap_mdl_abort(struct mcap_mdl *mdl, + mcap_mdl_notify_cb abort_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err); + +int mcap_mdl_get_fd(struct mcap_mdl *mdl); +uint16_t mcap_mdl_get_mdlid(struct mcap_mdl *mdl); +struct mcap_mdl *mcap_mdl_ref(struct mcap_mdl *mdl); +void mcap_mdl_unref(struct mcap_mdl *mdl); + +/* MCL operations */ + +gboolean mcap_create_mcl(struct mcap_instance *mi, + const bdaddr_t *addr, + uint16_t ccpsm, + mcap_mcl_connect_cb connect_cb, + gpointer user_data, + GDestroyNotify destroy, + GError **err); +void mcap_close_mcl(struct mcap_mcl *mcl, gboolean cache); +gboolean mcap_mcl_set_cb(struct mcap_mcl *mcl, gpointer user_data, + GError **gerr, McapMclCb cb1, ...); +void mcap_mcl_get_addr(struct mcap_mcl *mcl, bdaddr_t *addr); +struct mcap_mcl *mcap_mcl_ref(struct mcap_mcl *mcl); +void mcap_mcl_unref(struct mcap_mcl *mcl); + +/* CSP operations */ + +void mcap_enable_csp(struct mcap_instance *mi); +void mcap_disable_csp(struct mcap_instance *mi); +uint64_t mcap_get_timestamp(struct mcap_mcl *mcl, + struct timespec *given_time); +uint32_t mcap_get_btclock(struct mcap_mcl *mcl); + +void mcap_sync_cap_req(struct mcap_mcl *mcl, + uint16_t reqacc, + mcap_sync_cap_cb cb, + gpointer user_data, + GError **err); + +void mcap_sync_set_req(struct mcap_mcl *mcl, + uint8_t update, + uint32_t btclock, + uint64_t timestamp, + mcap_sync_set_cb cb, + gpointer user_data, + GError **err); + +/* MCAP main operations */ + +struct mcap_instance *mcap_create_instance(const bdaddr_t *src, + BtIOSecLevel sec, uint16_t ccpsm, + uint16_t dcpsm, + mcap_mcl_event_cb mcl_connected, + mcap_mcl_event_cb mcl_reconnected, + mcap_mcl_event_cb mcl_disconnected, + mcap_mcl_event_cb mcl_uncached, + mcap_info_ind_event_cb mcl_sync_info_ind, + gpointer user_data, + GError **gerr); +void mcap_release_instance(struct mcap_instance *mi); + +struct mcap_instance *mcap_instance_ref(struct mcap_instance *mi); +void mcap_instance_unref(struct mcap_instance *mi); + +uint16_t mcap_get_ctrl_psm(struct mcap_instance *mi, GError **err); +uint16_t mcap_get_data_psm(struct mcap_instance *mi, GError **err); + +gboolean mcap_set_data_chan_mode(struct mcap_instance *mi, uint8_t mode, + GError **err); + +int mcap_send_data(int sock, const void *buf, uint32_t size); + +void proc_sync_cmd(struct mcap_mcl *mcl, uint8_t *cmd, uint32_t len); +void mcap_sync_init(struct mcap_mcl *mcl); +void mcap_sync_stop(struct mcap_mcl *mcl); diff -Nru bluez-4.101/android/mcaptest.c bluez-5.23/android/mcaptest.c --- bluez-4.101/android/mcaptest.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/mcaptest.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,430 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "btio/btio.h" +#include "lib/l2cap.h" +#include "android/mcap-lib.h" + +enum { + MODE_NONE, + MODE_CONNECT, + MODE_LISTEN, +}; + +static GMainLoop *mloop; + +static int ccpsm = 0x1003, dcpsm = 0x1005; + +static struct mcap_instance *mcap = NULL; +static struct mcap_mdl *mdl = NULL; +static uint16_t mdlid; + +static int control_mode = MODE_LISTEN; +static int data_mode = MODE_LISTEN; + +static int mdl_conn_req_result = MCAP_SUCCESS; + +static gboolean send_synccap_req = FALSE; +static gboolean mcl_disconnect = FALSE; +static gboolean mdl_disconnect = FALSE; +static int mcl_disconnect_timeout = -1; +static int mdl_disconnect_timeout = -1; + +static struct mcap_mcl *mcl = NULL; + +#define REQ_CLOCK_ACC 0x1400 + +static void mdl_connected_cb(struct mcap_mdl *mdl, void *data) +{ + int fd = -1; + + printf("%s\n", __func__); + + if (mdl_disconnect && mdl_disconnect_timeout >= 0) { + sleep(mdl_disconnect_timeout); + + fd = mcap_mdl_get_fd(mdl); + + if (fd > 0) + close(fd); + } +} + +static void mdl_closed_cb(struct mcap_mdl *mdl, void *data) +{ + printf("%s\n", __func__); + + if (mcl_disconnect && mcl_disconnect_timeout >= 0) { + sleep(mcl_disconnect_timeout); + + mcap_close_mcl(mcl, TRUE); + } +} + +static void mdl_deleted_cb(struct mcap_mdl *mdl, void *data) +{ + /* TODO */ + printf("%s\n", __func__); +} + +static void mdl_aborted_cb(struct mcap_mdl *mdl, void *data) +{ + /* TODO */ + printf("%s\n", __func__); +} + +static uint8_t mdl_conn_req_cb(struct mcap_mcl *mcl, uint8_t mdepid, + uint16_t mdlid, uint8_t *conf, void *data) +{ + int ret; + + printf("%s\n", __func__); + + ret = mdl_conn_req_result; + + mdl_conn_req_result = MCAP_SUCCESS; + + return ret; +} + +static uint8_t mdl_reconn_req_cb(struct mcap_mdl *mdl, void *data) +{ + printf("%s\n", __func__); + + return MCAP_SUCCESS; +} + +static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr, + gpointer data); + +static void mcl_reconnected(struct mcap_mcl *mcl, gpointer data) +{ + GError *gerr = NULL; + + printf("%s\n", __func__); + + if (data_mode == MODE_CONNECT) { + mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr); + if (gerr) { + printf("Could not connect MDL: %s\n", gerr->message); + g_error_free(gerr); + } + } +} + +static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data) +{ + /* TODO */ + printf("MCL disconnected\n"); +} + +static void mcl_uncached(struct mcap_mcl *mcl, gpointer data) +{ + /* TODO */ + printf("MCL uncached unsupported\n"); +} + +static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data) +{ + mdlid = mcap_mdl_get_mdlid(mdl); + + printf("MDL %d connected\n", mdlid); +} + +static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr, + gpointer data) +{ + GError *err = NULL; + + if (gerr) { + printf("MDL error: %s\n", gerr->message); + + return; + } + + if (mdl) + mcap_mdl_unref(mdl); + + mdl = mcap_mdl_ref(mcap_mdl); + + if (!mcap_connect_mdl(mdl, L2CAP_MODE_ERTM, dcpsm, connect_mdl_cb, NULL, + NULL, &err)) { + printf("Error connecting to mdl: %s\n", err->message); + g_error_free(err); + } +} + +static void sync_cap_cb(struct mcap_mcl *mcl, uint8_t mcap_err, + uint8_t btclockres, uint16_t synclead, + uint16_t tmstampres, uint16_t tmstampacc, GError *err, + gpointer data) +{ + /* TODO */ + printf("%s\n", __func__); +} + +static void trigger_mdl_action(int mode) +{ + GError *gerr = NULL; + gboolean ret; + + ret = mcap_mcl_set_cb(mcl, NULL, &gerr, + MCAP_MDL_CB_CONNECTED, mdl_connected_cb, + MCAP_MDL_CB_CLOSED, mdl_closed_cb, + MCAP_MDL_CB_DELETED, mdl_deleted_cb, + MCAP_MDL_CB_ABORTED, mdl_aborted_cb, + MCAP_MDL_CB_REMOTE_CONN_REQ, mdl_conn_req_cb, + MCAP_MDL_CB_REMOTE_RECONN_REQ, mdl_reconn_req_cb, + MCAP_MDL_CB_INVALID); + + if (!ret && gerr) { + printf("MCL cannot handle connection %s\n", + gerr->message); + g_error_free(gerr); + } + + if (mode == MODE_CONNECT) { + mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr); + if (gerr) { + printf("Could not connect MDL: %s\n", gerr->message); + g_error_free(gerr); + } + } + + if (send_synccap_req && mcap->csp_enabled) { + mcap_sync_init(mcl); + + mcap_sync_cap_req(mcl, REQ_CLOCK_ACC, sync_cap_cb, NULL, &gerr); + if (gerr) { + printf("MCAP Sync req error: %s\n", gerr->message); + g_error_free(gerr); + } + } +} + +static void mcl_connected(struct mcap_mcl *mcap_mcl, gpointer data) +{ + printf("%s\n", __func__); + + if (mcl) { + mcap_sync_stop(mcl); + mcap_mcl_unref(mcl); + } + + mcl = mcap_mcl_ref(mcap_mcl); + trigger_mdl_action(data_mode); +} + +static void create_mcl_cb(struct mcap_mcl *mcap_mcl, GError *err, gpointer data) +{ + if (err) { + printf("Could not connect MCL: %s\n", err->message); + + return; + } + + if (mcl) { + mcap_sync_stop(mcl); + mcap_mcl_unref(mcl); + } + + mcl = mcap_mcl_ref(mcap_mcl); + trigger_mdl_action(data_mode); +} +static void usage(void) +{ + printf("mcaptest - MCAP testing ver %s\n", VERSION); + printf("Usage:\n" + "\tmcaptest [options]\n"); + printf("Control Link Mode:\n" + "\t-c connect \n" + "\t-e disconnect MCL and quit after MDL is closed\n" + "\t-g send clock sync capability request if MCL connected\n"); + printf("Data Link Mode:\n" + "\t-d connect\n" + "\t-f disconnect MDL after it's connected\n" + "\t-u send \'Unavailable\' on first MDL connection request\n"); + printf("Options:\n" + "\t-i HCI device\n" + "\t-C Control channel PSM\n" + "\t-D Data channel PSM\n"); +} + +static struct option main_options[] = { + { "help", 0, 0, 'h' }, + { "device", 1, 0, 'i' }, + { "connect_cl", 1, 0, 'c' }, + { "disconnect_cl", 1, 0, 'e' }, + { "synccap_req", 0, 0, 'g' }, + { "connect_dl", 0, 0, 'd' }, + { "disconnect_dl", 1, 0, 'f' }, + { "unavailable_dl", 0, 0, 'u' }, + { "control_ch", 1, 0, 'C' }, + { "data_ch", 1, 0, 'D' }, + { 0, 0, 0, 0 } +}; +int main(int argc, char *argv[]) +{ + GError *err = NULL; + bdaddr_t src, dst; + int opt; + char bdastr[18]; + + hci_devba(0, &src); + bacpy(&dst, BDADDR_ANY); + + mloop = g_main_loop_new(NULL, FALSE); + if (!mloop) { + printf("Cannot create main loop\n"); + + exit(1); + } + + while ((opt = getopt_long(argc, argv, "+i:c:C:D:e:f:dghu", + main_options, NULL)) != EOF) { + switch (opt) { + case 'i': + if (!strncmp(optarg, "hci", 3)) + hci_devba(atoi(optarg + 3), &src); + else + str2ba(optarg, &src); + + break; + + case 'c': + control_mode = MODE_CONNECT; + str2ba(optarg, &dst); + + break; + + case 'd': + data_mode = MODE_CONNECT; + + break; + + case 'e': + mcl_disconnect = TRUE; + mcl_disconnect_timeout = atoi(optarg); + + break; + + case 'f': + mdl_disconnect = TRUE; + mdl_disconnect_timeout = atoi(optarg); + + break; + + case 'g': + send_synccap_req = TRUE; + + break; + + case 'u': + mdl_conn_req_result = MCAP_RESOURCE_UNAVAILABLE; + + break; + + case 'C': + ccpsm = atoi(optarg); + + break; + + case 'D': + dcpsm = atoi(optarg); + + break; + + case 'h': + default: + usage(); + exit(0); + } + } + + mcap = mcap_create_instance(&src, BT_IO_SEC_MEDIUM, ccpsm, dcpsm, + mcl_connected, mcl_reconnected, + mcl_disconnected, mcl_uncached, + NULL, /* CSP is not used right now */ + NULL, &err); + + if (!mcap) { + printf("MCAP instance creation failed %s\n", err->message); + g_error_free(err); + + exit(1); + } + + mcap_enable_csp(mcap); + + switch (control_mode) { + case MODE_CONNECT: + ba2str(&dst, bdastr); + printf("Connecting to %s\n", bdastr); + + mcap_create_mcl(mcap, &dst, ccpsm, create_mcl_cb, NULL, NULL, + &err); + + if (err) { + printf("MCAP create error %s\n", err->message); + g_error_free(err); + + exit(1); + } + + break; + case MODE_LISTEN: + printf("Listening for control channel connection\n"); + + break; + case MODE_NONE: + default: + goto done; + } + + g_main_loop_run(mloop); + +done: + printf("Done\n"); + + if (mcap) + mcap_instance_unref(mcap); + + g_main_loop_unref(mloop); + + return 0; +} diff -Nru bluez-4.101/android/pan.c bluez-5.23/android/pan.c --- bluez-4.101/android/pan.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pan.c 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,854 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "btio/btio.h" +#include "lib/bluetooth.h" +#include "lib/bnep.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/uuid-helper.h" +#include "profiles/network/bnep.h" +#include "src/log.h" + +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "utils.h" +#include "bluetooth.h" +#include "pan.h" + +#define SVC_HINT_NETWORKING 0x02 + +#define BNEP_BRIDGE "bt-pan" +#define BNEP_PANU_INTERFACE "bt-pan" +#define BNEP_NAP_INTERFACE "bt-pan%d" + +static bdaddr_t adapter_addr; +static GSList *devices = NULL; +static uint8_t local_role = HAL_PAN_ROLE_NONE; +static struct ipc *hal_ipc = NULL; + +struct pan_device { + char iface[16]; + bdaddr_t dst; + uint8_t conn_state; + uint8_t role; + GIOChannel *io; + struct bnep *session; + guint watch; +}; + +static struct { + uint32_t record_id; + GIOChannel *io; + bool bridge; +} nap_dev = { + .record_id = 0, + .io = NULL, + .bridge = false, +}; + +static int set_forward_delay(int sk) +{ + unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0 , 0, 0 }; + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, BNEP_BRIDGE, IFNAMSIZ); + ifr.ifr_data = (char *) args; + + if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) { + error("pan: setting forward delay failed: %d (%s)", + errno, strerror(errno)); + return -1; + } + + return 0; +} + +static int nap_create_bridge(void) +{ + int sk, err; + + DBG("%s", BNEP_BRIDGE); + + if (nap_dev.bridge) + return 0; + + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return -EOPNOTSUPP; + + if (ioctl(sk, SIOCBRADDBR, BNEP_BRIDGE) < 0) { + err = -errno; + if (err != -EEXIST) { + close(sk); + return -EOPNOTSUPP; + } + } + + err = set_forward_delay(sk); + if (err < 0) + ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE); + + close(sk); + + nap_dev.bridge = err == 0; + + return err; +} + +static int bridge_if_down(void) +{ + struct ifreq ifr; + int sk, err; + + sk = socket(AF_INET, SOCK_DGRAM, 0); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, BNEP_BRIDGE, IF_NAMESIZE - 1); + + ifr.ifr_flags &= ~IFF_UP; + + /* Bring down the interface */ + err = ioctl(sk, SIOCSIFFLAGS, (caddr_t) &ifr); + + close(sk); + + if (err < 0) { + error("pan: Could not bring down %s", BNEP_BRIDGE); + return err; + } + + return 0; +} + +static int nap_remove_bridge(void) +{ + int sk, err; + + DBG("%s", BNEP_BRIDGE); + + if (!nap_dev.bridge) + return 0; + + bridge_if_down(); + + sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sk < 0) + return -EOPNOTSUPP; + + err = ioctl(sk, SIOCBRDELBR, BNEP_BRIDGE); + if (err < 0) + err = -errno; + + close(sk); + + if (err < 0) + return err; + + nap_dev.bridge = false; + + return 0; +} + +static int device_cmp(gconstpointer s, gconstpointer user_data) +{ + const struct pan_device *dev = s; + const bdaddr_t *dst = user_data; + + return bacmp(&dev->dst, dst); +} + +static void pan_device_free(void *data) +{ + struct pan_device *dev = data; + + if (dev->watch > 0) { + bnep_server_delete(BNEP_BRIDGE, dev->iface, &dev->dst); + g_source_remove(dev->watch); + } + + if (dev->io) { + g_io_channel_shutdown(dev->io, FALSE, NULL); + g_io_channel_unref(dev->io); + } + + if (dev->session) + bnep_free(dev->session); + + g_free(dev); +} + +static void pan_device_remove(struct pan_device *dev) +{ + devices = g_slist_remove(devices, dev); + + if (g_slist_length(devices) == 0) { + local_role = HAL_PAN_ROLE_NONE; + nap_remove_bridge(); + } + + pan_device_free(dev); +} + +static void bt_pan_notify_conn_state(struct pan_device *dev, uint8_t state) +{ + struct hal_ev_pan_conn_state ev; + char addr[18]; + + if (dev->conn_state == state) + return; + + dev->conn_state = state; + ba2str(&dev->dst, addr); + DBG("device %s state %u", addr, state); + + bdaddr2android(&dev->dst, ev.bdaddr); + ev.state = state; + ev.local_role = local_role; + ev.remote_role = dev->role; + ev.status = HAL_STATUS_SUCCESS; + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CONN_STATE, + sizeof(ev), &ev); + if (dev->conn_state == HAL_PAN_STATE_DISCONNECTED) + pan_device_remove(dev); +} + +static void bt_pan_notify_ctrl_state(struct pan_device *dev, uint8_t state, + uint8_t status) +{ + struct hal_ev_pan_ctrl_state ev; + + DBG(""); + + ev.state = state; + ev.local_role = local_role; + ev.status = status; + + memset(ev.name, 0, sizeof(ev.name)); + + if (local_role == HAL_PAN_ROLE_NAP) + memcpy(ev.name, BNEP_BRIDGE, sizeof(BNEP_BRIDGE)); + else if (local_role == HAL_PAN_ROLE_PANU) + memcpy(ev.name, dev->iface, sizeof(dev->iface)); + + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_PAN, HAL_EV_PAN_CTRL_STATE, + sizeof(ev), &ev); +} + +static void bnep_disconn_cb(void *data) +{ + struct pan_device *dev = data; + + DBG("%s disconnected", dev->iface); + + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); +} + +static void bnep_conn_cb(char *iface, int err, void *data) +{ + struct pan_device *dev = data; + + DBG(""); + + if (err < 0) { + error("bnep connect req failed: %s", strerror(-err)); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + return; + } + + memcpy(dev->iface, iface, sizeof(dev->iface)); + + DBG("%s connected", dev->iface); + + bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED); +} + +static void connect_cb(GIOChannel *chan, GError *err, gpointer data) +{ + struct pan_device *dev = data; + uint16_t l_role, r_role; + int perr, sk; + + DBG(""); + + if (err) { + error("%s", err->message); + goto fail; + } + + l_role = (local_role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : + BNEP_SVC_PANU; + r_role = (dev->role == HAL_PAN_ROLE_NAP) ? BNEP_SVC_NAP : BNEP_SVC_PANU; + + sk = g_io_channel_unix_get_fd(dev->io); + + dev->session = bnep_new(sk, l_role, r_role, BNEP_PANU_INTERFACE); + if (!dev->session) + goto fail; + + perr = bnep_connect(dev->session, bnep_conn_cb, dev); + if (perr < 0) { + error("bnep connect req failed: %s", strerror(-perr)); + goto fail; + } + + bnep_set_disconnect(dev->session, bnep_disconn_cb, dev); + + if (dev->io) { + g_io_channel_unref(dev->io); + dev->io = NULL; + } + + return; + +fail: + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); +} + +static void bt_pan_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_pan_connect *cmd = buf; + struct pan_device *dev; + uint8_t status; + bdaddr_t dst; + char addr[18]; + GSList *l; + GError *gerr = NULL; + + DBG(""); + + switch (cmd->local_role) { + case HAL_PAN_ROLE_NAP: + if (cmd->remote_role != HAL_PAN_ROLE_PANU) { + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } + break; + case HAL_PAN_ROLE_PANU: + if (cmd->remote_role != HAL_PAN_ROLE_NAP && + cmd->remote_role != HAL_PAN_ROLE_PANU) { + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } + break; + default: + status = HAL_STATUS_UNSUPPORTED; + goto failed; + } + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = g_new0(struct pan_device, 1); + bacpy(&dev->dst, &dst); + local_role = cmd->local_role; + dev->role = cmd->remote_role; + + ba2str(&dev->dst, addr); + DBG("connecting to %s %s", addr, dev->iface); + + dev->io = bt_io_connect(connect_cb, dev, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &dev->dst, + BT_IO_OPT_PSM, BNEP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_OMTU, BNEP_MTU, + BT_IO_OPT_IMTU, BNEP_MTU, + BT_IO_OPT_INVALID); + if (!dev->io) { + error("%s", gerr->message); + g_error_free(gerr); + g_free(dev); + status = HAL_STATUS_FAILED; + goto failed; + } + + devices = g_slist_append(devices, dev); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING); + + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_CONNECT, status); +} + +static void bt_pan_disconnect(const void *buf, uint16_t len) +{ + const struct hal_cmd_pan_disconnect *cmd = buf; + struct pan_device *dev; + uint8_t status; + GSList *l; + bdaddr_t dst; + + DBG(""); + + android2bdaddr(&cmd->bdaddr, &dst); + + l = g_slist_find_custom(devices, &dst, device_cmp); + if (!l) { + status = HAL_STATUS_FAILED; + goto failed; + } + + dev = l->data; + + if (dev->conn_state == HAL_PAN_STATE_CONNECTED && dev->session) + bnep_disconnect(dev->session); + + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + status = HAL_STATUS_SUCCESS; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_DISCONNECT, + status); +} + +static gboolean nap_watchdog_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + struct pan_device *dev = user_data; + + DBG("disconnected"); + + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + + return FALSE; +} +static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond, + gpointer user_data) +{ + struct pan_device *dev = user_data; + uint8_t packet[BNEP_MTU]; + struct bnep_setup_conn_req *req = (void *) packet; + uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED; + int sk, n, err; + + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { + error("Hangup or error or inval on BNEP socket"); + return FALSE; + } + + sk = g_io_channel_unix_get_fd(chan); + + /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */ + n = read(sk, packet, sizeof(packet)); + if (n < 0) { + error("read(): %s(%d)", strerror(errno), errno); + goto failed; + } + + /* Highest known control command id BNEP_FILTER_MULT_ADDR_RSP 0x06 */ + if (req->type == BNEP_CONTROL && + req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) { + error("cmd not understood"); + bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_CMD_NOT_UNDERSTOOD, + req->ctrl); + goto failed; + } + + if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) { + error("cmd is not BNEP_SETUP_CONN_REQ %02X %02X", req->type, + req->ctrl); + goto failed; + } + + rsp = bnep_setup_decode(req, &dst_role, &src_role); + if (rsp) { + error("bnep_setup_decode failed"); + goto failed; + } + + rsp = bnep_setup_chk(dst_role, src_role); + if (rsp) { + error("benp_setup_chk failed"); + goto failed; + } + + err = nap_create_bridge(); + if (err < 0) { + error("pan: Failed to create bridge: %s (%d)", strerror(-err), + -err); + goto failed; + } + + if (bnep_server_add(sk, dst_role, BNEP_BRIDGE, dev->iface, + &dev->dst) < 0) { + nap_remove_bridge(); + error("server_connadd failed"); + rsp = BNEP_CONN_NOT_ALLOWED; + goto failed; + } + + rsp = BNEP_SUCCESS; + bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp); + + dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + nap_watchdog_cb, dev); + g_io_channel_unref(dev->io); + dev->io = NULL; + + bt_pan_notify_ctrl_state(dev, HAL_PAN_CTRL_ENABLED, HAL_STATUS_SUCCESS); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTED); + + return FALSE; + +failed: + bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp); + pan_device_remove(dev); + + return FALSE; +} + +static void nap_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) +{ + struct pan_device *dev = user_data; + + DBG(""); + + if (err) { + error("%s", err->message); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); + return; + } + + g_io_channel_set_close_on_unref(chan, TRUE); + dev->watch = g_io_add_watch(chan, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + nap_setup_cb, dev); +} + +static void nap_confirm_cb(GIOChannel *chan, gpointer data) +{ + struct pan_device *dev; + bdaddr_t dst; + char address[18]; + GError *err = NULL; + + DBG(""); + + bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_DEST, address, BT_IO_OPT_INVALID); + if (err) { + error("%s", err->message); + g_error_free(err); + return; + } + + DBG("incoming connect request from %s", address); + dev = g_new0(struct pan_device, 1); + bacpy(&dev->dst, &dst); + local_role = HAL_PAN_ROLE_NAP; + dev->role = HAL_PAN_ROLE_PANU; + + strncpy(dev->iface, BNEP_NAP_INTERFACE, 16); + dev->iface[15] = '\0'; + + dev->io = g_io_channel_ref(chan); + g_io_channel_set_close_on_unref(dev->io, TRUE); + + if (!bt_io_accept(dev->io, nap_connect_cb, dev, NULL, &err)) { + error("bt_io_accept: %s", err->message); + g_error_free(err); + goto failed; + } + + devices = g_slist_append(devices, dev); + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_CONNECTING); + + return; + +failed: + bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED); +} + +static void destroy_nap_device(void) +{ + DBG(""); + + nap_remove_bridge(); + + if (nap_dev.io) { + g_io_channel_shutdown(nap_dev.io, FALSE, NULL); + g_io_channel_unref(nap_dev.io); + nap_dev.io = NULL; + } +} + +static int register_nap_server(void) +{ + GError *gerr = NULL; + + DBG(""); + + nap_dev.io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_PSM, BNEP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_OMTU, BNEP_MTU, + BT_IO_OPT_IMTU, BNEP_MTU, + BT_IO_OPT_INVALID); + + if (!nap_dev.io) { + destroy_nap_device(); + error("%s", gerr->message); + g_error_free(gerr); + return -EINVAL; + } + + return 0; +} + +static void bt_pan_enable(const void *buf, uint16_t len) +{ + const struct hal_cmd_pan_enable *cmd = buf; + uint8_t status, state; + int err; + + DBG(""); + + if (local_role == cmd->local_role) { + status = HAL_STATUS_SUCCESS; + goto reply; + } + + /* destroy existing server */ + destroy_nap_device(); + + switch (cmd->local_role) { + case HAL_PAN_ROLE_NAP: + break; + case HAL_PAN_ROLE_NONE: + local_role = HAL_PAN_ROLE_NONE; + status = HAL_STATUS_SUCCESS; + state = HAL_PAN_CTRL_DISABLED; + goto notify; + default: + status = HAL_STATUS_UNSUPPORTED; + goto reply; + } + + local_role = cmd->local_role; + err = register_nap_server(); + if (err < 0) { + status = HAL_STATUS_FAILED; + destroy_nap_device(); + goto reply; + } + + status = HAL_STATUS_SUCCESS; + state = HAL_PAN_CTRL_ENABLED; + +notify: + bt_pan_notify_ctrl_state(NULL, state, status); + +reply: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_ENABLE, status); +} + +static void bt_pan_get_role(const void *buf, uint16_t len) +{ + struct hal_rsp_pan_get_role rsp; + + DBG(""); + + rsp.local_role = local_role; + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_PAN, HAL_OP_PAN_GET_ROLE, + sizeof(rsp), &rsp, -1); +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_PAN_ENABLE */ + { bt_pan_enable, false, sizeof(struct hal_cmd_pan_enable) }, + /* HAL_OP_PAN_GET_ROLE */ + { bt_pan_get_role, false, 0 }, + /* HAL_OP_PAN_CONNECT */ + { bt_pan_connect, false, sizeof(struct hal_cmd_pan_connect) }, + /* HAL_OP_PAN_DISCONNECT */ + { bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) }, +}; + +static sdp_record_t *pan_record(void) +{ + sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto; + uuid_t root_uuid, pan, l2cap, bnep; + sdp_profile_desc_t profile[1]; + sdp_list_t *proto[2]; + sdp_data_t *v, *p; + uint16_t psm = BNEP_PSM, version = 0x0100; + uint16_t security = 0x0001, type = 0xfffe; + uint32_t rate = 0; + const char *desc = "Network Access Point", *name = "Network Service"; + sdp_record_t *record; + uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ }; + sdp_data_t *head, *pseq, *data; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + record->attrlist = NULL; + record->pattern = NULL; + + sdp_uuid16_create(&pan, NAP_SVCLASS_ID); + svclass = sdp_list_append(NULL, &pan); + sdp_set_service_classes(record, svclass); + + sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID); + profile[0].version = 0x0100; + pfseq = sdp_list_append(NULL, &profile[0]); + sdp_set_profile_descs(record, pfseq); + sdp_set_info_attr(record, name, NULL, desc); + sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type); + sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE, + SDP_UINT32, &rate); + + sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); + root = sdp_list_append(NULL, &root_uuid); + sdp_set_browse_groups(record, root); + + sdp_uuid16_create(&l2cap, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap); + p = sdp_data_alloc(SDP_UINT16, &psm); + proto[0] = sdp_list_append(proto[0], p); + apseq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&bnep, BNEP_UUID); + proto[1] = sdp_list_append(NULL, &bnep); + v = sdp_data_alloc(SDP_UINT16, &version); + proto[1] = sdp_list_append(proto[1], v); + + head = sdp_data_alloc(SDP_UINT16, &ptype[0]); + data = sdp_data_alloc(SDP_UINT16, &ptype[1]); + sdp_seq_append(head, data); + + pseq = sdp_data_alloc(SDP_SEQ16, head); + proto[1] = sdp_list_append(proto[1], pseq); + apseq = sdp_list_append(apseq, proto[1]); + aproto = sdp_list_append(NULL, apseq); + sdp_set_access_protos(record, aproto); + sdp_add_lang_attr(record); + sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security); + + sdp_data_free(p); + sdp_data_free(v); + sdp_list_free(apseq, NULL); + sdp_list_free(root, NULL); + sdp_list_free(aproto, NULL); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + sdp_list_free(svclass, NULL); + sdp_list_free(pfseq, NULL); + + return record; +} + +bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + sdp_record_t *rec; + int err; + + DBG(""); + + bacpy(&adapter_addr, addr); + + rec = pan_record(); + if (!rec) { + error("Failed to allocate PAN record"); + return false; + } + + if (bt_adapter_add_record(rec, SVC_HINT_NETWORKING) < 0) { + error("Failed to register PAN record"); + sdp_record_free(rec); + return false; + } + + err = bnep_init(); + if (err < 0) { + error("bnep init failed"); + bt_adapter_remove_record(rec->handle); + return false; + } + + err = register_nap_server(); + if (err < 0) { + error("Failed to register NAP"); + bt_adapter_remove_record(rec->handle); + bnep_cleanup(); + return false; + } + + nap_dev.record_id = rec->handle; + + hal_ipc = ipc; + ipc_register(hal_ipc, HAL_SERVICE_ID_PAN, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); + + return true; +} + +void bt_pan_unregister(void) +{ + DBG(""); + + g_slist_free_full(devices, pan_device_free); + devices = NULL; + local_role = HAL_PAN_ROLE_NONE; + + bnep_cleanup(); + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_PAN); + hal_ipc = NULL; + + bt_adapter_remove_record(nap_dev.record_id); + nap_dev.record_id = 0; + destroy_nap_device(); +} diff -Nru bluez-4.101/android/pan.h bluez-5.23/android/pan.h --- bluez-4.101/android/pan.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pan.h 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_pan_unregister(void); diff -Nru bluez-4.101/android/pics-a2dp.txt bluez-5.23/android/pics-a2dp.txt --- bluez-4.101/android/pics-a2dp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-a2dp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,86 @@ +A2DP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory if such role selected +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_1_1 True Role: Source (C.1) +TSPC_A2DP_1_2 False (*) Role: Sink (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + A2DP SRC Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_2_1 True SRC: Initiate connection establishment (M) +TSPC_A2DP_2_2 True SRC: Accept connection establishment (M) +TSPC_A2DP_2_3 True SRC: Initiate start streaming (M) +TSPC_A2DP_2_4 True SRC: Accept start streaming (M) +TSPC_A2DP_2_5 True SRC: Send audio stream (M) +TSPC_A2DP_2_6 True SRC: Initiate connection release (M) +TSPC_A2DP_2_7 True SRC: Accept connection release (M) +TSPC_A2DP_2_8 True (*) SRC: Initiate suspend (O) +TSPC_A2DP_2_9 True (*) SRC: Accept suspend (O) +TSPC_A2DP_2_10 True SRC: SBC encoder (M) +TSPC_A2DP_2_10a False SRC: Encode Audio Stream (O) +TSPC_A2DP_2_11 False SRC: SBC Configurations in 16 KHz sampling (O) +TSPC_A2DP_2_12 False SRC: SBC Configurations in 32 KHz sampling (O) +TSPC_A2DP_2_13 True SRC: SBC Configurations in 44.1 KHz sampling + (C.1) +TSPC_A2DP_2_14 False (*) SRC: SBC Configurations in 48 KHz sampling + (C.1) +------------------------------------------------------------------------------- +C.1: At least one of the values shall be supported. +------------------------------------------------------------------------------- + + + Supported Codecs in SRC +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_3_1 True SRC: SBC encoder Codec (M) +TSPC_A2DP_3_2 True (*) SRC: Additional encoder Codec (O) +------------------------------------------------------------------------------- + + + A2DP Sink Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_4_1 False SNK: Initiate connection establishment (O) +TSPC_A2DP_4_2 False (*) SNK: Accept connection establishment (M) +TSPC_A2DP_4_3 False SNK: Initiate start streaming (O) +TSPC_A2DP_4_4 False (*) SNK: Accept start streaming (M) +TSPC_A2DP_4_5 False (*) SNK: Receive audio stream (M) +TSPC_A2DP_4_6 False SNK: Initiate connection release (O) +TSPC_A2DP_4_7 False (*) SNK: Accept connection release (M) +TSPC_A2DP_4_8 False SNK: Initiate suspend (O) +TSPC_A2DP_4_9 False SNK: Accept suspend (O) +TSPC_A2DP_4_10 False (*) SNK: SBC decoder (M) +TSPC_A2DP_4_10a False (*) SNK: Decode Audio Stream (O) +TSPC_A2DP_4_11 False SNK: SBC Configurations in 16 KHz sampling (O) +TSPC_A2DP_4_12 False SNK: SBC Configurations in 32 KHz sampling (O) +TSPC_A2DP_4_13 False (*) SNK: SBC Configurations in 44.1 KHz sampling (M) +TSPC_A2DP_4_14 False (*) SNK: SBC Configurations in 48 KHz sampling (M) +------------------------------------------------------------------------------- + + + Supported codecs in SNK +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_A2DP_5_1 False (*) SNK: SBC decoder Codec (M) +TSPC_A2DP_5_2 False SNK: Additional decoder Coded (O) +TSPC_ALL False Enable all test cases when set to False. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-avctp.txt bluez-5.23/android/pics-avctp.txt --- bluez-4.101/android/pics-avctp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-avctp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,75 @@ +AVCTP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory if such role selected +O - optional + + Protocol Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVCTP_0_1 False AVCTP 1.0 (C.1) +TSPC_AVCTP_0_2 False AVCTP 1.2 (C.1) +TSPC_AVCTP_0_3 False AVCTP 1.3 (C.1) +TSPC_AVCTP_0_4 True (*) AVCTP 1.4 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support only one Protocol Version. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVCTP_1_1 True (*) Controller (C.1) +TSPC_AVCTP_1_2 True (*) Target (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Controller Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVCTP_2_1 False Message fragmentation (O) +TSPC_AVCTP_2_2 True Transaction label management (M) +TSPC_AVCTP_2_3 True Packet type field management (M) +TSPC_AVCTP_2_4 True Message type field management (M) +TSPC_AVCTP_2_5 True PID field management (M) +TSPC_AVCTP_2_6 True IPID field mangement (M) +TSPC_AVCTP_2_7 True Message information management (M) +TSPC_AVCTP_2_8 False Event registration for message reception (O) +TSPC_AVCTP_2_9 False Event registration for connection request (O) +TSPC_AVCTP_2_10 False Event registration for disconnection (O) +TSPC_AVCTP_2_11 False Connect request (O) +TSPC_AVCTP_2_12 False Disconnect request (O) +TSPC_AVCTP_2_13 False Send message (O) +TSPC_AVCTP_2_14 False Support for multiple AVCTP channel establishment + (O) +------------------------------------------------------------------------------- + + + Target Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVCTP_3_1 False Message fragmentation (O) +TSPC_AVCTP_3_2 True Transaction label management (M) +TSPC_AVCTP_3_3 True Packet type field management (M) +TSPC_AVCTP_3_4 True Message type field management (M) +TSPC_AVCTP_3_5 True PID field management (M) +TSPC_AVCTP_3_6 True IPID field management (M) +TSPC_AVCTP_3_7 True Message information management (M) +TSPC_AVCTP_3_8 True (*) Event registration for message reception (O) +TSPC_AVCTP_3_9 True (*) Event registration for connection request (O) +TSPC_AVCTP_3_10 True (*) Event registration for disconnection request (O) +TSPC_AVCTP_3_11 True (*) Connect request (O) +TSPC_AVCTP_3_12 True (*) Disconnect request (O) +TSPC_AVCTP_3_13 True (*) Send message (O) +TSPC_AVCTP_ALL False Enables all test cases when set to TRUE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-avdtp.txt bluez-5.23/android/pics-avdtp.txt --- bluez-4.101/android/pics-avdtp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-avdtp.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,234 @@ +AVDTP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory if such role selected +O - optional + + Versions +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_0_1 False AVDTP 1.0 (C.1) +TSPC_AVDTP_0_2 False AVDTP 1.2 (C.1) +TSPC_AVDTP_0_3 True (*) AVDTP 1.3 (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to select only one of the protocol versions. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_1_1 True (*) Source (C.1) +TSPC_AVDTP_1_2 True (*) Sink (C.1) +TSPC_AVDTP_1_3 True (*) Initiator (C.2) +TSPC_AVDTP_1_4 True (*) Aceptor (C.2) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +C.2: It is within the scope of profiles using the AVDTP specification to + mandate Initiator/Acceptor capabilities. It is mandatory to support at + least one of the defined roles. +------------------------------------------------------------------------------- + + + Signaling Message Format (Initiator) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_2_1 True Transaction label (M) +TSPC_AVDTP_2_2 True Packet type (M) +TSPC_AVDTP_2_3 True Message type (M) +TSPC_AVDTP_2_4 True Signal identifier (M) +------------------------------------------------------------------------------- + + + Signaling Channel Establishment/Disconnection (Initiator) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_3_1 True (*) Establish signaling channel (O) +TSPC_AVDTP_3_2 True (*) Disconnect signaling channel (O) +------------------------------------------------------------------------------- + + + Stream Discovery and Configuration (Initiator) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_4_1 True (*) Stream discover command (O) +TSPC_AVDTP_4_2 True (*) Stream get capabilities command (C.2) +TSPC_AVDTP_4_3 True (*) Set configuration command (O) +TSPC_AVDTP_4_4 True (*) Get configuration command (O) +TSPC_AVDTP_4_5 False Reconfigure command (O) +TSPC_AVDTP_4_6 True (*) Stream get all capabilities command (C.1) +------------------------------------------------------------------------------- +C.1: It is optional to support if AVDTP 1.3 is supported, otherwise excluded. +C.2: Mandatory to support if TSPC_AVDTP_4_6 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Stream Establishment, Suspension and Release (Initiator) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_5_1 True (*) Open stream command (O) +TSPC_AVDTP_5_2 True (*) Start stream command (O) +TSPC_AVDTP_5_3 True (*) Close stream command (O) +TSPC_AVDTP_5_4 True (*) Suspend command (O) +TSPC_AVDTP_5_5 True (*) Abort stream command (O) +------------------------------------------------------------------------------- + + + Security Signaling (Initiator) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_6_1 False Content security control command (O) +------------------------------------------------------------------------------- + + + Message Fragmentation (Initiator) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_7_1 True Signaling message fragmentation (M) +------------------------------------------------------------------------------- + + + Signaling Message Format (Acceptor) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_8_1 True Transaction label (M) +TSPC_AVDTP_8_2 True Packet type (M) +TSPC_AVDTP_8_3 True Message type (M) +TSPC_AVDTP_8_4 True Signal identifier (M) +------------------------------------------------------------------------------- + + + Signaling Channel Establishment/Disconnection (Acceptor) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_9_1 True (*) Establish signaling channel (O) +TSPC_AVDTP_9_1 True (*) Disconnect signaling channel (O) +------------------------------------------------------------------------------- + + + Stream Discovery and Configuration (Acceptor) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_10_1 True (*) Stream discover response (O) +TSPC_AVDTP_10_2 True (*) Stream get capabilities response (C.2) +TSPC_AVDTP_10_3 True (*) Set configuration response (O) +TSPC_AVDTP_10_4 True (*) Get configuration response (O) +TSPC_AVDTP_10_5 False Reconfigure response (O) +TSPC_AVDTP_10_6 True (*) Stream get all capabilities response (C.1) +------------------------------------------------------------------------------- +C.1: It is optional to support if AVDTP 1.3 is supported, otherwise excluded. +C.2: It is Mandatory to support if TSPC_AVDTP_10_6 is supported, otherwise + Optional. +------------------------------------------------------------------------------- + + + Stream Establishment, Suspension and Release (Acceptor) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_11_1 True (*) Open stream response (O) +TSPC_AVDTP_11_2 True (*) Start stream response (O) +TSPC_AVDTP_11_3 True (*) Close stream response (O) +TSPC_AVDTP_11_4 True (*) Suspend response (O) +TSPC_AVDTP_11_5 True (*) Abort stream response (O) +TSPC_AVDTP_11_6 True (*) General reject message (O) +------------------------------------------------------------------------------- + + + Security Signaling (Acceptor) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_12_1 False Content security control response (O) +------------------------------------------------------------------------------- + + + Message Fragmentation (Acceptor) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_13_1 True Signaling message fragmentation (M) +------------------------------------------------------------------------------- + + + Source Capabilities +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_14_1 True Basic transport service support (M) +TSPC_AVDTP_14_2 False Reporting service support (O) +TSPC_AVDTP_14_3 False Recovery service support (O) +TSPC_AVDTP_14_4 False Multiplexing service support (O) +TSPC_AVDTP_14_5 False Robust header compression service support (O) +TSPC_AVDTP_14_6 True (*) Delay Reporting (C.1) +------------------------------------------------------------------------------- +C.1: It is optional to support if AVDTP 1.3 is supported, else excluded. +------------------------------------------------------------------------------- + + + Sink Capabilities +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_15_1 True Basic transport service support (M) +TSPC_AVDTP_15_2 False Reporting service support (O) +TSPC_AVDTP_15_3 False Recovery service support (O) +TSPC_AVDTP_15_4 False Multiplexing service support (O) +TSPC_AVDTP_15_5 False Robust header compression service support (O) +TSPC_AVDTP_15_6 True (*) Delay Reporting (C.1) +------------------------------------------------------------------------------- +C.1: It is optional to support if AVDTP 1.3 is supported, else excluded. +------------------------------------------------------------------------------- + + + Message Error Handling Capabilities +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_16_1 False Reporting Capability Error (C.1) +TSPC_AVDTP_16_2 False Reject Corrupted Messages (C.2) +TSPC_AVDTP_16_3 True (*) General Reject Response Includes Signal ID (C.3) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_AVDTP_0_2 or TSPC_AVDTP_0_3 supported, excluded + otherwise. +C.2: Optional, excluded if TSPC_AVDTP_16_3 (General Reject Response Includes + Signal ID) is supported. +C.3: Mandatory if TSPC_AVDTP_0_3 supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Upper Test Interface +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_17_1 False Upper Test Interface provided (O) +------------------------------------------------------------------------------- + + + L2CAP Capabilities +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVDTP_18_1 False Enhanced Retransmission Mode preferred for + signaling channel (O) +TSPC_AVDTP_18_2 False Streaming Mode preferred for Media Transport + channel (O) +TSPC_AVDTP_18_3 False FCS Option (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_AVDTP_18_1 is supported, otherwise Optional. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-avrcp.txt bluez-5.23/android/pics-avrcp.txt --- bluez-4.101/android/pics-avrcp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-avrcp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,626 @@ +AVRCP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory if such role selected +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +SPC_AVRCP_1_1 True Role: Controller (CT) (C.1) +TSPC_AVRCP_1_2 True Role: Target (TG) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Controller Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_2_1 False (*) CT: Initiating connection establishment (M) +TSPC_AVRCP_2_2 False (*) CT: Accepting connection establishment (M) +TSPC_AVRCP_2_3 False (*) CT: Initiating connection release (M) +TSPC_AVRCP_2_4 False (*) CT: Accepting connection release (M) +TSPC_AVRCP_2_5 False CT: Sending UNIT INFO (O) +TSPC_AVRCP_2_6 False CT: Sending SUBUNIT INFO (O) +TSPC_AVRCP_2_7 False (*) CT: Sending PASS THROUGH command category 1 + (C.1) +TSPC_AVRCP_2_8 False CT: Sending PASS THROUGH command category 2 + (C.1) +TSPC_AVRCP_2_9 False CT: Sending PASS THROUGH command category 3 + (C.1) +TSPC_AVRCP_2_10 False CT: Sending PASS THROUGH command category 4 + (C.1) +TSPC_AVRCP_2_11 False CT: Get Capabilities (O) +TSPC_AVRCP_2_12 False CT: List Player Application Setting + Attributes (C.9) +TSPC_AVRCP_2_13 False CT: List Player Application Setting Values (O) +TSPC_AVRCP_2_14 False CT: Get Current Player Application Setting + (C.10) +TSPC_AVRCP_2_15 False CT: Set Player Application Setting Value (C.10) +TSPC_AVRCP_2_16 False CT: Get Player Application Setting + Attribute (O) +TSPC_AVRCP_2_17 False CT: Get Player Application Setting Value (O) +TSPC_AVRCP_2_18 False CT: Inform Displayable Character Set (O) +TSPC_AVRCP_2_19 False CT: Inform Battery Status of CT (O) +TSPC_AVRCP_2_20 False CT: Get Element Attributes (O) +TSPC_AVRCP_2_21 False CT: Get Play Status (O) +TSPC_AVRCP_2_22 False CT: Register Notification (C.11) +TSPC_AVRCP_2_23 False CT: Request Continuing Response (C.2) +TSPC_AVRCP_2_24 False CT: Abort Continuing Response (C.2) +TSPC_AVRCP_2_25 False CT: Next Group (C.12) +TSPC_AVRCP_2_26 False CT: Previous Group (C.12) +TSPC_AVRCP_2_27 False CT: Media Player Selection (O) +TSPC_AVRCP_2_28 False CT: SetAddressedPlayer (O) +TSPC_AVRCP_2_29 False CT: GetFolderItems(MediaPlayerList) (C.5) +TSPC_AVRCP_2_29b False CT: GetTotalNumberOfItems(MediaPlayerList) (C.5) +TSPC_AVRCP_2_30 False CT: EVENT_AVAILABLE_PLAYERS_CHANGED (O) +TSPC_AVRCP_2_31 False CT: EVENT_ADDRESSED_PLAYER_CHANGED (O) +TSPC_AVRCP_2_32 False CT: Browsing (O) +TSPC_AVRCP_2_33 False CT: SetBrowsedPlayer (C.4) +TSPC_AVRCP_2_34 False CT: ChangePath (C.4) +TSPC_AVRCP_2_35 False CT: GetFolderItems(Filesystem) (C.4) +TSPC_AVRCP_2_35b False CT: GetTotalNumberOfItems(Filesystem) (C.4) +TSPC_AVRCP_2_36 False CT: GetItemAttributes (O) +TSPC_AVRCP_2_37 False CT: PlayItem(Filesystem) (C.4) +TSPC_AVRCP_2_38 False CT: EVENT_UIDS_CHANGED (O) +TSPC_AVRCP_2_39 False CT: Searching (O) +TSPC_AVRCP_2_40 False CT: Search (C.7) +TSPC_AVRCP_2_41 False CT: GetFolderItems(Search Results) (C.7) +TSPC_AVRCP_2_41b False CT: GetTotalNumberOfItems(Search Results) (C.7) +TSPC_AVRCP_2_42 False CT: PlayItem(SearchResultList) (C.7) +TSPC_AVRCP_2_43 False CT: NowPlaying (C.8) +TSPC_AVRCP_2_44 False CT: GetFolderItems(NowPlayingList) (C.8) +TSPC_AVRCP_2_44b False CT: GetTotalNumberOfItems(NowPlayingList) (C.8) +TSPC_AVRCP_2_45 False CT: PlayItem(NowPlayingList) (C.8) +TSPC_AVRCP_2_46 False CT: AddToNowPlaying (O) +TSPC_AVRCP_2_47 False CT: EVENT_NOW_PLAYING_CONTENT_CHANGED (O) +TSPC_AVRCP_2_48 False CT: Playable Folders (O) +TSPC_AVRCP_2_49 True (*) CT: Absolute Volume (C.3) +TSPC_AVRCP_2_50 True (*) CT: SetAbsoluteVolume (C.3) +TSPC_AVRCP_2_51 True (*) CT: NotifyVolumeChange (C.3) +TSPC_AVRCP_2_52 False (*) CT: Discoverable Mode (M) +TSPC_AVRCP_2_53 False CT: PASSTHROUGH operation supporting press + and hold (O) +TSPC_AVRCP_2_54 False CT: Cover Art (O) +TSPC_AVRCP_2_55 False CT: GetCapabilities, Cover Art (C.10) +TSPC_AVRCP_2_56 False CT: GetImageProperties, Cover Art (C.10) +TSPC_AVRCP_2_57 False CT: GetImage, Cover Art (C.9) +TSPC_AVRCP_2_58 False CT: GetLinkedThumbnail, CoverArt (C.9) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined categories + (TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10). +C.2: Mandatory if TSPC_AVRCP_2_20 is supported, otherwise Optional. +C.3: Mandatory if TSPC_AVRCP_2_8 is supported, otherwise Excluded. +C.4: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded. +C.5: Mandatory if TSPC_AVRCP_2_27 is supported, otherwise Excluded. +C.7: Mandatory if item TSPC_AVRCP_2_39 is supported, Excluded otherwise. +C.8: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded. +C.9: Mandatory to support if Player Application Settings feature is supported. + If any item TSPC_AVRCP_2_13 through TSPC_AVRCP_2_15 is supported it is + required to claim support for this feature in accordance with Player + Application Settings support requirements, otherwise Optional. +C.10: Mandatory to support either Get or Set Player Application Settings + (TSPC_AVRCP_2_14 or TSPC_AVRCP_2_15) if List Player Application Setting + Attributes (TSPC_AVRCP_2_12) is supported. Either TSPC_AVRCP_2_14 + or TSPC_AVRCP_2_15 must be supported if Player Application Settings + feature is supported, in accordance with Player Application Settings + support requirements. +C.11: Mandatory if TSPC_AVRCP_2_7 or (TSPC_AVRCP_2_8 AND TSPC_AVRCP_2_49) + or TSPC_AVRCP_2_9 is supported, otherwise Optional. +C.12: Mandatory if Basic Group Navigation Feature supported. If any item + TSPC_AVRCP_2_25 or TSPC_AVRCP_2_26 is supported it is mandatory to + support both, in accordance with Basic Group Navigation support + requirements, otherwise Excluded. +------------------------------------------------------------------------------- + + + Controller Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_2b_1 False CT: AVRCP v1.0 (C.1) +TSPC_AVRCP_2b_2 False CT: AVRCP v1.3 (C.1) +TSPC_AVRCP_2b_3 False CT: AVRCP v1.4 (C.1) +TSPC_AVRCP_2b_4 False CT: AVRCP v1.5 (C.1) +TSPC_AVRCP_2b_5 False CT: AVRCP v1.6 (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the profile versions if + Controller role supported (SPC_AVRCP_1_1). +------------------------------------------------------------------------------- + + + Operation_id of Category 1 for CT +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_3_1 False CT: category 1 - Operation id: 0 (C.1) +TSPC_AVRCP_3_2 False CT: category 1 - Operation id: 1 (C.1) +TSPC_AVRCP_3_3 False CT: category 1 - Operation id: 2 (C.1) +TSPC_AVRCP_3_4 False CT: category 1 - Operation id: 3 (C.1) +TSPC_AVRCP_3_5 False CT: category 1 - Operation id: 4 (C.1) +TSPC_AVRCP_3_6 False CT: category 1 - Operation id: 5 (C.1) +TSPC_AVRCP_3_7 False CT: category 1 - Operation id: 6 (C.1) +TSPC_AVRCP_3_8 False CT: category 1 - Operation id: 7 (C.1) +TSPC_AVRCP_3_9 False CT: category 1 - Operation id: 8 (C.1) +TSPC_AVRCP_3_10 False CT: category 1 - Operation id: 9 (C.1) +TSPC_AVRCP_3_11 False CT: category 1 - Operation id: dot (C.1) +TSPC_AVRCP_3_12 False CT: category 1 - Operation id: enter (C.1) +TSPC_AVRCP_3_13 False CT: category 1 - Operation id: clear (C.1) +TSPC_AVRCP_3_14 False CT: category 1 - Operation id: sound_select + (C.1) +TSPC_AVRCP_3_15 False CT: category 1 - Operation id: input_select + (C.1) +TSPC_AVRCP_3_16 False CT: category 1 - Operation id: + display_information (C.1) +TSPC_AVRCP_3_17 False CT: category 1 - Operation id: help (C.1) +TSPC_AVRCP_3_18 False CT: category 1 - Operation id: power (C.1) +TSPC_AVRCP_3_19 False (*) CT: category 1 - Operation id: play (C.1) +TSPC_AVRCP_3_20 False (*) CT: category 1 - Operation id: stop (C.1) +TSPC_AVRCP_3_21 False (*) CT: category 1 - Operation id: pause (C.1) +TSPC_AVRCP_3_22 False CT: category 1 - Operation id: record (C.1) +TSPC_AVRCP_3_23 False CT: category 1 - Operation id: rewind (C.1) +TSPC_AVRCP_3_24 False CT: category 1 - Operation id: fast_forward + (C.1) +TSPC_AVRCP_3_25 False CT: category 1 - Operation id: eject (C.1) +TSPC_AVRCP_3_26 False CT: category 1 - Operation id: forward (C.1) +TSPC_AVRCP_3_27 False CT: category 1 - Operation id: backward (C.1) +TSPC_AVRCP_3_28 False CT: category 1 - Operation id: angle (C.1) +TSPC_AVRCP_3_29 False CT: category 1 - Operation id: subpicture (C.1) +TSPC_AVRCP_3_30 False CT: category 1 - Operation id: F1 (C.1) +TSPC_AVRCP_3_31 False CT: category 1 - Operation id: F2 (C.1) +TSPC_AVRCP_3_32 False CT: category 1 - Operation id: F3 (C.1) +TSPC_AVRCP_3_33 False CT: category 1 - Operation id: F4 (C.1) +TSPC_AVRCP_3_33a False CT: category 1 - Operation id: F5 (C.1) +TSPC_AVRCP_3_34 False CT: category 1 - Operation id: vendor_unique + (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of these operation_ids if the device + supports category 1 (TSPC_AVRCP_2_7). +------------------------------------------------------------------------------- + + + Operation_id of category 2 for CT +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_4_1 False CT: category 2 - Operation id: 0 (C.2) +TSPC_AVRCP_4_2 False CT: category 2 - Operation id: 1 (C.2) +TSPC_AVRCP_4_3 False CT: category 2 - Operation id: 2 (C.2) +TSPC_AVRCP_4_4 False CT: category 2 - Operation id: 3 (C.2) +TSPC_AVRCP_4_5 False CT: category 2 - Operation id: 4 (C.2) +TSPC_AVRCP_4_6 False CT: category 2 - Operation id: 5 (C.2) +TSPC_AVRCP_4_7 False CT: category 2 - Operation id: 6 (C.2) +TSPC_AVRCP_4_8 False CT: category 2 - Operation id: 7 (C.2) +TSPC_AVRCP_4_9 False CT: category 2 - Operation id: 8 (C.2) +TSPC_AVRCP_4_10 False CT: category 2 - Operation id: 9 (C.2) +TSPC_AVRCP_4_11 False CT: category 2 - Operation id: dot (C.2) +TSPC_AVRCP_4_12 False CT: category 2 - Operation id: enter (C.2) +TSPC_AVRCP_4_13 False CT: category 2 - Operation id: clear (C.2) +TSPC_AVRCP_4_14 False CT: category 2 - Operation id: sound_select + (C.2) +TSPC_AVRCP_4_15 False CT: category 2 - Operation id: input_select + (C.2) +TSPC_AVRCP_4_16 False CT: category 2 - Operation id: + display_information (C.2) +TSPC_AVRCP_4_17 False CT: category 2 - Operation id: help (C.2) +TSPC_AVRCP_4_18 False CT: category 2 - Operation id: power (C.2) +TSPC_AVRCP_4_19 False (*) CT: category 2 - Operation id: volume_up (C.2) +TSPC_AVRCP_4_20 False (*) CT: category 2 - Operation id: volume_down (C.2) +TSPC_AVRCP_4_21 False CT: category 2 - Operation id: mute (C.2) +TSPC_AVRCP_4_22 False CT: category 2 - Operation id: F1 (C.2) +TSPC_AVRCP_4_23 False CT: category 2 - Operation id: F2 (C.2) +TSPC_AVRCP_4_24 False CT: category 2 - Operation id: F3 (C.2) +TSPC_AVRCP_4_25 False CT: category 2 - Operation id: F4 (C.2) +TSPC_AVRCP_4_25a False CT: category 2 - Operation id: F5 (C.2) +TSPC_AVRCP_4_26 False CT: category 2 - Operation id: vendor_unique + (C.2) +------------------------------------------------------------------------------- +C.2: Mandatory to support at least one of these operation_ids if the device + supports category 2 (TSPC_AVRCP_2_8). +------------------------------------------------------------------------------- + + + Operation_id of category 3 for CT +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_5_1 False CT: category 3 - Operation id: 0 (C.3) +TSPC_AVRCP_5_2 False CT: category 3 - Operation id: 1 (C.3) +TSPC_AVRCP_5_3 False CT: category 3 - Operation id: 2 (C.3) +TSPC_AVRCP_5_4 False CT: category 3 - Operation id: 3 (C.3) +TSPC_AVRCP_5_5 False CT: category 3 - Operation id: 4 (C.3) +TSPC_AVRCP_5_6 False CT: category 3 - Operation id: 5 (C.3) +TSPC_AVRCP_5_7 False CT: category 3 - Operation id: 6 (C.3) +TSPC_AVRCP_5_8 False CT: category 3 - Operation id: 7 (C.3) +TSPC_AVRCP_5_9 False CT: category 3 - Operation id: 8 (C.3) +TSPC_AVRCP_5_10 False CT: category 3 - Operation id: 9 (C.3) +TSPC_AVRCP_5_11 False CT: category 3 - Operation id: dot (C.3) +TSPC_AVRCP_5_12 False CT: category 3 - Operation id: enter (C.3) +TSPC_AVRCP_5_13 False CT: category 3 - Operation id: clear (C.3) +TSPC_AVRCP_5_14 False CT: category 3 - Operation id: channel up (C.3) +TSPC_AVRCP_5_15 False CT: category 3 - Operation id: channel down + (C.3) +TSPC_AVRCP_5_16 False CT: category 3 - Operation id: previous channel + (C.3) +TSPC_AVRCP_5_17 False CT: category 3 - Operation id: sound_select + (C.3) +TSPC_AVRCP_5_18 False CT: category 3 - Operation id: input_select + (C.3) +TSPC_AVRCP_5_19 False CT: category 3 - Operation id: + display_information (C.3) +TSPC_AVRCP_5_20 False CT: category 3 - Operation id: help (C.3) +TSPC_AVRCP_5_21 False CT: category 3 - Operation id: power (C.3) +TSPC_AVRCP_5_22 False CT: category 3 - Operation id: angle (C.3) +TSPC_AVRCP_5_23 False CT: category 3 - Operation id: subpicture(C.3) +TSPC_AVRCP_5_24 False CT: category 3 - Operation id: F1 (C.3) +TSPC_AVRCP_5_25 False CT: category 3 - Operation id: F2 (C.3) +TSPC_AVRCP_5_26 False CT: category 3 - Operation id: F3 (C.3) +TSPC_AVRCP_5_27 False CT: category 3 - Operation id: F4 (C.3) +TSPC_AVRCP_5_27a False CT: category 3 - Operation id: F5 (C.3) +TSPC_AVRCP_5_28 False CT: category 3 - Operation id: vendor_unique + (C.3) +------------------------------------------------------------------------------- +C.3: Mandatory to support at least one of these operation_ids if the device + supports category 3 (TSPC_AVRCP_2_9). +------------------------------------------------------------------------------- + + + Operation_id of category 4 for CT +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_6_1 False CT: category 4 - Operation id: select (C.4) +TSPC_AVRCP_6_2 False CT: category 4 - Operation id: up (C.4) +TSPC_AVRCP_6_3 False CT: category 4 - Operation id: down (C.4) +TSPC_AVRCP_6_4 False CT: category 4 - Operation id: left (C.4) +TSPC_AVRCP_6_5 False CT: category 4 - Operation id: right (C.4) +TSPC_AVRCP_6_6 False CT: category 4 - Operation id: right up (C.4) +TSPC_AVRCP_6_7 False CT: category 4 - Operation id: right down (C.4) +TSPC_AVRCP_6_8 False CT: category 4 - Operation id: left up (C.4) +TSPC_AVRCP_6_9 False CT: category 4 - Operation id: left down (C.4) +TSPC_AVRCP_6_10 False CT: category 4 - Operation id: root menu (C.4) +TSPC_AVRCP_6_11 False CT: category 4 - Operation id: setup menu (C.4) +TSPC_AVRCP_6_12 False CT: category 4 - Operation id: contents menu + (C.4) +TSPC_AVRCP_6_13 False CT: category 4 - Operation id: favorite menu + (C.4) +TSPC_AVRCP_6_14 False CT: category 4 - Operation id: exit (C.4) +TSPC_AVRCP_6_15 False CT: category 4 - Operation id: 0 (C.4) +TSPC_AVRCP_6_16 False CT: category 4 - Operation id: 1 (C.4) +TSPC_AVRCP_6_17 False CT: category 4 - Operation id: 2 (C.4) +TSPC_AVRCP_6_18 False CT: category 4 - Operation id: 3 (C.4) +TSPC_AVRCP_6_19 False CT: category 4 - Operation id: 4 (C.4) +TSPC_AVRCP_6_20 False CT: category 4 - Operation id: 5 (C.4) +TSPC_AVRCP_6_21 False CT: category 4 - Operation id: 6 (C.4) +TSPC_AVRCP_6_22 False CT: category 4 - Operation id: 7 (C.4) +TSPC_AVRCP_6_23 False CT: category 4 - Operation id: 8 (C.4) +TSPC_AVRCP_6_24 False CT: category 4 - Operation id: 9 (C.4) +TSPC_AVRCP_6_25 False CT: category 4 - Operation id: dot (C.4) +TSPC_AVRCP_6_26 False CT: category 4 - Operation id: enter (C.4) +TSPC_AVRCP_6_27 False CT: category 4 - Operation id: clear (C.4) +TSPC_AVRCP_6_28 False CT: category 4 - Operation id: + display_information (C.4) +TSPC_AVRCP_6_29 False CT: category 4 - Operation id: help (C.4) +TSPC_AVRCP_6_30 False CT: category 4 - Operation id: page up (C.4) +TSPC_AVRCP_6_31 False CT: category 4 - Operation id: page down (C.4) +TSPC_AVRCP_6_32 False CT: category 4 - Operation id: power (C.4) +TSPC_AVRCP_6_33 False CT: category 4 - Operation id: F1 (C.4) +TSPC_AVRCP_6_34 False CT: category 4 - Operation id: F2 (C.4) +TSPC_AVRCP_6_35 False CT: category 4 - Operation id: F3 (C.4) +TSPC_AVRCP_6_36 False CT: category 4 - Operation id: F4 (C.4) +TSPC_AVRCP_6_36a False CT: category 4 - Operation id: F5 (C.4) +TSPC_AVRCP_6_37 False CT: category 4 - Operation id: vendor_unique + (C.4) +------------------------------------------------------------------------------- +C.4: Mandatory to support at least one of these operation_ids if the device + supports category 4 (TSPC_AVRCP_2_9). +------------------------------------------------------------------------------- + + + Target Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_7_1 True (*) TG: Initiating connection establishment (O) +TSPC_AVRCP_7_2 True TG: Accept connection establishment (M) +TSPC_AVRCP_7_3 True TG: Initiating connection release (M) +TSPC_AVRCP_7_4 True TG: Accepting connection release (M) +TSPC_AVRCP_7_5 True TG: Receiving UNIT INFO (M) +TSPC_AVRCP_7_6 True TG: Receiving SUBUNIT INFO (M) +TSPC_AVRCP_7_7 True TG: Receiving PASS THROUGH command category 1 + (C.1) +TSPC_AVRCP_7_8 True (*) TG: Receiving PASS THROUGH command category 2 + (C.1) +TSPC_AVRCP_7_9 False TG: Receiving PASS THROUGH command category 3 + (C.1) +TSPC_AVRCP_7_10 False TG: Receiving PASS THROUGH command category 4 + (C.1) +TSPC_AVRCP_7_11 True (*) TG: Get Capabilities Response (C.3) +TSPC_AVRCP_7_12 False TG: List Player Application Settings (C.14) +TSPC_AVRCP_7_13 False TG: List Player Application Setting Values + (C.14) +TSPC_AVRCP_7_14 False TG: Get Current Player Application Settings + (C.14) +TSPC_AVRCP_7_15 False TG: Set Player Application Setting Value (C.14) +TSPC_AVRCP_7_16 False TG: Get Player Application Setting Attribute + (O) +TSPC_AVRCP_7_17 False TG: Get Player Application Setting Value (O) +TSPC_AVRCP_7_18 False TG: Inform Displayable Character Set (O) +TSPC_AVRCP_7_19 False TG: Inform Battery Status Of CT Response (O) +TSPC_AVRCP_7_20 True (*) TG: Get Element Attributes Response (C.3) +TSPC_AVRCP_7_21 True (*) TG: Get Play Status Response (C.2) +TSPC_AVRCP_7_22 True (*) TG: Register Notification Response (C.12) +TSPC_AVRCP_7_23 True (*) TG: Notify Event Response: + PLAYBACK_STATUS_CHANGED (C.4) +TSPC_AVRCP_7_24 True (*) TG: Notify Event Response: TRACK_CHANGED (C.4) +TSPC_AVRCP_7_25 False TG: Notify Event Response: TRACK_REACHED_END (O) +TSPC_AVRCP_7_26 False TG: Notify Event Response: TRACK_REACHED_START + (O) +TSPC_AVRCP_7_27 False TG: Notify Event Response: PLAYBACK_POS_CHANGED + (O) +TSPC_AVRCP_7_28 False TG: Notify Event Response: BATT_STATUS_CHANGED + (O) +TSPC_AVRCP_7_29 False TG: Notify Event Response: SYSTEM_STATUS_CHANGED + (O) +TSPC_AVRCP_7_30 False TG: Notify Event Response: + PLAYER_APPLICATION_SETTING_CHANGED (O) +TSPC_AVRCP_7_31 True (*) TG: Request ContinuingResponse (C.2) +TSPC_AVRCP_7_32 True (*) TG: Abort ContinuingResponse Response (C.2) +TSPC_AVRCP_7_34 False TG: Next Group (C.15) +TSPC_AVRCP_7_35 False TG: Previous Group (C.15) +TSPC_AVRCP_7_36 False TG: Media Player Selection (C.8) +TSPC_AVRCP_7_37 False TG: SetAddressedPlayer (C.8) +TSPC_AVRCP_7_38 False TG: GetFolderItems(MediaPlayerList) (C.8) +TSPC_AVRCP_7_38b False TG: GetTotalNumberOfItems(MediaPlayerList) (C.8) +TSPC_AVRCP_7_39 False TG: EVENT_AVAILABLE_PLAYERS_CHANGED (C.8) +TSPC_AVRCP_7_40 False TG: EVENT_ADDRESSED_PLAYER_CHANGED (C.8) +TSPC_AVRCP_7_41 False TG: Supports Multiple Players (O) +TSPC_AVRCP_7_42 False TG: Browsing (O) +TSPC_AVRCP_7_42a False TG: Supports initiation of browsing channel + establishment (O) +TSPC_AVRCP_7_43 False TG: SetBrowsedPlayer (C.6) +TSPC_AVRCP_7_44 False TG: ChangePath (C.6) +TSPC_AVRCP_7_45 False TG: GetFolderItems(Filesystem) (C.6) +TSPC_AVRCP_7_45b False TG: GetTotalNumberOfItems(Filesystem) (C.6) +TSPC_AVRCP_7_46 False TG: GetItemAttributes (C.6) +TSPC_AVRCP_7_47 False TG: PlayItem(Filesystem) (C.6) +TSPC_AVRCP_7_48 False TG: EVENT_UIDS_CHANGED (C.9) +TSPC_AVRCP_7_49 False TG: Database Aware Players (O) +TSPC_AVRCP_7_50 False TG: Searching (O) +TSPC_AVRCP_7_51 False TG: Search (C.10) +TSPC_AVRCP_7_52 False TG: GetFolderItems(Search Results) (C.10) +TSPC_AVRCP_7_52b False TG: GetTotalNumberOfItems(Search Results) (C.10) +TSPC_AVRCP_7_53 False TG: PlayItem(SearchResultList) (C.10) +TSPC_AVRCP_7_54 False TG: NowPlaying (C.11) +TSPC_AVRCP_7_55 False TG: GetFolderItems(NowPlayingList) (C.11) +TSPC_AVRCP_7_55b False TG: GetTotalNumberOfItems(NowPlayingList) (C.11) +TSPC_AVRCP_7_56 False TG: PlayItem(NowPlayingList) (C.11) +TSPC_AVRCP_7_57 False TG: AddToNowPlaying (O) +TSPC_AVRCP_7_58 False TG: EVENT_NOW_PLAYING_CONTENT_CHANGED (C.11) +TSPC_AVRCP_7_59 False TG: Playable Folders (O) +TSPC_AVRCP_7_60 False TG: Absolute Volume (C.5) +TSPC_AVRCP_7_61 False TG: SetAbsoluteVolume (C.5) +TSPC_AVRCP_7_62 False TG: NotifyVolumeChange (C.5) +TSPC_AVRCP_7_63 False TG: Error Response (O) +TSPC_AVRCP_7_64 False TG: General Reject (C.13) +TSPC_AVRCP_7_65 True TG: Discoverable Mode (M) +TSPC_AVRCP_7_66 False TG: PASSTHROUGH operation supporting press + and hold (O) +TSPC_AVRCP_7_67 False TG: Cover Art (O) +TSPC_AVRCP_7_68 False TG: GetCapabilities, Cover Art (C.12) +TSPC_AVRCP_7_69 False TG: GetImageProperties, Cover Art (C.12) +TSPC_AVRCP_7_70 False TG: GetImage, Cover Art (C.12) +TSPC_AVRCP_7_71 False TG: GetLinkedThumbnail, Cover Art (C.12) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the categories. Supported + operation_id's are shown in Table 8 to Table 11. +C.2: Mandatory if 7/20 is supported, otherwise Optional. +C.3: Mandatory if 7/7 is supported, otherwise Optional. +C.4: Mandatory if 7/22 and 7/20 is supported, otherwise Optional. +C.5: Mandatory if 7/8 is supported, otherwise Excluded. +C.6: Mandatory if 7/42 is supported, otherwise Excluded. +C.7: Mandatory if 7/36 is supported, otherwise Excluded. +C.8: Mandatory if (7/7 or 7/9) is supported, otherwise Excluded. +C.9: Mandatory if 7/49 is supported, otherwise Optional. +C.10: Mandatory if 7/50 is supported, otherwise Excluded. +C.11: Mandatory if 7/42 is supported, otherwise Optional. +C.12: Mandatory if 7/7 or (7/8 AND 7/60) or 7/9 is supported, otherwise Optional +C.13: Mandatory if 7/7 or 7/9 or 7/42 is supported, otherwise Optional. +C.14: Mandatory if Player Application Settings Feature supported. If any item + 7/12 – 7/15 is supported, all items 7/12 – 7/15 shall be supported, + in accordance with Player Application Settings Feature support + requirements, otherwise Excluded. +C.15: Mandatory if Basic Group Navigation Feature supported. If any item + 7/34 or 7/35 is supported it is mandatory to support both, + in accordance with Basic Group Navigation support requirements, + otherwise Excluded. +------------------------------------------------------------------------------- + + Target Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_7b_1 False TG: AVRCP v1.0 (C.1) +TSPC_AVRCP_7b_2 False TG: AVRCP v1.3 (C.1) +TSPC_AVRCP_7b_3 False TG: AVRCP v1.4 (C.1) +TSPC_AVRCP_7b_4 True (*) TG: AVRCP v1.5 (C.1) +TSPC_AVRCP_7b_5 False TG: AVRCP v1.6 (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the profile versions. +------------------------------------------------------------------------------- + + + operation_id of category 1 for TG +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_8_1 False TG: category 1 - Operation id: 0 (O) +TSPC_AVRCP_8_2 False TG: category 1 - Operation id: 1 (O) +TSPC_AVRCP_8_3 False TG: category 1 - Operation id: 2 (O) +TSPC_AVRCP_8_4 False TG: category 1 - Operation id: 3 (O) +TSPC_AVRCP_8_5 False TG: category 1 - Operation id: 4 (O) +TSPC_AVRCP_8_6 False TG: category 1 - Operation id: 5 (O) +TSPC_AVRCP_8_7 False TG: category 1 - Operation id: 6 (O) +TSPC_AVRCP_8_8 False TG: category 1 - Operation id: 7 (O) +TSPC_AVRCP_8_9 False TG: category 1 - Operation id: 8 (O) +TSPC_AVRCP_8_10 False TG: category 1 - Operation id: 9 (O) +TSPC_AVRCP_8_11 False TG: category 1 - Operation id: dot (O) +TSPC_AVRCP_8_12 False TG: category 1 - Operation id: enter (O) +TSPC_AVRCP_8_13 False TG: category 1 - Operation id: clear (O) +TSPC_AVRCP_8_14 False TG: category 1 - Operation id: sound select (O) +TSPC_AVRCP_8_15 False TG: category 1 - Operation id: input select (O) +TSPC_AVRCP_8_16 False TG: category 1 - Operation id: display + information (O) +TSPC_AVRCP_8_17 False TG: category 1 - Operation id: help (O) +TSPC_AVRCP_8_18 False TG: category 1 - Operation id: power (O) +TSPC_AVRCP_8_19 True TG: category 1 - Operation id: play (M) +TSPC_AVRCP_8_20 True TG: category 1 - Operation id: stop (M) +TSPC_AVRCP_8_21 True TG: category 1 - Operation id: pause (O) +TSPC_AVRCP_8_22 False TG: category 1 - Operation id: record (O) +TSPC_AVRCP_8_23 True (*) TG: category 1 - Operation id: rewind (O) +TSPC_AVRCP_8_24 True (*) TG: category 1 - Operation id: fast forward (O) +TSPC_AVRCP_8_25 False TG: category 1 - Operation id: eject (O) +TSPC_AVRCP_8_26 True (*) TG: category 1 - Operation id: forward (O) +TSPC_AVRCP_8_27 True (*) TG: category 1 - Operation id: backward (O) +TSPC_AVRCP_8_28 False TG: category 1 - Operation id: angle (O) +TSPC_AVRCP_8_29 False TG: category 1 - Operation id: subpicture (O) +TSPC_AVRCP_8_30 False TG: category 1 - Operation id: F1 (O) +TSPC_AVRCP_8_31 False TG: category 1 - Operation id: F2 (O) +TSPC_AVRCP_8_32 False TG: category 1 - Operation id: F3 (O) +TSPC_AVRCP_8_33 False TG: category 1 - Operation id: F4 (O) +TSPC_AVRCP_8_33a False TG: category 1 - Operation id: F5 (O) +TSPC_AVRCP_8_34 False TG: category 1 - Operation id: vendor unique (O) +------------------------------------------------------------------------------- + + + operation_id of category 2 for TG +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_9_1 False TG: category 2 - Operation id: 0 (O) +TSPC_AVRCP_9_2 False TG: category 2 - Operation id: 1 (O) +TSPC_AVRCP_9_3 False TG: category 2 - Operation id: 2 (O) +TSPC_AVRCP_9_4 False TG: category 2 - Operation id: 3 (O) +TSPC_AVRCP_9_5 False TG: category 2 - Operation id: 4 (O) +TSPC_AVRCP_9_6 False TG: category 2 - Operation id: 5 (O) +TSPC_AVRCP_9_7 False TG: category 2 - Operation id: 6 (O) +TSPC_AVRCP_9_8 False TG: category 2 - Operation id: 7 (O) +TSPC_AVRCP_9_9 False TG: category 2 - Operation id: 8 (O) +TSPC_AVRCP_9_10 False TG: category 2 - Operation id: 9 (O) +TSPC_AVRCP_9_11 False TG: category 2 - Operation id: dot (O) +TSPC_AVRCP_9_12 False TG: category 2 - Operation id: enter (O) +TSPC_AVRCP_9_13 False TG: category 2 - Operation id: clear (O) +TSPC_AVRCP_9_14 False TG: category 2 - Operation id: sound select (O) +TSPC_AVRCP_9_15 False TG: category 2 - Operation id: input select (O) +TSPC_AVRCP_9_16 False TG: category 2 - Operation id: display + information (O) +TSPC_AVRCP_9_17 False TG: category 2 - Operation id: help (O) +TSPC_AVRCP_9_18 False TG: category 2 - Operation id: power (O) +TSPC_AVRCP_9_19 True TG: category 2 - Operation id: volume up (C.2) +TSPC_AVRCP_9_20 True TG: category 2 - Operation id: volume down (C.2) +TSPC_AVRCP_9_21 False TG: category 2 - Operation id: mute (O) +TSPC_AVRCP_9_24 False TG: category 2 - Operation id: F1 (O) +TSPC_AVRCP_9_25 False TG: category 2 - Operation id: F2 (O) +TSPC_AVRCP_9_26 False TG: category 2 - Operation id: F3 (O) +TSPC_AVRCP_9_27 False TG: category 2 - Operation id: F4 (O) +TSPC_AVRCP_9_27a False TG: category 2 - Operation id: F5 (O) +TSPC_AVRCP_9_28 False TG: category 2 - Operation id: vendor unique (O) +------------------------------------------------------------------------------- +C.2: Mandatory to support if the device supports category 2 (TSPC_AVRCP_7_8). +------------------------------------------------------------------------------- + + + operation_id of category 3 for TG +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_10_1 False TG: category 3 - Operation id: 0 (O) +TSPC_AVRCP_10_2 False TG: category 3 - Operation id: 1 (O) +TSPC_AVRCP_10_3 False TG: category 3 - Operation id: 2 (O) +TSPC_AVRCP_10_4 False TG: category 3 - Operation id: 3 (O) +TSPC_AVRCP_10_5 False TG: category 3 - Operation id: 4 (O) +TSPC_AVRCP_10_6 False TG: category 3 - Operation id: 5 (O) +TSPC_AVRCP_10_7 False TG: category 3 - Operation id: 6 (O) +TSPC_AVRCP_10_8 False TG: category 3 - Operation id: 7 (O) +TSPC_AVRCP_10_9 False TG: category 3 - Operation id: 8 (O) +TSPC_AVRCP_10_10 False TG: category 3 - Operation id: 9 (O) +TSPC_AVRCP_10_11 False TG: category 3 - Operation id: dot (O) +TSPC_AVRCP_10_12 False TG: category 3 - Operation id: enter (O) +TSPC_AVRCP_10_13 False TG: category 3 - Operation id: clear (O) +TSPC_AVRCP_10_14 False TG: category 3 - Operation id: channel up (C.3) +TSPC_AVRCP_10_15 False TG: category 3 - Operation id: channel down + (C.3) +TSPC_AVRCP_10_16 False TG: category 3 - Operation id: previous channel + (O) +TSPC_AVRCP_10_17 False TG: category 3 - Operation id: sound select (O) +TSPC_AVRCP_10_18 False TG: category 3 - Operation id: input select (O) +TSPC_AVRCP_10_19 False TG: category 3 - Operation id: display + information (O) +TSPC_AVRCP_10_20 False TG: category 3 - Operation id: help (O) +TSPC_AVRCP_10_21 False TG: category 3 - Operation id: power (O) +TSPC_AVRCP_10_21a False TG: category 3 - Operation id: angle (O) +TSPC_AVRCP_10_21b False TG: category 3 - Operation id: subpicture (O) +TSPC_AVRCP_10_22 False TG: category 3 - Operation id: F1 (O) +TSPC_AVRCP_10_23 False TG: category 3 - Operation id: F2 (O) +TSPC_AVRCP_10_24 False TG: category 3 - Operation id: F3 (O) +TSPC_AVRCP_10_25 False TG: category 3 - Operation id: F4 (O) +TSPC_AVRCP_10_25a False TG: category 3 - Operation id: F5 (O) +TSPC_AVRCP_10_26 False TG: category 3 - Operation id: vendor unique (O) +------------------------------------------------------------------------------- +C.3: Mandatory to support if the device supports category 3 (TSPC_AVRCP_7_9). +------------------------------------------------------------------------------- + + + operation_id of category 4 for TG +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_AVRCP_11_1 False TG: category 4 - Operation id: select (C.4) +TSPC_AVRCP_11_2 False TG: category 4 - Operation id: up (C.4) +TSPC_AVRCP_11_3 False TG: category 4 - Operation id: down (C.4) +TSPC_AVRCP_11_4 False TG: category 4 - Operation id: left (C.4) +TSPC_AVRCP_11_5 False TG: category 4 - Operation id: right (C.4) +TSPC_AVRCP_11_6 False TG: category 4 - Operation id: right up (O) +TSPC_AVRCP_11_7 False TG: category 4 - Operation id: right down (O) +TSPC_AVRCP_11_8 False TG: category 4 - Operation id: left up (O) +TSPC_AVRCP_11_9 False TG: category 4 - Operation id: left down (O) +TSPC_AVRCP_11_10 False TG: category 4 - Operation id: root menu (C.4) +TSPC_AVRCP_11_11 False TG: category 4 - Operation id: setup menu (O) +TSPC_AVRCP_11_12 False TG: category 4 - Operation id: contents menu (O) +TSPC_AVRCP_11_13 False TG: category 4 - Operation id: favorite menu (O) +TSPC_AVRCP_11_14 False TG: category 4 - Operation id: exit (O) +TSPC_AVRCP_11_15 False TG: category 4 - Operation id: 0 (O) +TSPC_AVRCP_11_16 False TG: category 4 - Operation id: 1 (O) +TSPC_AVRCP_11_17 False TG: category 4 - Operation id: 2 (O) +TSPC_AVRCP_11_18 False TG: category 4 - Operation id: 3 (O) +TSPC_AVRCP_11_19 False TG: category 4 - Operation id: 4 (O) +TSPC_AVRCP_11_20 False TG: category 4 - Operation id: 5 (O) +TSPC_AVRCP_11_21 False TG: category 4 - Operation id: 6 (O) +TSPC_AVRCP_11_22 False TG: category 4 - Operation id: 7 (O) +TSPC_AVRCP_11_23 False TG: category 4 - Operation id: 8 (O) +TSPC_AVRCP_11_24 False TG: category 4 - Operation id: 9 (O) +TSPC_AVRCP_11_25 False TG: category 4 - Operation id: dot (O) +TSPC_AVRCP_11_26 False TG: category 4 - Operation id: enter (O) +TSPC_AVRCP_11_27 False TG: category 4 - Operation id: clear (O) +TSPC_AVRCP_11_28 False TG: category 4 - Operation id: disply (O) +TSPC_AVRCP_11_29 False TG: category 4 - Operation id: help (O) +TSPC_AVRCP_11_30 False TG: category 4 - Operation id: page up (O) +TSPC_AVRCP_11_31 False TG: category 4 - Operation id: page down (O) +TSPC_AVRCP_11_32 False TG: category 4 - Operation id: power (O) +TSPC_AVRCP_11_33 False TG: category 4 - Operation id: F1 (O) +TSPC_AVRCP_11_34 False TG: category 4 - Operation id: F2 (O) +TSPC_AVRCP_11_35 False TG: category 4 - Operation id: F3 (O) +TSPC_AVRCP_11_36 False TG: category 4 - Operation id: F4 (O) +TSPC_AVRCP_11_36a False TG: category 4 - Operation id: F5 (O) +TSPC_AVRCP_11_37 False TG: category 4 - Operation id: vendor unique (O) +TSPC_AVRCP_ALL False Enables all test cases when set to TRUE. +------------------------------------------------------------------------------- +C.4: Mandatory to support if the device supports category 4 (TSPC_AVRCP_7_10). +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-did.txt bluez-5.23/android/pics-did.txt --- bluez-4.101/android/pics-did.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-did.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,23 @@ +DID PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + + SDP Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_DID_1_1 True Specification ID (M) +TSPC_DID_1_2 True Vendor ID (M) +TSPC_DID_1_3 True Product ID (M) +TSPC_DID_1_4 True Version (M) +TSPC_DID_1_5 True Primary Record (M) +TSPC_DID_1_6 True Vendor ID Source (M) +TSPC_ALL False Turns on all the test cases +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-dis.txt bluez-5.23/android/pics-dis.txt --- bluez-4.101/android/pics-dis.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-dis.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,59 @@ +DIS PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults + +M - mandatory +O - optional + + Transport Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_DIS_1_1 True Service supported over BR/EDR (C.1) +TSPC_DIS_1_2 True Service supported over LE (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of 1/1 or 1/2. +------------------------------------------------------------------------------- + + + Service Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_DIS_2_1 True Device Information Service (M) +TSPC_DIS_2_2 True Manufacturer Name String Characteristic (O) +TSPC_DIS_2_3 True Model Number String Characteristic (O) +TSPC_DIS_2_4 True Serial Number String Characteristic (O) +TSPC_DIS_2_5 True Hardware Revision String Characteristic (O) +TSPC_DIS_2_6 True Firmware Revision String Characteristic (O) +TSPC_DIS_2_7 True Software Revision String Characteristic (O) +TSPC_DIS_2_8 False (*) System ID Characteristic (O) +TSPC_DIS_2_9 False (*) IEEE 11073-20601 Regulatory Certification + Data List Characteristic (O) +TSPC_DIS_2_10 True SDP Interoperability (C.1) +TSPC_DIS_2_11 False (*) PnP ID (O) +------------------------------------------------------------------------------- +C.1: Mandatory if 1/1 (BR/EDR) is supported, otherwise excluded. +------------------------------------------------------------------------------- + + + GATT Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_DIS_3_1 True Generic Attribute Profile Server (M) +------------------------------------------------------------------------------- + + + SDP Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_DIS_4_1 True Support for server role (M) +TSPC_DIS_4_2 True ProtocolDescriptorList (M) +TSPC_DIS_4_3 True BrowseGroupList (M) +------------------------------------------------------------------------------- +Note: Marked as False as TSPC_DIS_1_1 is set to False +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-gap.txt bluez-5.23/android/pics-gap.txt --- bluez-4.101/android/pics-gap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-gap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,783 @@ +GAP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults + +M - mandatory +O - optional + + Device Configuration +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_0_1 False (*) BR/EDR (C.1) +TSPC_GAP_0_2 False (*) LE (C.2) +TSPC_GAP_0_3 True BR/EDR/LE (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if ('End Product' or 'Host Subsystem') and ('BR Host' or + 'BR/HS Host') are Supported ('End Product' or 'Host Subsystem' with 'BR' + or 'BR/HS Host' CC), otherwise excluded. Optional for + 'Component (Tested)' or 'Component (Non-Tested)'. +C.2: Mandatory if ('End Product' or 'Host Subsystem') and ('LE Host') are + Supported (End Product or Host Subsystem with LE Host CC), + otherwise excluded. Optional for 'Component (Tested)' or + 'Component (Non-Tested)'. +C.3: Mandatory if ('End Product' or 'Host Subsystem') and ('BR/LE Host' or + 'BR/HS/LE Host') are Supported (End Product or Host Subsystem with + BR/LE or BR/HS/LE Host CC), otherwise excluded. + Optional for 'Component (Tested)' or 'Component (Non-tested)'. +Note - Only one transport shall be supported. +------------------------------------------------------------------------------- + + + Version Configuration +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_0A_1 True Core Specification Addendum 3 (CSA3), GAP + Connection Parameters Changes, + Authentication and Lost Bond Changes, + Private Addressing Changes, Dual Mode + Addressing Changes, + Adopted 24 July 2012 (C.1) +TSPC_GAP_0A_2 True Core Specification Addendum 4 (CSA4) +TSPC_GAP_0A_3 True Core Spec version 4.1 (Core v4.1) GAP Connection + Parameters Changes, Authentication and + Lost Bond Changes, Private Addressing + Changes, Dual Mode Addressing Changes, + Adopted 03 December 2013 +------------------------------------------------------------------------------- +C.1: Mandatory if 'CSA3 Adopted 24 July 2012' is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Modes +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_1_1 True Non-discoverable mode (C.1) +TSPC_GAP_1_2 True Limited-discoverable Mode (O) +TSPC_GAP_1_3 True General-discoverable mode (O) +TSPC_GAP_1_4 True Non-connectable mode (O) +TSPC_GAP_1_5 True Connectable mode (M) +TSPC_GAP_1_6 True Non-bondable mode (O) +TSPC_GAP_1_7 True Bondable mode (C.2) +TSPC_GAP_1_8 False (*) Non-Synchronizable Mode (O) +TSPC_GAP_1_9 False (*) Synchronizable Mode (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_0_2 is supported, otherwise Optional. +C.2: Mandatory if TSPC_GAP_3_5 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Security Aspects +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_2_1 True Authentication procedure (C.1) +TSPC_GAP_2_2 True Support of LMP-Authentication (M) +TSPC_GAP_2_3 True Initiate LMP-Authentication (C.5) +TSPC_GAP_2_4 False (*) Security mode 1 (C.2) +TSPC_GAP_2_5 True Security mode 2 (O) +TSPC_GAP_2_6 False (*) Security mode 3 (C.7) +TSPC_GAP_2_7 True Security mode 4 (C.4) +TSPC_GAP_2_8 True Support of Authenticated link key (C.6) +TSPC_GAP_2_9 True Support of Unauthenticated link key (C.6) +TSPC_GAP_2_10 True No security (C.6) +TSPC_GAP_2_11 False (*) Secure Connections Only Mode (O) +------------------------------------------------------------------------------- +C.1: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6) is supported, otherwise + Optional. +Note 1: The Authentication Procedure in item GAP, TSPC_GAP_2_1 is the one + described in Fig. 5.1 on page 198 in the GAP Profile Specification and + not the LMP-Authenticaion. +C.2: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional. +C.5: Mandatory If (TSPC_GAP_2_5 or TSPC_GAP_2_6 or TSPC_GAP_2_7) is supported, + otherwise Optional. +C.4: Mandatory if (Core Spec 2.1 or later) is supported, otherwise Excluded. +Note 2. If a Core 2.0 and earlier design claims to support secure communcation + it should support either Security mode 2 or 3. +Note 3. A Core 2.1 or later device shall always support secure communication + in Security Mode 4, and shall use that mode to connect with another + Core 2.1 or later device. It shall use Security Mode 2 only for + backward compatibility purposes with Core 2.0 and earlier devices. + Security Mode 1 is excluded for Core 2.1 or later devices based on + condition C.2. +C.6: If TSPC_GAP_2_7 is supported then at least one of (TSPC_GAP_2_8 or + TSPC_GAP_2_9 or TSPC_GAP_2_10) is Mandatory, otherwise Excluded. +C.7: Excluded if TSPC_GAP_2_7 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Idle Mode Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_3_1 True Initiation of general inquiry (C.1) +TSPC_GAP_3_2 True Initiation of limited inquiry (C.1) +TSPC_GAP_3_3 True Initiation of name discover (O) +TSPC_GAP_3_4 True Initiation of device discovery (O) +TSPC_GAP_3_5 True Initiation of general bonding (O) +TSPC_GAP_3_6 True Initiation of dedicated bonding (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of TSPC_GAP_3_1 or TSPC_GAP_3_2 if + TSPC_GAP_3_5 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Establishment Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_4_1 True Support link establishment as initiator (M) +TSPC_GAP_4_2 True Support link establishment as acceptor (M) +TSPC_GAP_4_3 True Support channel establishment as initiator (O) +TSPC_GAP_4_4 True Support channel establishment as acceptor (M) +TSPC_GAP_4_5 True Support connection establishment as initiator + (O) +TSPC_GAP_4_6 True Support connection establishment as acceptor + (O) +TSPC_GAP_4_7 True Support synchronization establishment + as receiver (O) +------------------------------------------------------------------------------- + + + LE Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_5_1 False (*) Broadcaster (C.1) +TSPC_GAP_5_2 False (*) Observer (C.1) +TSPC_GAP_5_3 True Peripheral (C.1) +TSPC_GAP_5_4 True Central (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +Note: 'LE Roles' is applicable for LE-only configurations, but it appears that + PTS is checking this precondition also in some BR/EDR/LE tests. +------------------------------------------------------------------------------- + + + Broadcaster Physical Layer +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_6_1 False (*) Broadcaster: Transmitter (M) +TSPC_GAP_6_2 False (*) Broadcaster: Receiver (O) +------------------------------------------------------------------------------- + + + Broadcaster Link Layer States +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_7_1 False (*) Broadcaster: Standby (M) +TSPC_GAP_7_2 False (*) Broadcaster: Advertising (M) +------------------------------------------------------------------------------- + + + Broadcaster Link Layer Advertising Event Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_8_1 False (*) Broadcaster: Non-Connectable Undirected Event + (M) +TSPC_GAP_8_2 False (*) Broadcaster: Scannable Undirected Event (O) +------------------------------------------------------------------------------- + + + Broadcaster Link Layer Advertising Data Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_8A_1 False (*) AD Type-Service UUID (O) +TSPC_GAP_8A_2 False (*) AD Type-Local Name (O) +TSPC_GAP_8A_3 False (*) AD Type-Flags (O) +TSPC_GAP_8A_4 False (*) AD Type-Manufacturer Specific Data (O) +TSPC_GAP_8A_5 False (*) AD Type-TX Power Level (O) +TSPC_GAP_8A_6 False (*) AD Type-Security Manager Out of Band (OOB) (C.1) +TSPC_GAP_8A_7 False (*) AD Type-Security manager TK Value (O) +TSPC_GAP_8A_8 False (*) AD Type-Slave Connection Interval Range (O) +TSPC_GAP_8A_9 False (*) AD Type-Service Solicitation (O) +TSPC_GAP_8A_10 False (*) AD Type-Service Data (O) +TSPC_GAP_8A_11 False (*) AD Type-Appearance (O) +TSPC_GAP_8A_12 False (*) AD Type-Public Target Address (O) +TSPC_GAP_8A_13 False (*) AD Type-Random Target Address (O) +TSPC_GAP_8A_14 False (*) AD Type-Advertising Interval (O) +TSPC_GAP_8A_15 False (*) AD Type-LE Bluetooth Device Address (O) +TSPC_GAP_8A_16 False (*) AD Type –LE Role (O) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_SM_2_4 (OOB supported) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Broadcaster Connection Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_9_1 False (*) Broadcaster: Non-Connectable Mode +------------------------------------------------------------------------------- + + + Broadcaster Broadcasting and Observing Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_10_1 False (*) Broadcaster: Broadcast Mode +TSPC_GAP_11_1 False (*) Broadcaster: Privacy Feature +TSPC_GAP_11_1A False (*) Broadcaster: Privacy Feature v1.1 (O) +TSPC_GAP_11_2 False (*) Broadcaster: Resolvable Private Address + Generation Procedure +TSPC_GAP_11_3 False (*) Broadcaster: Non-Resolvable Private Address + Generation Procedure (O) +------------------------------------------------------------------------------- + + + Observer Physical Layer +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_12_1 False (*) Observer: Receiver +TSPC_GAP_12_2 False (*) Observer: Transmitter +------------------------------------------------------------------------------- + + + Observer Link Layer States +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_13_1 False (*) Observer: Standby +TSPC_GAP_13_2 False (*) Observer: Scanning +------------------------------------------------------------------------------- + + + Observer Link Layer Scanning Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_14_1 False (*) Observer: Passive Scanning +TSPC_GAP_14_2 False (*) Observer: Active Scanning +------------------------------------------------------------------------------- + + + Observer Connection Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_15_1 False (*) Observer: Non-Connectable Mode +------------------------------------------------------------------------------- + + + Observer Broadcasting and Observing Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_16_1 False (*) Observer: Observation Procedure +------------------------------------------------------------------------------- + + + Observer Privacy Feature +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_17_1 False (*) Observer: Privacy Feature (O) +TSPC_GAP_17_1A False (*) Observer: Privacy Feature v1.1 (O) +TSPC_GAP_17_2 False (*) Observer: Non-Resolvable Private Address + Generation Procedure (C.1) +TSPC_GAP_17_3 False (*) Observer: Resolvable Private Address Resolution + Procedure (C.2) +TSPC_GAP_17_4 False (*) Observer: Resolvable Private Address Generation + Procedure (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_17_1 and TSPC_GAP_14_2 (Active Scanning) are + supported and TSPC_GAP_17_4 (Resolvable Private Address Generation + Procedure) is Not Supported; Optional if CSA3 or later and + TSPC_GAP_17_4 are supported, otherwise Excluded. +C.2: Optional if TSPC_GAP_17_1 is supported, otherwise Excluded. +C.3: Mandatory if CSA3 or later and TSPC_GAP_17_1 and TSPC_GAP_14_2 + (Active Scanning) are supported and TSPC_GAP_17_2 (Non-Resolvable + Private Address Generation Procedure) is not supported; Optional if + CSA3 or later and TSPC_GAP_17_2 (Non-Resolvable Private Address + Generation Procedure) are supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral Physical Layer +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_18_1 True Peripheral: Transmitter +TSPC_GAP_18_2 True Peripheral: Receiver +------------------------------------------------------------------------------- + + + Peripheral Link Layer States +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_19_1 True Peripheral: Standby +TSPC_GAP_19_2 True Peripheral: Advertising +TSPC_GAP_19_3 True Peripheral: Connection, Slave Role +------------------------------------------------------------------------------- + + + Peripheral Link Layer Advertising Event Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_20_1 True Peripheral: Connectable Undirected Event (C.1) +TSPC_GAP_20_2 True Peripheral: Connectable Directed Event (C.2) +TSPC_GAP_20_2A True Peripheral: Low Duty Directed Advertising (C.3) +TSPC_GAP_20_3 True Peripheral: Non-Connectable Undirected Event +TSPC_GAP_20_4 True Peripheral: Scannable Undirected Event +------------------------------------------------------------------------------- + + + Peripheral Link Layer Advertising Data Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_20A_1 False (*) AD Type-Service UUID (C.1) +TSPC_GAP_20A_2 True AD Type-Local Name (C.1) +TSPC_GAP_20A_3 True AD Type-Flags (C.2) +TSPC_GAP_20A_4 False (*) AD Type-Manufacturer Specific Data (C.1) +TSPC_GAP_20A_5 True AD Type-TX Power Level (C.1) +TSPC_GAP_20A_6 False (*) AD Type-Security Manager Out of Band (OOB) (C.3) +TSPC_GAP_20A_7 False (*) AD Type-Security manager TK Value (C.1) +TSPC_GAP_20A_8 False (*) AD Type-Slave Connection Interval Range (C.1) +TSPC_GAP_20A_9 False (*) AD Type-Service Solicitation (C.1) +TSPC_GAP_20A_10 False (*) AD Type-Service Data (C.1) +TSPC_GAP_20A_11 False (*) AD Type –Appearance (C.1) +TSPC_GAP_20A_12 False (*) AD Type-Public Target Address (C.1) +TSPC_GAP_20A_13 False (*) AD Type-Random Target Address (C.1) +TSPC_GAP_20A_14 False (*) AD Type-Advertising Interval (C.1) +TSPC_GAP_20A_15 False (*) AD Type-LE Bluetooth Device Address (C.1) +TSPC_GAP_20A_16 False (*) AD Type – LE Role (C.1) +------------------------------------------------------------------------------- +C.1: Optional if (TSPC_GAP_20_1 or TSPC_GAP_20_3 or TSPC_GAP_20_4) is + supported, otherwise Excluded. +C.2: Mandatory if TSPC_GAP_22_2 (Limited Discoverable Mode) or TSPC_GAP_22_3 + (General Discoverable Mode) is supported, otherwise Optional. +C.3: Optional if (TSPC_GAP_20_1 (Connectable Undirected Event) or TSPC_GAP_20_3 + (Non-Connectable Undirected Event) or TSPC_GAP_20_4 + (Scannable Undirected Event)) and TSPC_SM_2_4 (OOB supported) are + supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral Link Layer Control Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_21_1 True Peripheral: Connection Update Procedure (M) +TSPC_GAP_21_2 True Peripheral: Channel Map Update Procedure (M) +TSPC_GAP_21_3 True Peripheral: Encryption Procedure (O) +TSPC_GAP_21_4 True Peripheral: Feature Exchange Procedure (M) +TSPC_GAP_21_5 True Peripheral: Version Exchange Procedure (M) +TSPC_GAP_21_6 True Peripheral: Termination Procedure (M) +TSPC_GAP_21_7 True Peripheral: LE Ping Procedure (C.3) +TSPC_GAP_21_8 True Peripheral: Slave Initiated Feature Exchange + Procedure (C.4) +TSPC_GAP_21_9 True Peripheral: Connection Parameter Request + Procedure (C.5) +------------------------------------------------------------------------------- + + + Peripheral Discovery Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_22_1 True Peripheral: Non-Discoverable Mode (C.2) +TSPC_GAP_22_2 True Peripheral: Limited Discoverable Mode (C.1) +TSPC_GAP_22_3 True Peripheral: General Discoverable Mode (C.1) +TSPC_GAP_22_4 True Peripheral: Name Discovery Procedure (C.3) +------------------------------------------------------------------------------- +C.1: Optional if (TSPC_GAP_5_3 OR TSPC_GAP_42_2), otherwise Excluded. +C.2: Mandatory if (TSPC_GAP_5_3 or TSPC_GAP_42_1) is supported, + otherwise Excluded. +C.3: Optional if TSPC_GAP_5_3 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral Connection Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_23_1 False (*) Peripheral: Non-Connectable Mode (C.1) +TSPC_GAP_23_2 True Peripheral: Directed Connectable Mode (O) +TSPC_GAP_23_3 True Peripheral: Undirected Connectable Mode (M) +TSPC_GAP_23_4 True Peripheral: Connection Parameter Update + Procedure (O) +TSPC_GAP_23_5 True Peripheral: Terminate Connection Procedure (M) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_5_3 (LE Only – Peripheral role) OR TSPC_GAP_42_3 + (BR/EDR/LE – Non-Connectable Mode) OR TSPC_GAP_42_4 + (BR/EDR/LE – Connectable Mode) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral Bonding Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_24_1 True Peripheral: Non-Bondable Mode (M) +TSPC_GAP_24_2 True Peripheral: Bondable Mode (C.1) +TSPC_GAP_24_3 True Peripheral: Bonding Procedure (C.2) +TSPC_GAP_24_4 True Peripheral: Multiple Bonds (C.3) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_GAP_5_3 (LE Only – Peripheral role) OR (TSPC_GAP_38_3 + (BR/EDR/LE – Peripheral role) AND NOT TSPC_GAP_42_6 (BR.EDR/LE - + Bondable Mode)) is supported, Mandatory if TSPC_GAP_42_6 + (BR/EDR/LE – Bondable Mode) is supported, otherwise Excluded. +C.2: Optional if TSPC_GAP_24_2 (Bondable Mode) is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + Peripheral Security Aspects Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_25_1 True Peripheral: Security Mode (O) +TSPC_GAP_25_2 True Peripheral: Security Mode 2 (O) +TSPC_GAP_25_3 True Peripheral: Authentication Procedure (C.2) +TSPC_GAP_25_4 True Peripheral: Authorization Procedure (O) +TSPC_GAP_25_5 True Peripheral: Connection Data Signing Procedure + (O) +TSPC_GAP_25_6 True Peripheral: Authenticate Signed Data Procedure + (O) +TSPC_GAP_25_7 True Peripheral: Authenticated Pairing + (LE security mode 1 level 3) (C.1) +TSPC_GAP_25_8 True Peripheral: Unauthenticated Pairing + (LE security mode 1 level 2) (C.1) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_GAP_25_1 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_GAP_0A_1 and TSPC_GAP_27_4 are supported, + otherwise Optional. +------------------------------------------------------------------------------- + + + Peripheral Privacy Feature +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_26_1 False (*) Peripheral: Privacy Feature (O) +TSPC_GAP_26_1A True Peripheral: Privacy Feature v1.1 (O) +TSPC_GAP_26_2 True Peripheral: Non-Resolvable Private Address + Generation Procedure (C.1) +TSPC_GAP_26_3 True Peripheral: Resolvable Private Address + Generation Procedure (C.2) +TSPC_GAP_26_4 True Peripheral: Resolvable Private Address + Generation Procedure (C.4) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_GAP_26_1 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral GAP Characteristics +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_27_1 True Peripheral: Device Name (M) +TSPC_GAP_27_2 True Peripheral: Appearance (M) +TSPC_GAP_27_3 False (*) Peripheral: Peripheral Privacy Flag (C.1) +TSPC_GAP_27_4 False (*) Peripheral: Reconnection Address (C.2) +TSPC_GAP_27_5 False (*) Peripheral: Peripheral Preferred Connection + Parameters (O) +TSPC_GAP_27_6 False (*) Peripheral: Writeable Device Name (O) +TSPC_GAP_27_7 False (*) Peripheral: Writeable Appearance (O) +TSPC_GAP_27_8 False (*) Peripheral: Writeable Peripheral Privacy Flag + (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_26_1 is supported, otherwise Excluded. +C.2: Optional if TSPC_GAP_26_1 and TSPC_GAP_27_3 are supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Physical Layer +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_28_1 True Central: Transmitter (M) +TSPC_GAP_28_2 True Central: Receiver (M) +------------------------------------------------------------------------------- + + + Central Link Layer States +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_29_1 True Central: Standby (M) +TSPC_GAP_29_2 True Central: Scanning (M) +TSPC_GAP_29_3 True Central: Initiating (M) +TSPC_GAP_29_4 True Central: Connection, Master Role (M) +------------------------------------------------------------------------------- + + + Central Link Layer Scanning Types +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_30_1 True Central: Passive Scanning (O) +TSPC_GAP_30_2 True Central: Active Scanning (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_38_4) is supported. + Optional if TSPC_GAP_30_1 and (TSPC_GAP_5_4 OR TSPC_GAP_38_4) + is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Link Layer Control Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_31_1 True Central: Connection Update Procedure (M) +TSPC_GAP_31_2 True Central: Channel Map Update Procedure (M) +TSPC_GAP_31_3 True Central: Encryption Procedure (O) +TSPC_GAP_31_4 True Central: Feature Exchange Procedure (M) +TSPC_GAP_31_5 True Central: Version Exchange Procedure (M) +TSPC_GAP_31_6 True Central: Termination Procedure (M) +TSPC_GAP_31_7 True Central: LE Ping Procedure (C.1) +TSPC_GAP_31_8 True Central: Slave Initiated Feature Exchange + Procedure (C.2) +TSPC_GAP_31_9 True Central: Connection Parameter Request Procedure + (C.3) +------------------------------------------------------------------------------- + + + Central Discovery Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_32_1 True Central: Limited Discovery Procedure (C.2) +TSPC_GAP_32_2 True Central: General Discovery Procedure (C.1) +TSPC_GAP_32_3 True Central: Name Discovery Procedure (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_1) is supported, else Excluded. +C.2: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_2) is supported, + otherwise Excluded. +C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_4) is supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Connection Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_33_1 True Central: Auto Connection Establishment + Procedure (C.3) +TSPC_GAP_33_2 True Central: General Connection Establishment + Procedure (C.1) +TSPC_GAP_33_3 True Central: Selective Connection Establishment + Procedure (C.3) +TSPC_GAP_33_4 True Central: Direct Connection Establishment + Procedure (C.2) +TSPC_GAP_33_5 True Central: Connection Parameter Update Procedure + (C.2) +TSPC_GAP_33_6 True Central: Terminate Connection Procedure + (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) and TSPC_GAP_36_1 is + supported, otherwise Optional. +C.2: Mandatory if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported, + otherwise Excluded. +C.3: Optional if (TSPC_GAP_5_4 or TSPC_GAP_40_5) is supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Bonding Modes and Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_34_1 True Central: Non-Bondable Mode (C.1) +TSPC_GAP_34_2 True Central: Bondable Mode (C.2) +TSPC_GAP_34_3 True Central: Bonding Procedure (O) +------------------------------------------------------------------------------- +C.1: Mandatory if (TSPC_GAP_5_4 or 39/5) is supported, otherwise Excluded. +C.2: Optional if (TSPC_GAP_5_4 or 39/6) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Security Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_35_1 True Central: Security Mode 1 (O) +TSPC_GAP_35_2 True Central: Security Mode 2 (O) +TSPC_GAP_35_3 True Central: Authentication Procedure (O) +TSPC_GAP_35_4 True Central: Authorization Procedure (O) +TSPC_GAP_35_5 True Central: Connection Data Signing Procedure (O) +TSPC_GAP_35_6 True Central: Authenticate Signed Data Procedure (O) +TSPC_GAP_35_7 True Central: Authenticated Pairing + (LE security mode 1 level 3) (C.1) +TSPC_GAP_35_8 True Central: Unauthenticated Pairing + (LE security mode 1 level 2) (C.1) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_GAP_35_1 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central Privacy Feature +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_36_1 True Central: Privacy Feature (C.3) +TSPC_GAP_36_1A True Central: Privacy Feature v1.1 (C.4) +TSPC_GAP_36_2 True Central: Non-Resolvable Private Address + Generation Procedure (C.1) +TSPC_GAP_36_3 True Central: Resolvable Private Address Resolution + Procedure (C.2) +TSPC_GAP_36_4 True Central: Write to Privacy Characteristic + (Enable/Disable Privacy) (O) +TSPC_GAP_36_5 True Central: Resolvable Private Address Generation + Procedure (C.6) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_36_1 and TSPC_GAP_30_2 are supported, + otherwise Excluded. +C.2: Mandatory if TSPC_GAP_36_1 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central GAP Characteristics +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_37_1 True Central: Device Name (M) +TSPC_GAP_37_2 True Central: Appearance (M) +------------------------------------------------------------------------------- + + + BR/EDR/LE Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_38_1 False (*) BR/EDR/LE: Broadcaster (C.1) +TSPC_GAP_38_2 False (*) BR/EDR/LE: Observer (C.1) +TSPC_GAP_38_3 True BR/EDR/LE: Peripheral (C.1) +TSPC_GAP_38_4 True BR/EDR/LE: Central (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +This table is applicable for BR/EDR/LE configurations. For LE-only +configurations, see 'LE Roles' table for role declarations. +------------------------------------------------------------------------------- + + + Central BR/EDR/LE Modes +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_39_1 True Central BR/EDR/LE: Non-Discoverable Mode (C.1) +TSPC_GAP_39_2 True Central BR/EDR/LE: Discoverable Mode (C.2) +TSPC_GAP_39_3 True Central BR/EDR/LE: Non-Connectable Mode (C.3) +TSPC_GAP_39_4 True Central BR/EDR/LE: Connectable Mode (M) +TSPC_GAP_39_5 True Central BR/EDR/LE: Non-Bondable Mode (C.4) +TSPC_GAP_39_6 True Central BR/EDR/LE: Bondable Mode (C.5) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded. +C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR, + otherwise Excluded. +C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded. +C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded. +C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded. +------------------------------------------------------------------------------- + + + Central BR/EDR/LE Idle Mode Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_40_1 True Central BR/EDR/LE: General Discovery (C.1) +TSPC_GAP_40_2 True Central BR/EDR/LE: Limited Discovery (C.2) +TSPC_GAP_40_3 True Central BR/EDR/LE: Device Type Discovery (C.3) +TSPC_GAP_40_4 True Central BR/EDR/LE: Name Discovery (C.4) +TSPC_GAP_40_5 True Central BR/EDR/LE: Link Establishment (C.5) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_3_1 is supported over BR/EDR, otherwise Excluded. +C.2: Mandatory if TSPC_GAP_3_2 is supported over BR/EDR, otherwise Excluded. +C.3: Mandatory if (TSPC_GAP_3_1 or TSPC_GAP_3_2) is supported over BR/EDR, + otherwise Excluded. +C.4: Mandatory if TSPC_GAP_3_3 is supported over BR/EDR, otherwise Excluded. +C.5: Mandatory if (TSPC_GAP_4_1 or TSPC_GAP_4_2) is supported over BR/EDR, + otherwise Excluded. +------------------------------------------------------------------------------- + + + Central BR/EDR/LE Security Aspects +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_41_1 True Central BR/EDR/LE: Security Aspects (M) +------------------------------------------------------------------------------- + + + Peripheral BR/EDR/LE Modes +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_42_1 True Peripheral BR/EDR/LE: Non-Discoverable Mode + (See Spec) +TSPC_GAP_42_2 True Peripheral BR/EDR/LE: Discoverable Mode + (See Spec) +TSPC_GAP_42_3 False (*) Peripheral BR/EDR/LE: Non-Connectable Mode + (See Spec) +TSPC_GAP_42_4 True Peripheral BR/EDR/LE: Connectable Mode (M) +TSPC_GAP_42_5 True Peripheral BR/EDR/LE: Non-Bondable Mode + (See Spec) +TSPC_GAP_42_6 True Peripheral BR/EDR/LE: Bondable Mode (See Spec) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_GAP_1_1 is supported over BR/EDR, otherwise Excluded. +C.2: Mandatory if (TSPC_GAP_1_2 or TSPC_GAP_1_3) is supported over BR/EDR, + otherwise Excluded. +C.3: Mandatory if TSPC_GAP_1_4 is supported over BR/EDR, otherwise Excluded. +C.4: Mandatory if TSPC_GAP_1_6 is supported over BR/EDR, otherwise Excluded. +C.5: Mandatory if TSPC_GAP_1_7 is supported over BR/EDR, otherwise Excluded. +------------------------------------------------------------------------------- + + + Peripheral BR/EDR/LE Security Aspects +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_43_1 True Peripheral BR/EDR/LE: Non-Discoverable Mode +------------------------------------------------------------------------------- + + + Central Simultaneous BR/EDR and LE Transports +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_44_1 True Central BR/EDR/LE: Simultaneous BR/EDR and LE + Transports – BR/EDR Slave to the same + device (O) +TSPC_GAP_44_2 True Central BR/EDR/LE: Simultaneous BR/EDR and LE + Transports – BR/EDR Master to the same + device (O) +------------------------------------------------------------------------------- + + + Peripheral Simultaneous BR/EDR and LE Transports +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_45_1 True Simultaneous BR/EDR and LE Transports – BR/EDR + Slave to the same device (C.1) +TSPC_GAP_45_2 True Simultaneous BR/EDR and LE Transports – BR/EDR + Master to the same device (C.1) +------------------------------------------------------------------------------- +C.1: Optional if ((SUM ICS 31/14 (Core Spec Version 4.1) or SUM ICS 31/15 +(Core Spec Version 4.1+HS)) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_1_1 True Master Role (Initiator) +TSPC_SM_1_2 True Slave Role (Responder) +TSPC_SM_2_4 True OOB supported (O) +TSPC_ALL False Turns on all +TSPC_GATT_1_1 True GATT Client Role +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-gatt.txt bluez-5.23/android/pics-gatt.txt --- bluez-4.101/android/pics-gatt.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-gatt.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,322 @@ +GATT PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults + +M - mandatory +O - optional + + Generic Attribute Profile Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_1_1 True Generic Attribute Profile Client (C.1) +TSPC_GATT_1_2 True Generic Attribute Profile Server (C.2) +TSPC_GATT_1A_1 True Complete GATT client (C.3) +TSPC_GATT_1A_2 True Complete GATT server (C.4) +------------------------------------------------------------------------------- +C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory + to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2 +C.2: Mandatory to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is + mandatory to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2 +C.3: Optional IF TSPC_GATT_1_1 is supported, otherwise Excluded +C.4: Optional IF TSPC_GATT_1_2 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + ATT Bearer Transport +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_2_1 True Attribute Protocol Supported over BR/EDR + (L2CAP fixed channel support) (C.1) +TSPC_GATT_2_2 True Attribute Protocol Supported over LE (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory IF (SUM ICS 12/2 OR SUM ICS 12/9) is supported, otherwise + Excluded +C.2: Mandatory IF (SUM ICS 12/7 OR SUM ICS 12/9) is supported, otherwise + Excluded +------------------------------------------------------------------------------- + + + + Generic Attribute Profile Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_3_1 True Client: Exchange MTU (C.1) +TSPC_GATT_3_2 True Client: Discover All Primary Services (C.1) +TSPC_GATT_3_3 True Client: Discover Primary Services Service + UUID (C.1) +TSPC_GATT_3_4 True Client: Find Included Services (C.1) +TSPC_GATT_3_5 True Client: Discover All characteristics of a + Service (C.1) +TSPC_GATT_3_6 True Client: Discover Characteristics by UUID (C.1) +TSPC_GATT_3_7 True Client: Discover All Characteristic Descriptors + (C.1) +TSPC_GATT_3_8 True Client: Read Characteristic Value (C.1) +TSPC_GATT_3_9 True Client: Read using Characteristic UUID (C.1) +TSPC_GATT_3_10 True Client: Read Long Characteristic Values (C.1) +TSPC_GATT_3_11 False (*) Client: Read Multiple Characteristic + Values (C.1) +TSPC_GATT_3_12 True Client: Write without Response (C.1) +TSPC_GATT_3_13 True Client: Signed Write Without Response (C.1) +TSPC_GATT_3_14 True Client: Write Characteristic Value (C.1) +TSPC_GATT_3_15 True Client: Write Long Characteristic Values (C.1) +TSPC_GATT_3_16 True Client: Characteristic Value Reliable + Writes (C.1) +TSPC_GATT_3_17 True Client: Notifications (C.1) +TSPC_GATT_3_18 True Client: Indications (M) +TSPC_GATT_3_19 True Client: Read Characteristic Descriptors (C.1) +TSPC_GATT_3_20 True Client: Read long Characteristic Descriptors + (C.1) +TSPC_GATT_3_21 True Client: Write Characteristic Descriptors (C.1) +TSPC_GATT_3_22 True Client: Write Long Characteristic Descriptors + (C.1) +TSPC_GATT_3_23 True Client: Service Changed Characteristic (M) +TSPC_GATT_3B_1 True Client: Primary Service Declaration (M) +TSPC_GATT_3B_2 True Client: Secondary Service Declaration (M) +TSPC_GATT_3B_3 True Client: Include Declaration (M) +TSPC_GATT_3B_4 True Client: Characteristic Declaration (M) +TSPC_GATT_3B_5 True Client: Characteristic Value Declaration (M) +TSPC_GATT_3B_6 True Client: Characteristic Extended Properties (M) +TSPC_GATT_3B_7 True Client: Characteristic User Description + Descriptor (M) +TSPC_GATT_3B_8 True Client: Client Characteristic Configuration + Descriptor (M) +TSPC_GATT_3B_9 True Client: Server Characteristic Configuration + Descriptor (M) +TSPC_GATT_3B_10 True Client: Characteristic Format Descriptor (M) +TSPC_GATT_3B_11 True Client: Characteristic Aggregate Format + Descriptor (M) +TSPC_GATT_3B_12 True Client: Characteristic Format: Boolean (M) +TSPC_GATT_3B_13 True Client: Characteristic Format: 2Bit (M) +TSPC_GATT_3B_14 True Client: Characteristic Format: nibble (M) +TSPC_GATT_3B_15 True Client: Characteristic Format: Uint8 (M) +TSPC_GATT_3B_16 True Client: Characteristic Format: Uint12 (M) +TSPC_GATT_3B_17 True Client: Characteristic Format: Uint16 (M) +TSPC_GATT_3B_18 True Client: Characteristic Format: Uint24 (M) +TSPC_GATT_3B_19 True Client: Characteristic Format: Uint32 (M) +TSPC_GATT_3B_20 True Client: Characteristic Format: Uint48 (M) +TSPC_GATT_3B_21 True Client: Characteristic Format: Uint64 (M) +TSPC_GATT_3B_22 True Client: Characteristic Format: Uint128 (M) +TSPC_GATT_3B_23 True Client: Characteristic Format: Sint8 (M) +TSPC_GATT_3B_24 True Client: Characteristic Format: Sint12 (M) +TSPC_GATT_3B_25 True Client: Characteristic Format: Sint16 (M) +TSPC_GATT_3B_26 True Client: Characteristic Format: Sint24 (M) +TSPC_GATT_3B_27 True Client: Characteristic Format: Sint32 (M) +TSPC_GATT_3B_28 True Client: Characteristic Format: Sint48 (M) +TSPC_GATT_3B_29 True Client: Characteristic Format: Sint64 (M) +TSPC_GATT_3B_30 True Client: Characteristic Format: Sint128 (M) +TSPC_GATT_3B_31 True Client: Characteristic Format: Float32 (M) +TSPC_GATT_3B_32 True Client: Characteristic Format: Float64 (M) +TSPC_GATT_3B_33 True Client: Characteristic Format: SFLOAT (M) +TSPC_GATT_3B_34 True Client: Characteristic Format: FLOAT (M) +TSPC_GATT_3B_35 True Client: Characteristic Format: Duint16 (M) +TSPC_GATT_3B_36 True Client: Characteristic Format: utf8s (M) +TSPC_GATT_3B_37 True Client: Characteristic Format: utf16s (M) +TSPC_GATT_3B_38 True Client: Characteristic Format: struct (M) +------------------------------------------------------------------------------- +C.1: Mandatory IF TSPC_GATT_1A_1 is supported, otherwise Optional +------------------------------------------------------------------------------- + + + + Generic Attribute Profile Support, by Server +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_4_1 True Server: Exchange MTU (C.4) +TSPC_GATT_4_2 True Server: Discover All Primary Services (M) +TSPC_GATT_4_3 True Server: Discover Primary Services Service + UUID (M) +TSPC_GATT_4_4 True Server: Find Included Services (M) +TSPC_GATT_4_5 True Server: Discover All characteristics of + a Service (M) +TSPC_GATT_4_6 True Server: Discover Characteristics by UUID (M) +TSPC_GATT_4_7 True Server: Discover All Characteristic + Descriptors (M) +TSPC_GATT_4_8 True Server: Read Characteristic Value (M) +TSPC_GATT_4_9 True Server: Read using Characteristic UUID (M) +TSPC_GATT_4_10 True Server: Read Long Characteristic Values (C.4) +TSPC_GATT_4_11 False (*) Server: Read Multiple Characteristic + Values (C.4) +TSPC_GATT_4_12 True Server: Write without Response (C.2) +TSPC_GATT_4_13 True Server: Signed Write Without Response (C.4) +TSPC_GATT_4_14 True Server: Write Characteristic Value (C.3) +TSPC_GATT_4_15 True Server: Write Long Characteristic Values (C.4) +TSPC_GATT_4_16 True Server: Characteristic Value Reliable + Writes (C.4) +TSPC_GATT_4_17 True Server: Notifications (C.4) +TSPC_GATT_4_18 True Server: Indications (C.1) +TSPC_GATT_4_19 True Server: Read Characteristic Descriptors (C.4) +TSPC_GATT_4_20 True Server: Read long Characteristic + Descriptors (C.4) +TSPC_GATT_4_21 True Server: Write Characteristic Descriptors (C.4) +TSPC_GATT_4_22 True Server: Write Long Characteristic + Descriptors (C.4) +TSPC_GATT_4_23 True Server: Service Changed Characteristic (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory IF service definitions on the server can be added, changed, or + removed, otherwise Optional +C.2: Mandatory IF GATT TSPC_GATT_4_13 is supported, otherwise Optional +C.3: Mandatory IF GATT TSPC_GATT_4_15 is supported, otherwise Optional +C.4: Mandatory IF GATT TSPC_GATT_1A_2 is supported, otherwise Optional +------------------------------------------------------------------------------- + + + Profile Attribute Types and Characteristic Formats +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_4B_1 True Server: Primary Service Declaration (M) +TSPC_GATT_4B_2 True Server: Secondary Service Declaration (M) +TSPC_GATT_4B_3 True Server: Include Declaration (M) +TSPC_GATT_4B_4 True Server: Characteristic Declaration (M) +TSPC_GATT_4B_5 True Server: Characteristic Value Declaration (M) +TSPC_GATT_4B_6 True Server: Characteristic Extended Properties (M) +TSPC_GATT_4B_7 True Server: Characteristic User Description + Descriptor (M) +TSPC_GATT_4B_8 True Server: Client Characteristic Configuration + Descriptor (M) +TSPC_GATT_4B_9 True Server: Server Characteristic Configuration + Descriptor (M) +TSPC_GATT_4B_10 True Server: Characteristic Format Descriptor (M) +TSPC_GATT_4B_11 True Server: Characteristic Aggregate Format + Descriptor (M) +TSPC_GATT_4B_12 True Server: Characteristic Format: Boolean (M) +TSPC_GATT_4B_13 True Server: Characteristic Format: 2Bit (M) +TSPC_GATT_4B_14 True Server: Characteristic Format: nibble (M) +TSPC_GATT_4B_15 True Server: Characteristic Format: Uint8 (M) +TSPC_GATT_4B_16 True Server: Characteristic Format: Uint12 (M) +TSPC_GATT_4B_17 True Server: Characteristic Format: Uint16 (M) +TSPC_GATT_4B_18 True Server: Characteristic Format: Uint24 (M) +TSPC_GATT_4B_19 True Server: Characteristic Format: Uint32 (M) +TSPC_GATT_4B_20 True Server: Characteristic Format: Uint48 (M) +TSPC_GATT_4B_21 True Server: Characteristic Format: Uint64 (M) +TSPC_GATT_4B_22 True Server: Characteristic Format: Uint128 (M) +TSPC_GATT_4B_23 True Server: Characteristic Format: Sint8 (M) +TSPC_GATT_4B_24 True Server: Characteristic Format: Sint12 (M) +TSPC_GATT_4B_25 True Server: Characteristic Format: Sint16 (M) +TSPC_GATT_4B_26 True Server: Characteristic Format: Sint24 (M) +TSPC_GATT_4B_27 True Server: Characteristic Format: Sint32 (M) +TSPC_GATT_4B_28 True Server: Characteristic Format: Sint48 (M) +TSPC_GATT_4B_29 True Server: Characteristic Format: Sint64 (M) +TSPC_GATT_4B_30 True Server: Characteristic Format: Sint128 (M) +TSPC_GATT_4B_31 True Server: Characteristic Format: Float32 (M) +TSPC_GATT_4B_32 True Server: Characteristic Format: Float64 (M) +TSPC_GATT_4B_33 True Server: Characteristic Format: SFLOAT (M) +TSPC_GATT_4B_34 True Server: Characteristic Format: FLOAT (M) +TSPC_GATT_4B_35 True Server: Characteristic Format: Duint16 (M) +TSPC_GATT_4B_36 True Server: Characteristic Format: utf8s (M) +TSPC_GATT_4B_37 True Server: Characteristic Format: utf16s (M) +TSPC_GATT_4B_38 True Server: Characteristic Format: struct (M) +------------------------------------------------------------------------------- + + + Generic Attribute Profile Service +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_6_2 True Discover GATT Services using Service Discovery + Profile (C.1) +TSPC_GATT_6_3 True Publish SDP record for GATT services support + via BR/EDR (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory IF TSPC_GATT_1_1 is supported, otherwise Excluded +C.2: Mandatory IF TSPC_GATT_1_2 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + Attribute Protocol Transport Security +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GATT_7_1 True Security Mode 4 (C.1) +TSPC_GATT_7_2 True LE Security Mode 1 (C.2) +TSPC_GATT_7_3 True LE Security Mode 2 (C.2) +TSPC_GATT_7_4 True LE Authentication Procedure (C.2) +TSPC_GATT_7_5 True LE connection data signing procedure (C.2) +TSPC_GATT_7_6 True LE Authenticate signed data procedure (C.2) +TSPC_GATT_7_7 True LE Authorization Procedure (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory IF TSPC_GATT_2_1 is supported, otherwise Excluded +C.2: Optional IF TSPC_GATT_2_2 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + Attribute Protocol Client Messages +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ATT_3_1 True Attribute Error Response (M) +TSPC_ATT_3_2 True Exchange MTU Request (O) +TSPC_ATT_3_4 True Find Information Request (O) +TSPC_ATT_3_6 True Find by Type Value Request (O) +TSPC_ATT_3_8 True Read by Type Request (O) +TSPC_ATT_3_10 True Read Request (O) +TSPC_ATT_3_12 True Read Blob Request (O) +TSPC_ATT_3_14 False (*) Read Multiple Request (O) +TSPC_ATT_3_16 True Read by Group Type Request (O) +TSPC_ATT_3_17 True Read by Group Type Response (C.6) +TSPC_ATT_3_18 True Write Request (O) +TSPC_ATT_3_20 True Write Command (O) +TSPC_ATT_3_21 True Signed Write Command (O) +TSPC_ATT_3_22 True Prepare Write Request (O) +TSPC_ATT_3_24 True Execute Write Request (C.8) +TSPC_ATT_3_26 True Handle Value Notification (M) +------------------------------------------------------------------------------- +C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded +C.8: Mandatory IF TSPC_ATT_3_22 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + Attribute Protocol Server Messages +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ATT_4_1 True Attribute Error Response (M) +TSPC_ATT_4_3 True Exchange MTU Response (M) +TSPC_ATT_4_5 True Find Information Response (M) +TSPC_ATT_4_7 True Find by Type Value Response (M) +TSPC_ATT_4_8 True Read by Type Request (M) +TSPC_ATT_4_9 True Read by Type Response (M) +TSPC_ATT_4_11 True Read Response (M) +TSPC_ATT_4_15 False (*) Read Multiple Response (C.2) +TSPC_ATT_4_17 True Read by Group Type Response (M) +TSPC_ATT_4_19 True Write Response (C.3) +TSPC_ATT_4_20 True Write Command (O) +TSPC_ATT_4_21 True Signed Write Command (O) +TSPC_ATT_4_23 True Prepare Write Response (C.4) +TSPC_ATT_4_25 True Execute Write Response (C.5) +TSPC_ATT_4_26 True Handle Value Notification (O) +------------------------------------------------------------------------------- +C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded +C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded +C.4: Mandatory IF TSPC_ATT_4_22 is supported, otherwise Excluded +C.5: Mandatory IF TSPC_ATT_4_27 is supported, otherwise Excluded +------------------------------------------------------------------------------- + + + Attribute Protocol Transport +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ATT_5_2 True LE Security Mode 1 (C.2) +TSPC_ATT_5_4 True LE Authentication Procedure (C.2) +TSPC_ATT_5_7 True LE Authorization Procedure (C.2) +------------------------------------------------------------------------------- +C.2: Optional to support if 2/2 (Attribute Protocol Supported over LE), + otherwise Excluded +------------------------------------------------------------------------------- + + + Device Configuration +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_GAP_0_2 True LE (C.2) +------------------------------------------------------------------------------- +C.2: Mandatory IF (SUM ICS 34/2 (LE GAP) AND NOT SUM ICS 32/3 (BR/EDR GAP)) + is supported, otherwise Excluded +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-hdp.txt bluez-5.23/android/pics-hdp.txt --- bluez-4.101/android/pics-hdp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-hdp.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,307 @@ +HDP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + + Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_0_1 False HDP 1.0 (C.1) +TSPC_HDP_0_2 True HDP 1.1 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support only one Profile version. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_1_1 True Supports Source Role (C.1, C.2) +TSPC_HDP_1_2 True (*) Supports Sink Role (C.1, C.3) +------------------------------------------------------------------------------- +C.1: At least one of the defined roles is Mandatory. +C.2: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded. +C.3: Mandatory if TSPC_MCAP_1_1 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + GAP Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_2_1 True Supports General-discoverable Mode (M) +TSPC_HDP_2_2 True Supports bondable Mode (M) (C.1) +TSPC_HDP_2_3 True Supports Response to Authentication requests (M) +TSPC_HDP_2_4 True Supports Initiation of Authentication (M) (C.2) +TSPC_HDP_2_5 True Supports Acceptance of Encryption request (M) +TSPC_HDP_2_6 True Supports Initiation of Encryption (M) (C.3) +TSPC_HDP_2_7 True (*) Supports General Inquiry (C.5) (C.4) +TSPC_HDP_2_8 True Supports Acceptance of Bonding requests (M) +TSPC_HDP_2_9 True (*) Supports Initiation of Bonding (O) +TSPC_HDP_2_10 True (*) Supports Extended Inquiry Response (C.7) +TSPC_HDP_2_11 False Supports use of Health Class of Device (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. +C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional. +C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed, otherwise Optional. +C.4: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. +C.5: Mandatory if TSPC_HDP_2_9 is supported, otherwise Optional. +C.6: Mandatory if Bluetooth Core Specification 2.1 + EDR or later + (Not SUM ICS 31/4) and Table 2/1 (Supports General-discoverable Mode) + is supported, otherwise Optional if Bluetooth Core Specification 2.1 + + EDR or later (Not SUM ICS 31/4) is supported, otherwise Excluded. +C.7: Mandatory if Bluetooth Core specification 2.1 + EDR or later is supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + L2CAP Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_3_1 True Supports Reliable Control Channel (C.1) +TSPC_HDP_3_2 True Uses FCS for Control Channel (M) +TSPC_HDP_3_3 True Supports Reliable Data Channel (C.1) +TSPC_HDP_3_4 True Can send data using SAR in ERTM (C.2) +TSPC_HDP_3_5 True (*) Uses FCS for Reliable Data Channel (O) +TSPC_HDP_3_6 True (*) Supports FCS option of "No FCS" for Reliable + Data Channel (C.3) +TSPC_HDP_3_7 True Supports Streaming Data Channel (C.4) +TSPC_HDP_3_8 True (*) Can send data using SAR in SM (C.5) +TSPC_HDP_3_9 True (*) Uses FCS for Steaming Data Channel (C.6) +TSPC_HDP_3_10 True (*) Supports FCS option of "No FCS" for Streaming + (C.7) +TSPC_HDP_3_11 True Maximum number of simultaneous Data Channels + supported (DCmax) per MCL (C.8) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded. +C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded. +C.4: Optional if TSPC_L2CAP_2_13 is supported, otherwise Excluded. +C.5: Mandatory if TSPC_HDP_3_7 and TSPC_L2CAP_2_23 are supported, otherwise + Excluded. +C.6: Optional if TSPC_HDP_3_7 is supported, otherwise Excluded. +C.7: Optional if TSPC_HDP_3_7 and TSPC_L2CAP_2_14 are supported, otherwise + Excluded. +C.8: >=2 if Table TSPC_HDP_3_7 is claimed, otherwise >=1. +------------------------------------------------------------------------------- + + + SDP Attributes - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_4_1 True Supports advertisement of HDP Service Record + (C.1) (C.4) +TSPC_HDP_4_2 True Service Class ID List (C.2) +TSPC_HDP_4_3 True Protocol Descriptor List (C.2) +TSPC_HDP_4_4 True Bluetooth Profile Descriptor List (C.2) +TSPC_HDP_4_5 True (*) Additional Protocol Descriptor Lists (C.2) +TSPC_HDP_4_6 True (*) Service Name (O) +TSPC_HDP_4_7 True (*) Service Description (O) +TSPC_HDP_4_8 True (*) Provider Name (O) +TSPC_HDP_4_9 True HDP Supported Features (MDEP List) (C.3) +TSPC_HDP_4_10 True MCAP Data Exchange Specification (C.3) +TSPC_HDP_4_11 True MCAP Supported Procedures (C.3) +TSPC_HDP_4_12 True (*) Service Record State (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional. +C.3: Mandatory if TSPC_HDP_4_1 is supported, otherwise Excluded. +C.4: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported. +------------------------------------------------------------------------------- + + + Device Identification - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_5_1 True Device Identification Profile v1.3 or later + (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_4_1 is supported, otherwise Optional. +------------------------------------------------------------------------------- + + + HDP Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_6_1 True Supports Standard Op Codes (M) +TSPC_HDP_6_2 True (*) Supports Initiate creation of Control and Data + Channels (C.3) (C.7) (C.1 - MCAP Status) +TSPC_HDP_6_3 True Supports Accept creation of Control and Data + Channels (C.3) (C.8) (C.1 - MCAP Status) +TSPC_HDP_6_4 False Supports Initiate Reconnection of MDL (O) + (C.2 - MCAP Status) +TSPC_HDP_6_5 True Supports Accept Reconnection of MDL (C.4) +TSPC_HDP_6_6 False Supports Clock Synchronization Protocol (O) +TSPC_HDP_6_7 False (*) Supports Sync-Slave (C.5) +TSPC_HDP_6_8 False Supports Sync-Master (C.6) +------------------------------------------------------------------------------- +C.1: If TSPC_HDP_6_1 is supported, at least one is Mandatory, otherwise + Excluded. +C.2: Optional if TSPC_HDP_6_1 is supported, otherwise Excluded. +C.3: Mandatory to support at least one. +C.4: Mandatory if TSPC_HDP_6_3 is supported, otherwise Excluded. + +C.5: Mandatory if TSPC_HDP_6_6 is supported, otherwise Excluded. +C.6: Optional if TSPC_HDP_6_6 is supported, otherwise Excluded. +C.7: Mandatory to support SDP Client Role (SDP 1b/2) if this item is supported. +C.8: Mandatory to support SDP Server Role (SDP 1b/1) if this item is supported. +------------------------------------------------------------------------------- + + + Data Exchange Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_7_1 False Supports Initiation of Echo Test (O) +TSPC_HDP_7_2 True Supports Acceptance of Echo Test (M) +TSPC_HDP_7_3 True Supports IEEE 11073-20601 (M) +TSPC_HDP_7_4 False (*) Supports IEEE 11073-20601 Agent Role (C.1) +TSPC_HDP_7_5 True (*) Supports IEEE 11073-20601 Manager Role (C.1) +TSPC_HDP_7_6 False Supports Initiation of Association Release (O) +------------------------------------------------------------------------------- +C.1: If TSPC_HDP_7_3 is supported, at least one is Mandatory, otherwise + Excluded. +------------------------------------------------------------------------------- + + + GAP Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_8_1 True Supports General-discoverable Mode (M) +TSPC_HDP_8_2 True Supports Bondable Mode (M) (C.1) +TSPC_HDP_8_3 True Supports Response to Authentitaction requests + (M) +TSPC_HDP_8_4 True Supports Initiation of Authentication (M) (C.2) +TSPC_HDP_8_5 True Supports Acceptance of Encryption request (M) +TSPC_HDP_8_6 True Supports Initiation of Encryption (M) (C.3) +TSPC_HDP_8_7 True Supports General Inquiry (M) C.4) +TSPC_HDP_8_8 True Supports Acceptance of Bonding requests (M) +TSPC_HDP_8_9 True (*) Supports Initiation of Bonding (O) +TSPC_HDP_8_10 True (*) Supports Extended Inquiry Response (C.5) (C.6) +TSPC_HDP_8_11 False Supports use of Health Class of Device (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional. +C.2: Mandatory if Security Mode 2, 3, or 4 is supported, otherwise Optional. +C.3: Mandatory if Bluetooth version 2.1 + EDR is claimed (Not SUM ICS 31/4), + otherwise Optional. +C.4: Mandatory if TSPC_HDP_8_9 is supported, otherwise Optional. +C.5: Mandatory if Bluetooth Core Specification 2.1 + EDR or later + (Not SUM ICS 31/4) and TSPC_HDP_8_1 is supported, otherwise Optional + if Bluetooth Core Specification 2.1 + EDR or later is supported, + otherwise Excluded. +C.6: Mandatory if Bluetooth Core specification 2.1 + EDR or later + (Not SUM ICS 31/4) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + + L2CAP Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_9_1 True Supports Reliable Control Channel (C.1) +TSPC_HDP_9_2 True Uses FCS for Control Channel (M) +TSPC_HDP_9_3 True Supports Reliable Data Channel (C.1) +TSPC_HDP_9_4 True Can send data using SAR in ERTM (C.2) +TSPC_HDP_9_5 True (*) Uses FCS for Reliable Data Channel (O) +TSPC_HDP_9_6 True (*) Supports FCS option of "No FCS" for Reliable + Data Channel (C.3) +TSPC_HDP_9_7 True Supports Streaming Data Channel (C.4) +TSPC_HDP_9_8 True Can send data using SAR in SM (C.5) +TSPC_HDP_9_9 True (*) Uses FCS for Steaming Data Channel (O) +TSPC_HDP_9_10 True (*) Supports FCS option of "No FCS" for Streaming + Data Channel (C.3) +TSPC_HDP_9_11 True Maximum number of simultaneous Data Channels + supported (DCmax) per MCL (M) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_L2CAP_2_12 is supported, otherwise Excluded. +C.2: Mandatory if TSPC_L2CAP_2_22 is supported, otherwise Excluded. +C.3: Optional if TSPC_L2CAP_2_14 is supported, otherwise Excluded. +C.4: Mandatory if TSPC_L2CAP_2_13 is supported, otherwise Excluded. +C.5: Mandatory if TSPC_L2CAP_2_23 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + SDP Attributes - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_10_1 True Supports advertisement of HDP Service Record (C.1) +TSPC_HDP_10_2 True Service Class ID List (M) +TSPC_HDP_10_3 True Protocol Descriptor List (M) +TSPC_HDP_10_4 True Bluetooth Profile Descriptor List (M) +TSPC_HDP_10_5 True Additional Protocol Descriptor Lists (M) +TSPC_HDP_10_6 True (*) Service Name (O) +TSPC_HDP_10_7 True (*) Service Description (O) +TSPC_HDP_10_8 True (*) Provider Name (O) +TSPC_HDP_10_9 True HDP Supported Features (MDEP List) (M) +TSPC_HDP_10_10 True MCAP Data Exchange Specification (M) +TSPC_HDP_10_11 True MCAP Supported Procedures (M) +TSPC_HDP_10_12 True (*) Service Record State (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support 10/1 and SDP Server Role (SDP 1b/1). +------------------------------------------------------------------------------- + + + Device Identification - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_11_1 True Device Identification Profile v1.3 or later + (M) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if 1/2 is supported. +------------------------------------------------------------------------------- + + + HDP Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_12_1 True Supports Standard Op Codes (M) +TSPC_HDP_12_2 True Supports Initiate creation of Control and Data + Channels (C.1) (C.5) +TSPC_HDP_12_3 True Supports Accept creation of Control and Data + Channels (C.1) (C.6) +TSPC_HDP_12_4 False Supports Initiate Reconnection of MDL (O) (C.2) +TSPC_HDP_12_5 True Supports Accept Reconnection of MDL (M) +TSPC_HDP_12_6 False Supports Clock Synchronization Protocol (O) +TSPC_HDP_12_7 False Supports Sync-Slave (C.3) +TSPC_HDP_12_8 False Supports Sync-Master (C.6) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HDP_12_1 is supported, otherwise Excluded. +C.2: Optional if TSPC_HDP_12_1 is supported, otherwise Excluded. +C.3: Mandatory if TSPC_HDP_12_6 is supported, otherwise Excluded. +C.4: Optional if TSPC_HDP_12_6 is supported, otherwise Excluded. +C.5: Mandatory to support 12/2 and SDP Client Role (SDP 1b/2). +C.6: Mandatory to support 12/3 and SDP Server Role (SDP 1b/1). +------------------------------------------------------------------------------- + + + Data Exchange Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HDP_13_1 False Supports Initiation of Echo Test (O) +TSPC_HDP_13_2 True Supports Acceptance of Echo Test (M) +TSPC_HDP_13_3 True Supports IEEE 11073-20601 (M) +TSPC_HDP_13_4 True (*) Supports IEEE 11073-20601 Agent Role (C.1) +TSPC_HDP_13_5 False Supports IEEE 11073-20601 Manager Role (C.1) +TSPC_HDP_13_6 False Supports Initiation of Association Release (O) +------------------------------------------------------------------------------- +C.1: If TSPC_HDP_13_3 is supported, at least one is Mandatory, otherwise + Excluded. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-hfp.txt bluez-5.23/android/pics-hfp.txt --- bluez-4.101/android/pics-hfp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-hfp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,217 @@ +HFP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_0_1 False Version: Hands-Free Profile v1.5 (O.1) +TSPC_HFP_0_2 True (*) Version: Hands-Free Profile v1.6 (O.1) +------------------------------------------------------------------------------- +O.1: It is mandatory to support only one of the adopted versions. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_1_1 True (*) Role: Audio Gateway (AG) (O.1) +TSPC_HFP_1_2 False Role: Hands-Free (HF) (O.1) +------------------------------------------------------------------------------- +O.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Audio Gateway Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_2_1 True Connection management (M) +TSPC_HFP_2_1a True (*) SLC initiation during active ongoing call (O) +TSPC_HFP_2_2 True Phone Status Information (M) +TSPC_HFP_2_3 True Audio connection handling (M) +TSPC_HFP_2_3a False Audio connection establishment independent of + call processing (O) +TSPC_HFP_2_3b True (*) eSCO support in Audio Connection (C.10) +TSPC_HFP_2_3c True (*) Codec negotiation (C.7) +TSPC_HFP_2_4a False Accept an incoming voice call + (in-band ring) (C.1) +TSPC_HFP_2_4b True (*) Accept an incoming voice call + (no in-band ring) (C.1) +TSPC_HFP_2_4c False Capability to change the "in-band ring" + settings (O) +TSPC_HFP_2_5 True (*) Reject an incoming voice call (O) +TSPC_HFP_2_6 True Terminate a call (M) +TSPC_HFP_2_7 True Audio connection transfer during an ongoing + call (M) +TSPC_HFP_2_7a True (*) HF-initiated Audio transfer to AG during + ongoing call (O) +TSPC_HFP_2_8 True Place a call with a phone number supplied by + the HF (M) +TSPC_HFP_2_9 True Place a call using memory dialing (M) +TSPC_HFP_2_10 True Place a call to the last number dialed (M) +TSPC_HFP_2_11 True Call waiting notification (M) +TSPC_HFP_2_12 True (*) Three Way Calling (O) +TSPC_HFP_2_12a True (*) User Busy (AT+CHLD value 0) (C.3) +TSPC_HFP_2_12b True (*) Call Hold Handling (AT+CHLD value 1,2) (C.2) +TSPC_HFP_2_12c True (*) Three Way Call (AT+CHLD value 3) (C.3) +TSPC_HFP_2_12d False Explicit Call Transfer (AT+CHLD value 4) (C.3) +TSPC_HFP_2_13 True Calling Line Identification (CLI) (M) +TSPC_HFP_2_14 True (*) Echo canceling (EC) and Noise reduction (NR) (O) +TSPC_HFP_2_15 True (*) Voice recognition activation (O) +TSPC_HFP_2_15a True (*) Initiate voice recognition from AG (C.6) +TSPC_HFP_2_15b True (*) Autonomous voice deactivation (C.6) +TSPC_HFP_2_16 False Attach a phone number to a voice tag (O) +TSPC_HFP_2_17 True Ability to transmit DTMF codes (M) +TSPC_HFP_2_18a True (*) Remote audio volume control – speaker (O) +TSPC_HFP_2_18b False Remote audio volume control – microphone (O) +TSPC_HFP_2_18c True (*) Volume Level Synchronization – speaker and + microphone (C.5) +TSPC_HFP_2_19 False Response and hold (O) +TSPC_HFP_2_20 True Subscriber Number Information (M) +TSPC_HFP_2_21a True Enhanced Call Status (C.4) +TSPC_HFP_2_21b False Enhanced Call Control (C.3) +TSPC_HFP_2_21c True (*) Enhanced Call Status with limited network + notification (C.4) +TSPC_HFP_2_22 False Support for automatic link loss recovery (O) +TSPC_HFP_2_23 True (*) Individual Indicator Activation (C.9) +TSPC_HFP_2_24 True (*)s Wide Band Speech service (C.8) +TSPC_HFP_2_25 False Support roaming function (O) +------------------------------------------------------------------------------- +C.1: The AG must support one of item TSPC_HFP_2_4a or TSPC_HFP_2_4b +C.2: Mandatory if TSPC_HFP_2_12is TRUE; otherwise excluded +C.3: Optional if TSPC_HFP_2_12 is TRUE; otherwise excluded +C.4: The AG must support one of item TSPC_HFP_2_21a or TSPC_HFP_2_21c +C.5: Mandatory if TSPC_HFP_2_18a or TSPC_HFP_2_18b; otherwise optional +C.6: Optional if TSPC_HFP_2_15 is supported, otherwise excluded +C.7: Mandatory if TSPC_HFP_2_24 otherwise excluded +C.8: Excluded if TSPC_HFP_0_1 otherwise optional +C.9: Excluded if TSPC_HFP_0_1 otherwise mandatory +C.10: Mandatory if TSPC_HFP_2_24 otherwise optional +------------------------------------------------------------------------------- + + + Hands-Free Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_3_1 False (*) Connection Management (M) +TSPC_HFP_3_2a False (*) Phone Status Information ("service" and "call" + indicators) (M) +TSPC_HFP_3_2b False Phone Status Information ("callsetup" + indicators) (O) +TSPC_HFP_3_2c False Accept indicator of signal strength (O) +TSPC_HFP_3_2d False Accept indicator of roaming state ("roam:") (O) +TSPC_HFP_3_2e False Accept indicator of battery level ("battchg") (O) +TSPC_HFP_3_2f False Accept indicator of operator selection (O) +TSPC_HFP_3_3 False (*) Audio connection handling (M) +TSPC_HFP_3_3a False Audio connection establishment independent + of call processing (O) +TSPC_HFP_3_3b False eSCO support in Audio Connection (C.7) +TSPC_HFP_3_3c False Codec negotiation (C.5) +TSPC_HFP_3_4a False (*) Accept an incoming voice call (in-band ring) (M) +TSPC_HFP_3_4b False (*) Accept an incoming voice call (no in-band + ring) (M) +TSPC_HFP_3_4c False Accept an incoming voice call (in-band ring + muting) (O) +TSPC_HFP_3_5 False (*) Reject an incoming voice call (M) +TSPC_HFP_3_6 False (*) Terminate a call (M) + +TSPC_HFP_3_7 False (*) Audio connection transfer during an ongoing + call (M) +TSPC_HFP_3_7a False HF-initiated Audio transfer to AG during + ongoing call (O) +TSPC_HFP_3_8 False Place a call with a phone number supplied by + the HF (O) +TSPC_HFP_3_9 False Place a call using memory dialing (O) +TSPC_HFP_3_10 False Place a call to the last number dialed (O) +TSPC_HFP_3_11 False Call waiting notification (O) +TSPC_HFP_3_12 False Three Way Calling (O) +TSPC_HFP_3_12a False Three way calling (AT+CHLD values 0) (C.2) +TSPC_HFP_3_12b False Three way calling (AT+CHLD values 1 and 2) (C.1) +TSPC_HFP_3_12c False Three way calling (AT+CHLD value 3) (C.2) +TSPC_HFP_3_12d False Three way calling (AT+CHLD value 4) (C.2) +TSPC_HFP_3_12e False Originate new call with established call in + progress (C.2) +TSPC_HFP_3_13 False Calling Line Identification (CLI) (O) +TSPC_HFP_3_14 False Echo cancelling (EC) and Noise reduction (NR) (O) +TSPC_HFP_3_15 False Voice recognition activation/deactivation (O) +TSPC_HFP_3_16 False Attach a phone number to a voice tag (O) +TSPC_HFP_3_17 False Ability to transmit DTMF codes (O) +TSPC_HFP_3_18a False Remote audio volume control – speaker (O) +TSPC_HFP_3_18b False Remote audio volume control – microphone (O) +TSPC_HFP_3_18c False Volume Level Synchronization – speaker (C.3) +TSPC_HFP_3_18d False Volume Level Synchronization – microphone (C.4) +TSPC_HFP_3_18e False HF informs AG about local changes of audio + volume (O) +TSPC_HFP_3_18f False HF informs AG about local changes of + microphone gain (O) +TSPC_HFP_3_19 False Response and hold (O) +TSPC_HFP_3_20 False Subscriber Number Information (O) +TSPC_HFP_3_21a False Enhanced Call Status (O) +TSPC_HFP_3_21b False Enhanced Call Control (C.2) +TSPC_HFP_3_22 False Support for automatic link loss recovery (O) +TSPC_HFP_3_23 False Individual Indicator Activation (C.6) +TSPC_HFP_3_24 False Wide Band Speech service (C.6) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HFP_3_12; otherwise excluded +C.2: Optional if TSPC_HFP_3_12; otherwise excluded +C.3: Mandatory if TSPC_HFP_3_18a or TSPC_HFP_3_18b, otherwise optional +C.4: Mandatory if TSPC_HFP_3_18a, otherwise optional +C.5: Mandatory if TSPC_HFP_3_24 otherwise excluded +C.6: Excluded if TSPC_HFP_0_1 otherwise optional +C.7: Mandatory if TSPC_HFP_3_24 otherwise optional +------------------------------------------------------------------------------- + + + Audio Coding Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_4_1 True CVSD audio coding over SCO (M) +TSPC_HFP_4_2 True (*) mSBC audio coding over eSCO (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if Wide band speech service is supported TSPC_HFP_2_24 or + TSPC_HFP_3_24, otherwise excluded +------------------------------------------------------------------------------- + + + Supplementary Interoperability Verification +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HFP_8_1 True (*) Multiple audio transfers during call – + AG and HF initiated (C.1) +TSPC_HFP_8_2 True (*) Audio transfer by SLC release during + an active call (C.1) +TSPC_HFP_8_3 True (*) Audio transfer by powering ON HF (O) +TSPC_HFP_8_4 True (*) SLC during SDP response (O) +TSPC_HFP_8_5 True (*) Handle dynamic server channel number for HFP + service (O) +TSPC_HFP_8_6 False HF disallows connections in non-discoverable + mode (C.2) +TSPC_HFP_8_7 True (*) HF connects to AG during incoming call (O) +TSPC_HFP_8_8 True (*) Link loss during incoming call (C.3) +TSPC_HFP_8_9 True (*) SLC release during incoming call (C.3) +TSPC_HFP_8_10 True (*) Voice Recognition Activation (C.4) +TSPC_HFP_8_11 True (*) Place outgoing call by dialing number on + the AG (O) +TSPC_HFP_8_12 True (*) Active call termination – NO CARRIER signal + (C.5) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_HFP_2_7a or TSPC_HFP_3_7a is supported, + otherwise excluded +C.2: Optional if TSPC_HFP_1_2 is supported, otherwise excluded +C.3: Optional if TSPC_HFP_1_1 is supported, otherwise excluded +C.4: Optional if TSPC_HFP_2_15 or TSPC_HFP_3_15 is supported, + otherwise excluded +C.5: Optional if TSPC_HFP_2_6 is supported, otherwise excluded +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-hid.txt bluez-5.23/android/pics-hid.txt --- bluez-4.101/android/pics-hid.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-hid.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,292 @@ +HID PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_1_1 True (*) Role: Host, Report protocol (O.1) +TSPC_HID_1_2 False Role: HID Role (O.1) +TSPC_HID_1_3 False Role: Host, Boot protocol (O.1) +------------------------------------------------------------------------------- +O.1: It is Mandatory to support One of these roles. +------------------------------------------------------------------------------- + + + Application Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_2_1 True (*) Host: Establish HID connection (M.1) +TSPC_HID_2_2 True (*) Host: Accept HID connection (M.1) +TSPC_HID_2_3 True (*) Host: Terminate HID connection (M.1) +TSPC_HID_2_4 True (*) Host: Accept termination of HID connection (M.1) +TSPC_HID_2_5 True (*) Host: Support for virtual cables (M.1) +TSPC_HID_2_6 True (*) Host: HID initiated connection (M.1) +TSPC_HID_2_7 True (*) Host: Host initiated connection (M.1) +TSPC_HID_2_8 True (*) Host: Host data transfer to HID (C.1) +TSPC_HID_2_9 True (*) Host: HID data transfer to Host (C.1) +TSPC_HID_2_10 False Host: Boot mode data transfer to Host (C.2) +TSPC_HID_2_11 False Host : Boot mode data transfer to HID (C.2) +TSPC_HID_2_12 False Host : Support for Application to send + GET_Report (O) +TSPC_HID_2_13 False Host : Support for Application to send + SET_REPORT (O) +TSPC_HID_2_14 False Host : Support for sending HCI_CONTROL with + VIRTUAL_CABLE_UNPLUG (C.3) +TSPC_HID_2_15 False Host : Support for receiving HCI_CONTROL with + VIRTUAL_CABLE_UNPLUG (C.3) +------------------------------------------------------------------------------- +M.1: Mandatory to support IF (TSPC_HID_1_1) supported. +C.1: Optional for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory + for Host Role (TSPC_HID_1_1). +C.2: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional. +C.3: Optional IF (TSPC_HID_2_5) supported, otherwise excluded. +------------------------------------------------------------------------------- + + + Device to Host Transfers +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_3_1 False Host : Data reports larger than host MTU on + Control channel (C.1) +TSPC_HID_3_2 True (*) Host : Data reports larger than host MTU on + Interrupt channel (C.1) +TSPC_HID_3_3 True (*) Host : Data reports to host (C.2) +TSPC_HID_3_4 False Host : Boot mode reports to host (O) +------------------------------------------------------------------------------- +C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional +C.2: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory for + Host Role (TSPC_HID_1_1) +------------------------------------------------------------------------------- + + + Host to Device Transfers +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_4_1 False Host : Data reports larger than device MTU on + Control channel (C.1) +TSPC_HID_4_2 False Host : Data reports larger than device MTU on + Interrupt channel (C.1) +TSPC_HID_4_3 True (*) Host : Data reports to device (C.2) +TSPC_HID_4_4 False Host : Boot mode reports to device (O) +------------------------------------------------------------------------------- +C.1: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional +C.2: Excluded for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Mandatory for + Host Role (TSPC_HID_1_1). +------------------------------------------------------------------------------- + + + HID Control Commands +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_5_1 False Host : Set_Protocol command (C.1, C.4) +TSPC_HID_5_2 False Host : Get_Protocol command (C.1, C.4) +TSPC_HID_5_3 False Host : Set_Idle command (O) +TSPC_HID_5_4 False Host : Get_Idle command (O) +TSPC_HID_5_5 False Host : Set_Report command (C.2) +TSPC_HID_5_6 False Host : Get_Report command (C.3) +------------------------------------------------------------------------------- +C.2: Mandatory IF (TSPC_HID_1_1) supported AND (TSPC_HID_2_13) supported. +C.1: Mandatory for Boot Mode Only Hosts (TSPC_HID_1_3); otherwise Optional. + If either Set_Protocol or Get_Protocol supported, both are Mandatory. +C.3: Mandatory IF (TSPC_HID_1_1) Supported AND (TSPC_HID_2_12) Supported +C.4: Mandatory to support TSPC_HID_5_1 (Set_Protocol command) AND TSPC_HID_5_2 + (Get_Protocol command) IF one of TSPC_HID_5_1 (Set_Protocol command) + OR TSPC_HID_5_2 (Get_Protocol command) is supported, otherwise + Excluded. +------------------------------------------------------------------------------- + + + Host Link Manager Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_6_1 False Host : Initiate Authentication before + connection completed (C.1) +TSPC_HID_6_2 False Host : Initiate Authentication after connection + completed (C.1) +TSPC_HID_6_3 False Host : Initiate pairing before connection + completed (C.2) +TSPC_HID_6_4 False Host : Initiate pairing after connection + completed (C.2) +TSPC_HID_6_5 False Host : Encryption (O) +TSPC_HID_6_6 False Host : Initiate encryption (C.3) +TSPC_HID_6_7 False Host : Accept encryption requests (C.3) +TSPC_HID_6_8 True (*) Host : Role switch (Master/Slave) (M.1) +TSPC_HID_6_9 True (*) Host : Request Master Slave switch (M.1) +TSPC_HID_6_10 True (*) Host : Accept Master Slave switch requests (M.1) +TSPC_HID_6_11 False Host : Hold mode (O) +TSPC_HID_6_12 True (*) Host : Sniff mode (M.1) +TSPC_HID_6_13 False Host : Park mode (O) +------------------------------------------------------------------------------- +C.1: If Host Authentication supported, both (TSPC_HID_6_1) AND (TSPC_HID_6_2) + must be supported. +C.2: If Pairing supported both (TSPC_HID_6_3) AND (TSPC_HID_6_4) must + be supported. +M.1: Mandatory IF (TSPC_HID_1_1) supported. +C.3: Mandatory IF (TSPC_HID_6_5) encryption supported. +------------------------------------------------------------------------------- + + + Host Link Control Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_7_1 True (*) Host : Supports inquiry, 79 channel (M.1) +TSPC_HID_7_2 False Host : Supports inquiry scan, 79 channel (C.2) +------------------------------------------------------------------------------- +M.1: Mandatory to support IF (TSPC_HID_1_1) supported. +C.2: Feature should not be used by a Host, but can be supported in LM. +------------------------------------------------------------------------------- + + + HID Device Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_8_1 False Hid : Pointing HID (O.1) +TSPC_HID_8_2 False Hid : Keyboard HID (O.1) +TSPC_HID_8_3 False Hid : Identification HID (O.1) +TSPC_HID_8_4 False Hid : Other HID (O.1) +------------------------------------------------------------------------------- +O.1: It is Mandatory to support One of these roles IF (TSPC_HID_1_2) + is selected +------------------------------------------------------------------------------- + + + HID Application Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_9_1 False Hid : Establish HID connection (O) +TSPC_HID_9_2 False (*) Hid : Accept HID connection (M.1) +TSPC_HID_9_3 False Hid : Terminate HID connection (O) +TSPC_HID_9_4 False (*) Hid : Accept Termination of HID connection (M.1) +TSPC_HID_9_5 False Hid : Support for virtual cables (O) +TSPC_HID_9_6 False Hid : HID initiated reconnection (C.1) +TSPC_HID_9_7 False Hid : Host initiated reconnection (C.1) +TSPC_HID_9_8 False Hid : Host data transfer to HID (C.2) +TSPC_HID_9_9 False Hid : HID data transfer to Host (C.2) +TSPC_HID_9_10 False Hid : HID Boot mode data transfer to Host (C.3) +TSPC_HID_9_11 False Hid : Host Boot mode data transfer to HID (C.4) +TSPC_HID_9_12 False Hid : Output reports declared (C.4) +TSPC_HID_9_13 False Hid : Input reports declared (C.3) +TSPC_HID_9_14 False Hid : Feature reports declared (O) +TSPC_HID_9_15 False Hid : Support for sending HCI_CONTROL with + VIRTUAL_CABLE_UNPLUG (C.5) +TSPC_HID_9_16 False Hid : Support for receiving HCI_CONTROL with + VIRTUAL_CABLE_UNPLUG (C.5) +------------------------------------------------------------------------------- +M.1: Mandatory IF (TSPC_HID_1_2) supported. +C.1: One of these is Mandatory IF (TSPC_HID_9_5) is supported + (SDP attribute 0x204=True) +C.2: One of these is Mandatory. +C.3: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is selected +C.4: Mandatory IF (TSPC_HID_8_2) is supported (for status indicators) +C.5: Optional IF (TSPC_HID_9_5) supported, otherwise excluded. +------------------------------------------------------------------------------- + + + Device to Host Transfers +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_10_1 False Hid : Data reports larger than host MTU on + Control channel (O) +TSPC_HID_10_2 False Hid : Data reports larger than host MTU on + Interrupt channel (O) +TSPC_HID_10_3 False Hid : Data reports to host (O) +TSPC_HID_10_4 False Hid : Boot mode reports to host (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported. + Optional for other HIDs. +------------------------------------------------------------------------------- + + + Host to Device Transfers +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_11_1 False Hid : Data reports larger than device MTU on + Control channel (O) +TSPC_HID_11_2 False Hid : Data reports larger than device MTU on + Interrupt channel (O) +TSPC_HID_11_3 False Hid : Data reports to device (O) +TSPC_HID_11_4 False Hid : Boot mode reports to device (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory IF (TSPC_HID_8_2) is supported. Optional for other HIDs. +------------------------------------------------------------------------------- + + + HID Control Commands +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_12_1 False Hid : Set_Protocol command (C.1, C.5) +TSPC_HID_12_2 False Hid : Get_Protocol command (C.1, C.5) +TSPC_HID_12_3 False Hid : Set_Idle command (C.2) +TSPC_HID_12_4 False Hid : Get_Idle command (C.2) +TSPC_HID_12_5 False Hid : Set_Report command (C.3) +TSPC_HID_12_6 False Hid : Get_Report command (C.4) +------------------------------------------------------------------------------- +C.1: Mandatory IF (TSPC_HID_8_1) OR (TSPC_HID_8_2) is supported. + Optional for other HIDs. If either Set_Protocol or Get_Protocol + supported, both are Mandatory. +C.2: Mandatory IF (TSPC_HID_8_2) Keyboard is selected. Optional for other HIDs. +C.3: Mandatory IF (TSPC_HID_9_12) or (TSPC_HID_9_14) supported. +C.4: Mandatory IF (TSPC_HID_9_13) or (TSPC_HID_9_14) supported +C.5: If either TSPC_HID_12_1 (Set_Protocol command) OR TSPC_HID_12_2 + (Get_Protocol command) is supported, both TSPC_HID_12_1 + (Set_Protocol command) AND TSPC_HID_12_2 (Get_Protocol command) are + Mandatory to support +------------------------------------------------------------------------------- + + + HID Link Manager Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_13_1 False Hid : Host initiated Authentication before + connection completed (C.1) +TSPC_HID_13_2 False Hid : Host initiated Authentication after + connection completed (C.1) +TSPC_HID_13_3 False Hid : Item no longer used (N/A) +TSPC_HID_13_4 False Hid : Item no longer used (N/A) +TSPC_HID_13_5 False Hid : Encryption (C.1) +TSPC_HID_13_6 False Hid : Initiate encryption (O) +TSPC_HID_13_7 False Hid : Accept encryption requests (C.2) +TSPC_HID_13_8 False Hid : Role switch (Master/Slave) (C.3) +TSPC_HID_13_9 False Hid : Request Master Slave switch (O) +TSPC_HID_13_10 False Hid : Accept Master Slave switch requests (C.3) +TSPC_HID_13_11 False Hid : Hold mode (O) +TSPC_HID_13_12 False Hid : Sniff mode (O) +TSPC_HID_13_13 False Hid : Park mode (O) +------------------------------------------------------------------------------- +C.1: Mandatory IF (TSPC_HID_8_2) OR (TSPC_HID_8_3) is selected. Optional + for other HIDs. +C.2: Mandatory IF (TSPC_HID_13_5) supported. +C.3: Mandatory IF (TSPC_HID_9_6) is supported. +------------------------------------------------------------------------------- + + + HID Link Control Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HID_14_1 False Hid : Supports inquiry, 79 channel (O) +TSPC_HID_14_2 False Hid : Supports inquiry scan, 79 channel (M.1) +TSPC_ALL False Enables all test cases when set to true. +------------------------------------------------------------------------------- +M.1: Mandatory IF (TSPC_HID_1_2) is supported. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-hogp.txt bluez-5.23/android/pics-hogp.txt --- bluez-4.101/android/pics-hogp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-hogp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,401 @@ +HOGP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Profile Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_1_1 False (*) HID Device (Server) (C.1) +TSPC_HOGP_1_2 True Report Host (Client) (C.1, C.2) +TSPC_HOGP_1_3 False (*) Boot Host (Client) (C.1, C.3) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of TSPC_HOGP_1_1 or TSPC_HOGP_1_2 + or TSPC_HOGP_1_3. +C.2: Excluded if TSPC_HOGP_1_3 is supported. +C.3: Excluded if TSPC_HOGP_1_2 is supported. +------------------------------------------------------------------------------- + + + Transport +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_2_1 False (*) Profile supported over BR/EDR (C.1) +TSPC_HOGP_2_2 True Profile supported over LE (M) +------------------------------------------------------------------------------- +C.1: Excluded for this profile. +------------------------------------------------------------------------------- + + + Services - HID Device +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_3_1 False (*) Implements HID Service (M.1) +TSPC_HOGP_3_2 False (*) Multiple Service instances - HID Service (O) +TSPC_HOGP_3_3 False (*) Implements Battery Service (M.1) +TSPC_HOGP_3_4 False (*) Implements Device Information Service (M.1) +TSPC_HOGP_3_5 False (*) Implements Scan Parameters Service (O) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_1 selected +------------------------------------------------------------------------------- + + + Features - HID Device +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_4_1 False (*) Include HID Service UUID in AD in GAP + Discoverable Mode (O) +TSPC_HOGP_4_2 False (*) Include Local Name in AD or Scan Response Data + (O) +TSPC_HOGP_4_3 False (*) Include Appearance in AD or Scan Response Data + (O) +TSPC_HOGP_4_4 False (*) Support Device Information Service + characteristic: PnP ID (M) +TSPC_HOGP_4_5 False (*) Report characteristic (C.1) +TSPC_HOGP_4_6 False (*) Non-HID Service characteristic described within + Report Map characteristic (C.1) +TSPC_HOGP_4_7 False (*) External Report Reference characteristic + descriptor for Report Map characteristic + (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of these features. +C.2: Mandatory if TSPC_HOGP_4_6 is supported, else excluded. +------------------------------------------------------------------------------- + + + GAP Requirements - HID Device +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_5_1 False (*) Peripheral (M.1) +TSPC_HOGP_5_2 False (*) Directed Connectable Mode (O) +TSPC_HOGP_5_3 False (*) Undirected Connectable Mode (M.1) +TSPC_HOGP_5_4 False (*) Bondable mode (peripheral) (M.1) +TSPC_HOGP_5_5 False (*) Bonding procedure (peripheral) (M.1) +TSPC_HOGP_5_6 False (*) LE Security Mode 1 (peripheral) (M.1) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_1 selected +------------------------------------------------------------------------------- + + + SM Requirements - HID Device +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_6_1 False (*) SM 2.3.1 (M.1) +TSPC_HOGP_6_2 False (*) Unauthenticated no MITM protection + (LE Security Level 2, Just Works) (M.1) +TSPC_HOGP_6_3 False (*) Authenticated MITM protection + (LE Security Level 3, Passkey) (O) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_1 selected +------------------------------------------------------------------------------- + + + Client Services Support - Report Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_7_1 True HID Service (M.1) +TSPC_HOGP_7_2 True Battery Service (M.1) +TSPC_HOGP_7_3 True Device Information Service (M.1) +TSPC_HOGP_7_4 True Scan Parameters Service (M.1) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_2 selected +------------------------------------------------------------------------------- + + + GATT based Profile Support - Report Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_7a_1 True Scan Parameters Profile (M.1) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_2 selected +------------------------------------------------------------------------------- + + + Client Service Support - Boot Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_8_1 False (*) HID Service (M.1) +TSPC_HOGP_8_2 False (*) Battery Service (O) +TSPC_HOGP_8_3 False (*) Device Information Service (O) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_3 selected +------------------------------------------------------------------------------- + + + Discover Services & Characteristics - Report Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_9_1 True Discover HID Service (M.1) +TSPC_HOGP_9_2 True Discover Battery Service (M.1) +TSPC_HOGP_9_3 True Discover Device Information Service (M.1) +TSPC_HOGP_9_4 True Discover Scan Parameters Service (M.1) +TSPC_HOGP_9_5 True Discover HID Service characteristic: Report Map + (M.1) +TSPC_HOGP_9_6 True Discover HID Service characteristic: Report Map + - External Report Reference + characteristic descriptor (M.1) +TSPC_HOGP_9_7 True Discover HID Service characteristic: Report + (M.1) +TSPC_HOGP_9_8 True Discover HID Service characteristic: Report + - Client Characteristic Configuration + characteristic descriptor (M.1) +TSPC_HOGP_9_9 True Discover HID Service characteristic: Report + - Report Reference characteristic + descriptor (M.1) +TSPC_HOGP_9_10 True Discover HID Service characteristic: HID + Information (M.1) +TSPC_HOGP_9_11 True Discover HID Service characteristic: HID + Control Point (M.1) +TSPC_HOGP_9_12 True Discover HID Service characteristic: Protocol + Mode (O) +TSPC_HOGP_9_13 True Discover Battery Service characteristic: Battery + Level (M.1) +TSPC_HOGP_9_14 True Discover Battery Service characteristic: Battery + Level - Client Characteristic + Configuration characteristic descriptor + (M.1) +TSPC_HOGP_9_15 True Discover Device Information Service + characteristic: PnP ID (M.1) +TSPC_HOGP_9_16 True Discover non-HID Service characteristic: Report + Reference characteristic descriptor + (M.1) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_2 selected +------------------------------------------------------------------------------- + + + Discover Services & Characteristics - Boot Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_10_1 False (*) Discover HID Service (M.1) +TSPC_HOGP_10_2 False (*) Discover Battery Service (O) +TSPC_HOGP_10_3 False (*) Discover Device Information Service (O) +TSPC_HOGP_10_4 False (*) Discover HID Service characteristic: Protocol + Mode (M.1) +TSPC_HOGP_10_5 False (*) Discover HID Service characteristic: Boot + Keyboard Input Report (C.1, C.2) +TSPC_HOGP_10_6 False (*) Discover HID Service characteristic: Boot + Keyboard Input Report - Client + Characteristic Configuration + characteristic descriptor (C.3) +TSPC_HOGP_10_7 False (*) Discover HID Service characteristic: Boot + Keyboard Output Report (C.1, C.2) +TSPC_HOGP_10_8 False (*) Discover HID Service characteristic: Boot + Mouse Input Report (C.1) +TSPC_HOGP_10_9 False (*) Discover HID Service characteristic: Boot + Mouse Input Report - Client + Characteristic Configuration + characteristic descriptor (C.4) +TSPC_HOGP_10_10 False (*) Discover Battery Service characteristic: + Battery Level (O) +TSPC_HOGP_10_11 False (*) Discover Battery Service characteristic: + Battery Level - Client Characteristic + Configuration characteristic descriptor + (O) +TSPC_HOGP_10_12 False (*) Discover Device Information Service + characteristic: PnP ID (O) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_3 selected +C.1: Mandatory to support at least one of TSPC_HOGP_10_5, TSPC_HOGP_10_7, or + TSPC_HOGP_10_8. +C.2: If one of TSPC_HOGP_10_5 or TSPC_HOGP_10_7 is supported, both shall be + supported. +C.3: Mandatory to support if TSPC_HOGP_10_5 is supported, else excluded. +C.4: Mandatory to support if TSPC_HOGP_10_8 is supported, else excluded. +------------------------------------------------------------------------------- + + + Features - Report Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_11_1 True Read Report Map characteristic (M.1) +TSPC_HOGP_11_2 True Read Report Map characteristic: External + Report Reference characteristic + descriptor (M.1) +TSPC_HOGP_11_3 True Read Report characteristic: Report Type: + Input Report (M.1) +TSPC_HOGP_11_4 True Write Report characteristic: Report Type: + Input Report (M.1) +TSPC_HOGP_11_5 True Read Report characteristic: Report Type: + Output Report (M.1) +TSPC_HOGP_11_6 True Write HID Report characteristic: Report Type: + Output Report (M.1) +TSPC_HOGP_11_7 True Read HID Report characteristic: Report Type: + Feature Report (M.1) +TSPC_HOGP_11_8 True Write HID Report characteristic: Report Type: + Feature Report (M.1) +TSPC_HOGP_11_9 True Read Report characteristic: Report Reference + characteristic descriptor (M.1) +TSPC_HOGP_11_10 True Read Report characteristic: Input Report: + Client Characteristic Configuration + characteristic descriptor (M.1) +TSPC_HOGP_11_11 True Report characteristic configuration with 0x0001 + (M.1) +TSPC_HOGP_11_11a True Report characteristic configuration with 0x0000 + (M.1) +TSPC_HOGP_11_12 True Read HID Information characteristic (M.1) +TSPC_HOGP_11_13 False (*) Suspend State (O) +TSPC_HOGP_11_14 False (*) Exit Suspend State (C.1) +TSPC_HOGP_11_15 False (*) Write HID Control Point characteristic: Suspend + command (C.1) +TSPC_HOGP_11_16 False (*) Write HID Control Point characteristic: Exit + Suspend command (C.1) +TSPC_HOGP_11_17 False (*) Read Protocol Mode characteristic: Get Protocol + command (O) +TSPC_HOGP_11_18 False (*) Write Protocol Mode characteristic: Set Report + Protocol Mode command (O) +TSPC_HOGP_11_19 True Read Battery Level characteristic (M.1) +TSPC_HOGP_11_20 True Read Battery Level characteristic: Client + Characteristic Configuration + characteristic descriptor (M.1) +TSPC_HOGP_11_21 True Battery Level characteristic configuration with + 0x0000 0r 0x0001 (M.1) +TSPC_HOGP_11_22 True Read non-HID Service characteristic: Report + Reference characteristic descriptor + (M.1) +TSPC_HOGP_11_23 True Read PnP ID characteristic (M.1) +TSPC_HOGP_11_24 True Notify Report characteristic (M.1) +TSPC_HOGP_11_25 True Notify Battery Level characteristic (M.1) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_2 selected +C.1: Mandatory to support if TSPC_HOGP_11_13 is supported, else excluded. +------------------------------------------------------------------------------- + + + Features - Boot Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_12_1 False (*) Read Protocol Mode characteristic: Get Protocol + Mode command (M.1) +TSPC_HOGP_12_2 False (*) Write Protocol Mode characteristic: Set Boot + Protocol Mode command (M.1) +TSPC_HOGP_12_3 False (*) Read HID Service characteristic: Boot Keyboard + Input Report (C.1) +TSPC_HOGP_12_4 False (*) Write HID Service characteristic: Boot Keyboard + Input Report (C.1) +TSPC_HOGP_12_5 False (*) Read Client Characteristic Configuration + characteristic descriptor for Boot + Keyboard Input Report (C.1) +TSPC_HOGP_12_6 False (*) Boot Keyboard Input Report characteristic: + configuration with 0x0000 or 0x0001 + (C.1) +TSPC_HOGP_12_7 False (*) Read HID Service characteristic: Boot Keyboard + Output Report (C.1) +TSPC_HOGP_12_8 False (*) Write HID Service characteristic: Boot Keyboard + Output Report (C.1) +TSPC_HOGP_12_9 False (*) Read HID Service characteristic: Boot Mouse + Input Report (C.2) +TSPC_HOGP_12_10 False (*) Write HID Service characteristic: Boot Mouse + Input Report (C.2) +TSPC_HOGP_12_11 False (*) Read Client Characteristic Configuration + characteristic descriptor for Boot + Mouse Input Report (C.2) +TSPC_HOGP_12_12 False (*) Boot Mouse Input Report characteristic: + configuration with 0x0000 or 0x0001 + (C.2) +TSPC_HOGP_12_13 False (*) Notify Boot Keyboard Input Report characteristic + (C.1) +TSPC_HOGP_12_14 False (*) Notify Boot Mouse Input Report characteristic + (C.2) +TSPC_HOGP_12_15 False (*) Read Battery Level characteristic (O) +TSPC_HOGP_12_16 False (*) Read Battery Level characteristic: Client + Characteristic Configuration + characteristic descriptor (O) +TSPC_HOGP_12_17 False (*) Battery Level characteristic: configuration with + 0x0000 or 0x0001 (O) +TSPC_HOGP_12_18 False (*) Notify Battery Level characteristic (O) +TSPC_HOGP_12_19 False (*) Read PnP ID characteristic (O) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_3 selected +C.1: Mandatory to support if TSPC_HOGP_10_5 or TSPC_HOGP_10_7 is supported, + else excluded. +C.2: Mandatory to support if TSPC_HOGP_10_8 is supported, else excluded. +------------------------------------------------------------------------------- + + + GATT Requirements - Report Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_13_1 True Attribute Protocol supported over LE Transport +TSPC_HOGP_13_2 True Generic Attribute Profile Client +TSPC_HOGP_13_3 True Discover All Primary Services +TSPC_HOGP_13_4 False (*) Discover Primary Services by Service UUID +TSPC_HOGP_13_5 True Find Included Services +TSPC_HOGP_13_6 True Discover All Characteristics of a Service +TSPC_HOGP_13_7 False (*) Discover Characteristics by UUID +TSPC_HOGP_13_8 True Discover All Characteristic Descriptors +TSPC_HOGP_13_9 True Read Characteristic Value +TSPC_HOGP_13_10 True Read using Characteristic UUID +TSPC_HOGP_13_11 True Read Long Characteristic Value +TSPC_HOGP_13_12 True Read Characteristic Descriptors +TSPC_HOGP_13_13 True Write without Response +TSPC_HOGP_13_14 True Write Characteristic Value +TSPC_HOGP_13_15 True Write Characteristic Descriptors +TSPC_HOGP_13_16 True Notifications +TSPC_HOGP_13_17 True Exchange MTU +------------------------------------------------------------------------------- + + + GATT Requirements - Boot Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_14_1 False (*) Attribute Protocol supported over LE Transport +TSPC_HOGP_14_2 False (*) Generic Attribute Profile Client +TSPC_HOGP_14_3 False (*) Discover All Primary Services +TSPC_HOGP_14_4 False (*) Discover Primary Services by Service UUID +TSPC_HOGP_14_5 False (*) Discover All Characteristics of a Service +TSPC_HOGP_14_6 False (*) Discover Characteristics by UUID +TSPC_HOGP_14_7 False (*) Discover All Characteristic Descriptors +TSPC_HOGP_14_8 False (*) Read Characteristic Value +TSPC_HOGP_14_9 False (*) Read using Characteristic UUID +TSPC_HOGP_14_10 False (*) Read Characteristic Descriptors +TSPC_HOGP_14_11 False (*) Write without Response +TSPC_HOGP_14_12 False (*) Write Characteristic Value +TSPC_HOGP_14_13 False (*) Write Characteristic Descriptors +TSPC_HOGP_14_14 False (*) Notifications +------------------------------------------------------------------------------- + + + GAP Requirements - HID Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_15_1 True Central (M.1 or M.2) +TSPC_HOGP_15_2 True LE Security Mode 1 (central) (M.1 or M.2) +------------------------------------------------------------------------------- +M.1: Mandatory if TSPC_HOGP_1_2 selected +M.2: Mandatory if TSPC_HOGP_1_3 selected +------------------------------------------------------------------------------- + + + SM Requirements - HID Host +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HOGP_16_1 True No Security Requirements (LE Security Level 1, + No Security) +TSPC_HOGP_16_2 True Unauthenticated no MITM protection (LE Security + Level 2, Just Works) +TSPC_HOGP_16_3 True Authenticated MITM protection (LE Security + Level 3, Passkey) +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-hsp.txt bluez-5.23/android/pics-hsp.txt --- bluez-4.101/android/pics-hsp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-hsp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,103 @@ +HSP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_0_1 False Version: Headset Profile v1.1 (C.1) +TSPC_HSP_0_2 True (*) Version: Headset Profile v1.2 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support one and only one of these versions. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_1_1 True (*) Role: Audio Gateway (AG) (C.1) +TSPC_HSP_1_2 False Role: Headset (HS) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Audio Gateway Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_2_1 True Incoming audio connection establishment (M) +TSPC_HSP_2_2 True (*) Ring (AT command) (C.3) +TSPC_HSP_2_3 False Inband ring tone (O) +TSPC_HSP_2_4 True (*) Outgoing audio connection establishment (O) +TSPC_HSP_2_5 True (*) Audio connection release from HS (C.5) +TSPC_HSP_2_6 True Audio connection release from AG (M) +TSPC_HSP_2_7 True Audio connection transfer: AG to HS (M) +TSPC_HSP_2_8 True Audio connection transfer: HS to AG (M) +TSPC_HSP_2_9 True (*) Remote audio volume control (C.1) +TSPC_HSP_2_10 True (*) HS informs AG about local changes of audio + volume (O) +TSPC_HSP_2_11 True (*) Audio volume setting storage by HS (O) +TSPC_HSP_2_12 False Remote microphone gain control (C.2) +TSPC_HSP_2_13 False HS informs AG about local changes of microphone + gain (O) +TSPC_HSP_2_14 False Microphone gain setting storage by HS (O) +TSPC_HSP_2_15 True Connection handling with Detach/Page (M) +TSPC_HSP_2_16 False Connection handling with Park Mode (C.4) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HSP_2_10 is supported, otherwise optional +C:2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional +C.3: Excluded if TSPC_HSP_2_3 and TSPC_HSP_4_1 ("Show that in-band + ringing and RING are mutually exclusive") are supported, + otherwise optional +C.4: Excluded if TSPC_HSP_0_2 is supported, otherwise optional +C.5: Mandatory if TSPC_HSP_0_1 is supported, otherwise optional +------------------------------------------------------------------------------- + + + Headset Application Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_3_1 False (*) Incoming audio connection establishment (M) +TSPC_HSP_3_2 False (*) Ring (AT command) (M) +TSPC_HSP_3_3 False (*) Inband ring tone (M) +TSPC_HSP_3_4 False (*) Outgoing audio connection establishment (M) +TSPC_HSP_3_5 False (*) Audio connection release from HS (M) +TSPC_HSP_3_6 False (*) Audio connection release from AG (M) +TSPC_HSP_3_7 False (*) Audio connection transfer: AG to HS (M) +TSPC_HSP_3_8 False (*) Audio connection transfer: HS to AG (M) +TSPC_HSP_3_9 False Remote audio volume control (C.1) +TSPC_HSP_3_10 False HS informs AG about local changes of audio + volume (O) +TSPC_HSP_3_11 False Audio volume setting storage by HS (O) +TSPC_HSP_3_12 False Remote microphone gain control (C.2) +TSPC_HSP_3_13 False HS informs AG about local changes of microphone + gain (O) +TSPC_HSP_3_14 False (*) Microphone gain setting storage by HS (O) +TSPC_HSP_3_15 False Connection handling with Detach/Page (M) +TSPC_HSP_3_16 False Connection handling with Park Mode (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_HSP_3_10 is supported, otherwise optional +C.2: Mandatory if TSPC_HSP_2_13 is supported, otherwise optional +C.3: Excluded if TSPC_HSP_0_2 is supported, otherwise optional +------------------------------------------------------------------------------- + + + Errata Service Releases +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_HSP_4_1 False Show that in-band ringing and RING are + mutually exclusive (C.1) +------------------------------------------------------------------------------- +C.1: Excluded if TSPC_HSP_0_2 is supported, otherwise optional +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-iopt.txt bluez-5.23/android/pics-iopt.txt --- bluez-4.101/android/pics-iopt.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-iopt.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,223 @@ +IOPT PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Profiles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_support_ Support for: Advanced +AdvancedAudioDistributionProfile_Sink False Audio Distribution + Profile. Role: Sink + +TSPC_support_ Support for: Advanced +AdvancedAudioDistributionProfile_Source True (*) Audio Distribution + Profile. Role: Source + +TSPC_support_AVRemoteControlProfile_CT True (*) Support for: Audio\Video + Remote Control Profile. + Role: Controller + +TSPC_support_AVRemoteControlProfile_TG True (*) Support for: Audio\Video + Remote Control Profile. + Role: Target + +TSPC_support_BasicImagingProfile_CLIENT False Support for: Basic + Imaging Profile. + Role: Client + +TSPC_support_BasicImagingProfile_ Support for: Basic +SERVER_ImagingAutomaticArchive False Imaging Profile. Role: + Server Functionality: + Imaging autoarchive + +TSPC_support_BasicImagingProfile_ False Support for: Basic +SERVER_ImagingReferencedObjects Imaging Profile. Role: + Server Functionality: + Imaging ref. objects + +TSPC_support_BasicImagingProfile_ False Support for: Basic +SERVER_ImagingResponder Imaging Profile. Role: + Server Functionality: + Imaging responder + +TSPC_support_ False Support for: Basic +BasicPrintingProfile_PRINTER Printing Profile. Role: + Printer + +TSPC_support_ Support for: Basic +BasicPriProfile_PRINTER_ReflectedUI False Printing Profile. Role: + Printer Functionality: + Reflected UI + +TSPC_support_BasicPrintingProfile_ Support for: Basic +SENDER_Referenced_objects_Service False Printing Profile. Role: + Sender Functionality: + Refe. objects service + +TSPC_support_DialUpNetworkingProfile_DT False Support for: Dial-Up + Networking Profile. + Role: Data Terminal + +TSPC_support_DialUpNetworkingProfile_GW False Support for: Dial-Up + Networking Profile. + Role: Gateway + +TSPC_support_ False Support for: Extended +ExtendedServiceDiscoveryProfile_IP_LAP SDP. Version: IP-LAP + +TSPC_support_ False Support for: Extended +ExtendedServiceDiscoveryProfile_IP_PAN SDP. Version: IP-PAN + +TSPC_support_ False Support for: Extended +ExtendedServiceDiscoveryProfile_L2CAP SDP. Version: L2CAP + +TSPC_support_FAXProfile_DE False Support for: FAX Profile + Role: Data Terminal + +TSPC_support_FAXProfile_GW False Support for: FAX Profile + Role: Gateway + +TSPC_support_FileTransferProfile_CLIENT False Support for: FTP + Role: Client + +TSPC_support_FileTransferProfile_SERVER False Support for: FTP + Role: Server + +TSPC_support_HealthDeviceProfile_Sink True (*) Support for: HDP + Role: Sink + +TSPC_support_HealthDeviceProfile_Source False Support for: HDP + Role: Source + +TSPC_support_NewHandsFreeProfile_AG False Support for: HFP + Role: Audio gateway + +TSPC_support_NewHandsFreeProfile_HF False Support for: HFP + Role: Hands-Free unit + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_ cable Repl. Profile +CLIENT_CR_RegisterNotofication_support Role: Client + Functionality: CR + register notification + support + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_CLIENT_print cable Repl. Profile. + Role: Client + Functionality: Print + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_CLIENT_scan cable Repl. Profile. + Role: Client + Functionality: Scan + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_SERVER_print cable Repl. Profile. + Role: Server + Functionality: Print + +TSPC_support_ False Support for: Hard Copy +HardCopyReplacementProfile_SERVER_scan cable Repl. Profile. + Role: Server + Functionality: Scan + +TSPC_support_HeadsetProfile_AG True (*) Support for: HSP + Role: Audio Gateway + +TSPC_support_HeadsetProfile_HS False Support for: HSP + Role: Headset + +TSPC_support_ False Support for: HID +HumanInterfaceDeviceProfile Role: Device + +TSPC_support_HID_Host True (*) Support for: HID + Role: Host + +TSPC_support_LANAccessProfile_DT False Support for: LAN Access + Profile. Role: Data + Terminal + +TSPC_support_LANAccessProfile_LAP False Support for: LAN Access + Profile. Role: LAN + Access Point + +TSPC_support_MessaeAccessProfile_MCE False Support for: MAP + Role: MCE + +TSPC_support_MessageAccessProfile_MSE True (*) Support for: MAP + Role: MSE + +TSPC_support_ObjectPushProfile_CLIENT True (*) Support for: OPP + Role: Client + +TSPC_support_ObjectPushProfile_SERVER True (*) Support for: OPP + Role: Server + +TSPC_support_ False Support for: PAN +PersonalAreaNetworkProfile_GN Role: GN + +TSPC_support_ True (*) Support for: PAN +PersonalAreaNetworkProfile_NAP Role: NAP + +TSPC_support_ True (*) Support for: PAN +PersonalAreaNetworkProfile_PANU Role: PANU + +TSPC_support_PhonebookAccessProfile_PCE False Support for: PBAP + Role: PCE + +TSPC_support_PhonebookAccessProfile_PSE True (*) Support for: PBAP + Role: PSE + +TSPC_support_SerialPortProfile_Service False Support for: SPP + Role: Dev B + +TSPC_support_ False Support for: Service +ServiceDiscoveryApplicationProfile Discovery Application + Profile + +TSPC_support_SIMAccessProfile_CLIENT False Support for: SIM access + Profile. Role: Client + +TSPC_support_SIMAccessProfile_SERVER False Support for: SIM access + Profile. Role: Server + +TSPC_support_ False Support for: +SynchronizationProfile_CLIENT Synchronization Profile + Role: Client + +TSPC_support_ False Support for: +SynchronizationProfile_SERVER Synchronization Profile + Role: Server + +TSPC_support_UDIProfile_MT False Support for: UDI Profile + Role: MT + +TSPC_support_UDIProfile_TA False Support for: UDI Profile + Role: TA + +TSPC_support_ False Support for: Video +VideoDistributionProfile_Sink distribution Profile + Role: Sink + +TSPC_support_ False Support for: Video +VideoDistributionProfile_Source distribution Profile + Role: Source + +TSPC_support_WAPOverBluetooth_CLIENT False Support for: WAP over + Bluetooth Profile + Role: Client + +TSPC_support_WAPOverBluetooth_PROXY False Support for: WAP over + Bluetooth Profile + Role: PROXY + +TSPC_support_GNSS_SERVER False Support for: GNSS + Role: Server diff -Nru bluez-4.101/android/pics-l2cap.txt bluez-5.23/android/pics-l2cap.txt --- bluez-4.101/android/pics-l2cap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-l2cap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,162 @@ +L2CAP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_L2CAP_1_1 True Data Channel Initiator (C.1) +TSPC_L2CAP_1_2 True Data Channel Acceptor (C.1) +TSPC_L2CAP_1_3 True (#) LE Master (C.2) +TSPC_L2CAP_1_4 True (#) LE Slave (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory IF BR/EDR or BR/EDR/LE is claimed, ELSE Excluded. +C.2: Mandatory to support (at least one of TSPC_L2CAP_1_3 or TSPC_L2CAP_1_4) + IF LE or BR/EDR/LE claimed, ELSE Excluded. +------------------------------------------------------------------------------- + + + General Operation +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_L2CAP_2_1 True Support of L2CAP signaling channel (C.20) +TSPC_L2CAP_2_2 True Support of configuration process (C.20) +TSPC_L2CAP_2_4 True Support of command echo request (C.21) +TSPC_L2CAP_2_3 True Support of connection oriented data + channel (C.20) +TSPC_L2CAP_2_5 True Support of command echo response (C.20) +TSPC_L2CAP_2_6 True (*) Support of command information request (C.21) +TSPC_L2CAP_2_7 True Support of command information response (C.20) +TSPC_L2CAP_2_8 False Support of a channel group (C.21) +TSPC_L2CAP_2_9 False Support of packet for connectionless + channel (C.21) +TSPC_L2CAP_2_10 False Support retransmission mode (C.21) +TSPC_L2CAP_2_11 False Support flow control mode(C.21) +TSPC_L2CAP_2_12 True (*) Enhanced Retransmission Mode (C.1, C.13) +TSPC_L2CAP_2_13 True (*) Streaming Mode (C.1, C.14) +TSPC_L2CAP_2_14 True (*) FCS Option (C.2) +TSPC_L2CAP_2_15 True (*) Generate Local Busy Condition (C.3) +TSPC_L2CAP_2_16 False Send Reject (C.3) +TSPC_L2CAP_2_17 True (*) Send Selective Reject (C.3) +TSPC_L2CAP_2_18 True (*) Mandatory use of ERTM (C.4) +TSPC_L2CAP_2_19 True (*) Mandatory use of Streaming Mode (C.5) +TSPC_L2CAP_2_20 True (*) Optional use of ERTM (C.4) +TSPC_L2CAP_2_21 True (*) Optional use of Streaming Mode (C.5) +TSPC_L2CAP_2_22 True (*) Send data using SAR in ERTM (C.6) +TSPC_L2CAP_2_23 True (*) Send data using SAR in Streaming Mode (C.7) +TSPC_L2CAP_2_24 True (*) Actively request Basic Mode for a PSM that + supports the use of ERTM or Streaming + Mode (C.8) +TSPC_L2CAP_2_25 True (*) Supports performing L2CAP channel mode + configuration fallback from SM + to ERTM (C.9) +TSPC_L2CAP_2_26 True (*) Supports sending more than one unacknowledged + I-Frame when operating in ERTM (C.10) +TSPC_L2CAP_2_27 True (*) Supports sending more than three unacknowledged + I-Frame when operating in ERTM (C.10) +TSPC_L2CAP_2_28 True (*) Supports configuring the peer TxWindow + greater than 1 (C.11) +TSPC_L2CAP_2_29 False AMP Support (C.12) +TSPC_L2CAP_2_30 True (*) Fixed Channel Support (C.12) +TSPC_L2CAP_2_31 False AMP Manager Support (C.12) +TSPC_L2CAP_2_32 False ERTM over AMP (C.12) +TSPC_L2CAP_2_33 False Streaming Mode Source over AMP Support (C.15) +TSPC_L2CAP_2_34 False Streaming Mode Sink over AMP Support (C.15) +TSPC_L2CAP_2_35 True (*) Unicast Connectionless Data, Reception + (C.1, C.16) +TSPC_L2CAP_2_36 True (*) Ability to transmit an unencrypted packet over + a Unicast connectionless L2CAP + channel (C.16) +TSPC_L2CAP_2_37 True (*) Ability to transmit an encrypted packet over + a Unicast connectionless L2CAP + channel (C.16) +TSPC_L2CAP_2_38 False Extended Flow Specification for BR/EDR (C.8) +TSPC_L2CAP_2_39 False Extended Window Size (C.8) +TSPC_L2CAP_2_40 True (*) Support of Low Energy signaling channel (C.17) +TSPC_L2CAP_2_41 True (*) Support of command reject (C.17) +TSPC_L2CAP_2_42 True (*) Send Connection Parameter Update Request (C.18) +TSPC_L2CAP_2_43 True (*) Send Connection Parameter Update Response (C.19) +TSPC_L2CAP_2_44 False Extended Flow Specification for AMP (C.22) +TSPC_L2CAP_2_45 True (*) Send disconnect request command (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 OR + TSPC_L2CAP_2_35 IF BR/EDR BR/EDR/LE AND SUM_ICS 31/7 (CSA1) OR + SUM_ICS 31/8 (3.0) OR SUM_ICS 31/9 (3.0+HS) OR SUM_ICS 31/10 (4.0)) + is supported, ELSE Excluded +C.2: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded. +C.3: Optional IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_28 is claimed, ELSE Excluded. +C.4: IF TSPC_L2CAP_2_12 is claimed THEN either TSPC_L2CAP_2_18 + OR TSPC_L2CAP_2_20 are Mandatory, ELSE Excluded. +C.5: IF TSPC_L2CAP_2_13 is claimed THEN either TSPC_L2CAP_2_19 + OR TSPC_L2CAP_2_21 are Mandatory, ELSE Excluded. +C.6: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. +C.7: Optional IF TSPC_L2CAP_2_13 is claimed, ELSE Excluded. +C.8: Optional IF TSPC_L2CAP_2_12 OR TSPC_L2CAP_2_13 is claimed, ELSE Excluded. +C.9: Mandatory IF TSPC_L2CAP_2_12 AND TSPC_L2CAP_2_13 AND TSPC_L2CAP_2_21 + is claimed, ELSE Excluded. +C.10: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. +C.11: Optional IF TSPC_L2CAP_2_12 is claimed, ELSE Excluded. +C.12: Mandatory IF SUM_ICS 31/9 (3.0 + HS) is claimed, ELSE Optional. +C.13: Mandatory IF SUM_ICS 31/9 (3.0 + HS) is claimed, ELSE Optional. +C.14: Optional IF SUM_ICS 31/8 OR 31/9 OR 31/10 OR 31/11 is claimed, + ELSE Excluded. +C.15: Optional IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded. +C.16: Optional IF (SUM_ICS 31/8 OR SUM_ICS 31/9 OR 31/10 OR 31/11) is claimed, + ELSE Excluded. +C.17: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded. +C.18: Optional IF (SUM_ICS 31/10 AND 1/4) is claimed, ELSE Excluded. +C.19: Mandatory IF (SUM_ICS 31/10 AND 1/3) is claimed, ELSE Excluded. +C.20: Mandatory IF LE OR BR/EDR/LE, is claimed, ELSE Excluded +C.21: Optional IF LE OR BR/EDR/LE, is claimed, ELSE Excluded +C.22: Mandatory IF TSPC_L2CAP_2_29 is claimed, ELSE Excluded. +------------------------------------------------------------------------------- + + + Configurable Parameters +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_L2CAP_3_1 True Support of RTX timer (M) +TSPC_L2CAP_3_2 True Support of ERTX timer (C.4) +TSPC_L2CAP_3_3 True Support minimum MTU size 48 octets (C.4) +TSPC_L2CAP_3_4 True (*) Support MTU size larger than 48 octets (C.5) +TSPC_L2CAP_3_5 True Support of flush timeout value for reliable + channel (C.4) +TSPC_L2CAP_3_6 False Support of flush timeout value for unreliable + channel (C.5) +TSPC_L2CAP_3_7 False Support of bi-directional quality of service + (QoS) option field (C.1) +TSPC_L2CAP_3_8 False Negotiate QoS service type (C.5) +TSPC_L2CAP_3_9 False Negotiate and support service type ‘No + traffic’ (C.2) +TSPC_L2CAP_3_10 False Negotiate and support service type ‘Best + effort’ (C.3) +TSPC_L2CAP_3_11 False Negotiate and support service type + ‘Guaranteed’ (C.2) +TSPC_L2CAP_3_12 True (*) Support minimum MTU size 23 octets (C.6) +TSPC_L2CAP_3_13 False Negotiate and support service type ‘No traffic’ + for Extended Flow Specification (C.7) +TSPC_L2CAP_3_14 False Negotiate and support service type ‘Best Effort' + for Extended Flow Specification (C.8) +TSPC_L2CAP_3_15 False Negotiate and support service type ‘Guaranteed’ + for Extended Flow Specification (C.9) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Optional. +C.2: Optional if TSPC_L2CAP_3_8 is supported, ELSE Excluded. +C.3: Mandatory if TSPC_L2CAP_3_8 is supported, ELSE Excluded. +C.4: Mandatory IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded. +C.5: Optional IF BR/EDR OR BR/EDR/LE is claimed, ELSE Excluded. +C.6: Mandatory IF LE OR BR/EDR/LE is claimed, ELSE Excluded. +C.7: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded. +C.8: Mandatory if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, + ELSE Excluded. +C.9: Optional if TSPC_L2CAP_2_44 OR TSPC_L2CAP_2_38 is supported, ELSE Excluded. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-map.txt bluez-5.23/android/pics-map.txt --- bluez-4.101/android/pics-map.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-map.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,175 @@ +MAP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_0_1 False Role: Map 1.0 (C1) +TSPC_MAP_0_2 True (*) Role: Map 1.1 (C1) +TSPC_MAP_0_3 False Role: Map 1.2 (C1) +------------------------------------------------------------------------------- +C.1: Mandatory to support only one Profile version. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_1_1 True (*) Role: Messaging Server Equipment (C1) +TSPC_MAP_1_2 False Role: Messaging Client Equipment (C1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Supported features MCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_2_1 False MCE: Message Notification (C1) +TSPC_MAP_2_1a False MCE: SendEvent (C4) +TSPC_MAP_2_2 False MCE: Message Browsing (C1) +TSPC_MAP_2_2a False MCE: SetFolder (C5) +TSPC_MAP_2_2b False MCE: GetFoldersListing (C5) +TSPC_MAP_2_2c False MCE: GetMessagesListing (C5) +TSPC_MAP_2_2d False MCE: GetMessage (O) +TSPC_MAP_2_2e False MCE: SetMessageStatus (O) +TSPC_MAP_2_2f False MCE: UpdateInbox (O) +TSPC_MAP_2_2g False MCE: Filtering (O) +TSPC_MAP_2_2h False MCE: Multiple simultaneous MAS instances (O) +TSPC_MAP_2_3 False MCE: Message Uploading (O) +TSPC_MAP_2_3a False MCE: SetFolder (C6) +TSPC_MAP_2_3b False MCE: GetFoldersListing (C6) +TSPC_MAP_2_3c False MCE: PushMessage (C6) +TSPC_MAP_2_4 False MCE: Message Delete (O) +TSPC_MAP_2_4a False MCE: SetMessageStatus (C7) +TSPC_MAP_2_5 False MCE: Notification Registration (C2) +TSPC_MAP_2_5a False MCE: SetNotificationRegistration off (O) +TSPC_MAP_2_5b False MCE: SetNotificationRegistration on (C8) +TSPC_MAP_2_6 False MCE: Supported Message Types +TSPC_MAP_2_6a False (*) MCE: EMAIL (C3) +TSPC_MAP_2_6b False (*) MCE: SMS_GSM (C3) +TSPC_MAP_2_6c False (*) MCE: SMS_CDMA (C3) +TSPC_MAP_2_6d False (*) MCE: MMS (C3) +TSPC_MAP_2_7 False MCE: Instance Information (Not Supported) +TSPC_MAP_2_7a False (*) MCE: GetMASInstanceInformation (Not Supported) +TSPC_MAP_2_8 False MCE: Extended MAP-Event-Report (Not Supported) +TSPC_MAP_2_8a False (*) MCE: MAP-Event-Report: Version 1.1 + (Not Supported) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined features TSPC_MAP_2_1 or + TSPC_MAP_2_2. +C.2: Mandatory to support TSPC_MAP_2_5 if TSPC_MAP_2_1 is supported. +C.3: Mandatory to support at least one of the defined message types + TSPC_MAP_2_6a to TSPC_MAP_2_6d IF TSPC_MAP_2_2 or TSPC_MAP_2_3 is + supported. +C.4: Support of functionality TSPC_MAP_2_1a mandatory IF related feature + TSPC_MAP_2_1 supported. +C.5: Support of functionality mandatory IF TSPC_MAP_2_2 supported. +C.6: Support of functionality mandatory IF TSPC_MAP_2_3 supported. +C.7: Support of functionality mandatory IF TSPC_MAP_2_4 supported. +C.8: Mandatory to support IF TSPC_MAP_2_5 (Notification Registration) is + supported, otherwise excluded. +C.9: Optional to support IF TSPC_MAP_0_3 (MAP v1.2) is supported, otherwise + excluded. +C.10: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) and TSPC_MAP_2_1 + (Message Notification) is supported, otherwise excluded. +------------------------------------------------------------------------------- + + + Supported features MSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_3_1 True MSE: Message Notification (M) +TSPC_MAP_3_1a True MSE: SendEvent (M) +TSPC_MAP_3_2 True MSE: Message Browsing (M) +TSPC_MAP_3_2a True MSE: SetFolder (M) +TSPC_MAP_3_2b True MSE: GetFoldersListing (M) +TSPC_MAP_3_2c True MSE: GetMessagesListing (M) +TSPC_MAP_3_2d True MSE: GetMessage (M) +TSPC_MAP_3_2e True MSE: SetMessageStatus (M) +TSPC_MAP_3_2f True MSE: UpdateInbox (M) +TSPC_MAP_3_2g False MSE: Multiple simultaneous MAS instances (O) +TSPC_MAP_3_3 True MSE: Message Uploading (M) +TSPC_MAP_3_3a True MSE: SetFolder (M) +TSPC_MAP_3_3b True MSE: GetFoldersListing (M) +TSPC_MAP_3_3c True MSE: PushMessage (M) +TSPC_MAP_3_4 True MSE: Message Delete (M) +TSPC_MAP_3_4a True MSE: SetMessageStatus (M) +TSPC_MAP_3_5 True MSE: Notification Registration (M) +TSPC_MAP_3_5a True MSE: SetNotificationRegistration (M) +TSPC_MAP_3_6 False MSE: Supported Message Types +TSPC_MAP_3_6a False MSE: EMAIL (C1) +TSPC_MAP_3_6b True MSE: SMS_GSM (C1) +TSPC_MAP_3_6c False MSE: SMS_CDMA (C1) +TSPC_MAP_3_6d False (*) MSE: MMS (C1) +TSPC_MAP_3_7 False MSE: Instance Information (Not Supported) +TSPC_MAP_3_7a False (*) MSE: GetMASInstanceInformation (Not Supported) +TSPC_MAP_3_8 False MSE: Extended MAP-Event-Report (Not Supported) +TSPC_MAP_3_8a False (*) MSE: MAP-Event-Report: Version 1.1 + (Not Supported) +------------------------------------------------------------------------------- +C.1: Mandatory to support at least one of the defined message types + TSPC_MAP_3_6a to TSPC_MAP_3_6d IF TSPC_MAP_3_2 or TSPC_MAP_3_3 + is supported. +C.2: Mandatory to support IF TSPC_MAP_0_3 (MAP v1.2) is supported, + otherwise excluded. +------------------------------------------------------------------------------- + + + GOEP v2.0 or later Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_7b_1 False GOEP v2.0 or later (C1) +TSPC_MAP_7b_2 False GOEP v2 Backwards Compatibility (C1) +TSPC_MAP_7b_3 False OBEX over L2CAP (C1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. +------------------------------------------------------------------------------- + + + MCE OBEX Header Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_10_1 False (*) Name (M) +TSPC_MAP_10_2 False (*) Typr (M) +TSPC_MAP_10_3 False (*) Body (M) +TSPC_MAP_10_4 False (*) End of Body (M) +TSPC_MAP_10_5 False (*) Target (M) +TSPC_MAP_10_6 False (*) Who (M) +TSPC_MAP_10_7 False (*) Connection ID (M) +TSPC_MAP_10_8 False (*) Application Parameters (M) +TSPC_MAP_10_9 False SRM (C2) +TSPC_MAP_10_10 False Receive SRMP (C2) +TSPC_MAP_10_11 False Send SRMP (C2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. +C.2: Optional if TSPC_MAP_0_3 (MAP v1.2) is supported else excluded. +------------------------------------------------------------------------------- + + + GetMessagesListing Filtering Parameter Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MAP_20_1 False (*) MCE: FilterMessageType (O) +TSPC_MAP_20_2 False (*) MCE: FilterPeriodBegin (O) +TSPC_MAP_20_3 False (*) MCE: FilterPeriodEnd (O) +TSPC_MAP_20_4 False (*) MCE: FilterReadStatus (O) +TSPC_MAP_20_5 False (*) MCE: FilterRecipient (O) +TSPC_MAP_20_6 False (*) MCE: FilterOriginator (O) +TSPC_MAP_20_7 False (*) MCE: FilterPriority (O) +TSPC_ALL False (*) Turns on all the test cases +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-mcap.txt bluez-5.23/android/pics-mcap.txt --- bluez-4.101/android/pics-mcap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-mcap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,141 @@ +MCAP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Protocols +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_1_A_1 True (*) Supports Standard Op Codes (C.1) +TSPC_MCAP_1_A_2 False Supports Clock Synchronization Protocol (C.1) +------------------------------------------------------------------------------- +C.1: Support for at least one of the defined protocols is Mandatory. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_1_1 True (*) Supports Source Role (C.1) +TSPC_MCAP_1_2 True (*) Supports Sink Role (C.1) +TSPC_MCAP_1_3 False Supports Sync-Slave Role (C.2) +TSPC_MCAP_1_4 False Supports Sync-Master Role (C.3) +------------------------------------------------------------------------------- +C.1: If support for TSPC_MCAP_1_A_1 is supported, at least one of the + defined roles is Mandatory otherwise Excluded. +C.2: Mandatory if TSPC_MCAP_1_A_2 is supported, otherwise Excluded. +C.3: Optional if TSPC_MCAP_1_A_2 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + L2CAP Features - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_2_1 True (*) Supports L2CAP Control Channel (M) +TSPC_MCAP_2_2 True (*) Supports at least one L2CAP Data Channel (M) +TSPC_MCAP_2_3 True (*) Maximum number of simultaneous L2CAP Data + Channels supported (DCmax) per MCL (M) +TSPC_MCAP_2_4 False Can support multiple simultaneous MCLs with + Standard Op Codes (O) +------------------------------------------------------------------------------- + + + Connection Management - Source +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_3_1 This row inentionally left blank +TSPC_MCAP_3_2 True (*) Initiate creation of Control and Data Channels + (C.1) +TSPC_MCAP_3_3 True (*) Accept creation of Control and Data Channels + (C.1) +TSPC_MCAP_3_4 True (*) Initiate Disconnection of MCL (M) +TSPC_MCAP_3_5 True (*) Accept Disconnection of MCL (M) +TSPC_MCAP_3_6 True (*) Initiate Disconnection of MDL (M) +TSPC_MCAP_3_7 True (*) Accept Disconnection of MDL (M) +TSPC_MCAP_3_8 False Initiate Reconnection of MDL (O) +TSPC_MCAP_3_9 False Accept Reconnection of MDL (C.2) +TSPC_MCAP_3_10 False Initiate Deletion of MDL (O) +TSPC_MCAP_3_11 True (*) Accept Deletion of MDL (M) +TSPC_MCAP_3_12 False Initiate Delete of All MDLs using 0xFFFF (O) +TSPC_MCAP_3_13 True (*) Accept Delete of All MDLs using 0xFFFF (M) +TSPC_MCAP_3_14 False Send MDL Abort request (O) +TSPC_MCAP_3_15 True (*) Accept MDL Abort request (M) +------------------------------------------------------------------------------- +C.1: Support for at least one of TSPC_MCAP_3_2 or TSPC_MCAP_3_3 is Mandatory. +C.2: Mandatory if TSPC_MCAP_3_3 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + L2CAP Features - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_4_1 True (*) Supports L2CAP Control Channel (M) +TSPC_MCAP_4_2 True (*) Supports at least one L2CAP Data Channel (M) +TSPC_MCAP_4_3 True (*) Maximum number of simultaneous L2CAP Data + Channels supported (DCmax) per MCL (M) +TSPC_MCAP_4_4 False Can support multiple simultaneous MCLs with + Standard Op Codes (O) +------------------------------------------------------------------------------- + + + Connection Management - Sink +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_5_1 This row intentionally left blank +TSPC_MCAP_5_2 True (*) Initiate creation of Control and Data Channels + (M) +TSPC_MCAP_5_3 True (*) Accept creation of Control and Data Channels + (M) +TSPC_MCAP_5_4 True (*) Initiate Disconnection of MCL (M) +TSPC_MCAP_5_5 True (*) Accept Disconnection of MCL (M) +TSPC_MCAP_5_6 True (*) Initiate Disconnection of MDL (M) +TSPC_MCAP_5_7 True (*) Accept Disconnection of MDL (M) +TSPC_MCAP_5_8 False Initiate Reconnection of MDL (O) +TSPC_MCAP_5_9 True (*) Accept Reconnection of MDL (M) +TSPC_MCAP_5_10 False Initiate Deletion of MDL (O) +TSPC_MCAP_5_11 True (*) Accept Deletion of MDL (M) +TSPC_MCAP_5_12 False Initiate Delete of All MDLs using 0xFFFF (O) +TSPC_MCAP_5_13 True (*) Accept Delete of All MDLs using 0xFFFF (M) +TSPC_MCAP_5_14 False Send MDL Abort request (O) +TSPC_MCAP_5_15 True (*) Accept MDL Abort request (M) +------------------------------------------------------------------------------- + + + Clock Synchronization Features - Sync-Slave +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_6_1 False (*) Accept MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ and + Initiate MD_SYNC_INFO_IND (C.1) +TSPC_MCAP_6_2 This row intentionally left blank +TSPC_MCAP_6_3 False Can support multiple simultaneous MCLs with CSP + (O) +TSPC_MCAP_6_4 False Can access Bluetooth Clock (O) +------------------------------------------------------------------------------- +C.1: Mandatory if MCAP TSPC_MCAP_1_A_2 is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Clock Synchronization Features - Sync-Master +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MCAP_7_1 This row intentionally left blank +TSPC_MCAP_7_2 False (*) Initiate MD_SYNC_CAP_REQ and MD_SYNC_SET_REQ + and Accept MD_SYNC_INFO_IND (C.1) +TSPC_MCAP_7_3 False Can support multiple simultaneous MCLs with CSP (O) +TSPC_MCAP_7_4 False (*) Can access Bluetooth Clock (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support IF TSPC_MCAP_1_A_2 is supported. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-mps.txt bluez-5.23/android/pics-mps.txt --- bluez-4.101/android/pics-mps.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-mps.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,339 @@ +MPS PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Profile Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_0_1 True MPS v1.0 (M) +------------------------------------------------------------------------------- + + + Profile Version Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_1_1 True (*) A2DP 1.2 or later (O) +TSPC_MPS_1_2 True (*) AVRCP 1.3 or later (O) +TSPC_MPS_1_3 False DUN 1.1 or later (O) +TSPC_MPS_1_4 True (*) HFP 1.5 or later (O) +TSPC_MPS_1_5 True (*) PAN 1.0 or later (O) +TSPC_MPS_1_6 True (*) PBAP 1.1 or later (O) +------------------------------------------------------------------------------- + + + Profile Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_2_1 True (*) A2DP Source (SRC) (C.1) +TSPC_MPS_2_2 False A2DP Sink (SNK) (C.1) +TSPC_MPS_2_3 True (*) AVRCP Controller (CT) (C.1) +TSPC_MPS_2_4 True (*) AVRCP Target (TG) (C.1) +TSPC_MPS_2_5 False DUN Gateway (GW) (C.1) +TSPC_MPS_2_6 False DUN Data Terminal (DT) (C.1) +TSPC_MPS_2_7 True (*) HFP Audio Gateway (AG) (C.1) +TSPC_MPS_2_8 False HFP Hands-Free (HF) (C.1) +TSPC_MPS_2_9 True (*) PAN Network Access Point (NAP) (C.1) +TSPC_MPS_2_10 False PAN Group Ad-hoc Network (GN) (C.1) +TSPC_MPS_2_11 True (*) PAN User (PANU) (C.1) +TSPC_MPS_2_12 False PBAP PCE (C.1) +TSPC_MPS_2_13 True (*) PBAP PSE (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to declare each role as supported within the represented Profile + otherwise Excluded. The roles declared shall match that of the roles + supported within the Profile. +------------------------------------------------------------------------------- + + + Profile Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_3_1 True (*) Receiving PASS THROUGH command in Category 1 + (AVRCP - TG) (C.1) +TSPC_MPS_3_2 True (*) Receiving PASS THROUGH command in Category 1 + (AVRCP - TG) - PAUSE (C.1) +TSPC_MPS_3_3 False Sending PASS THROUGH command in Category 1 + (AVRCP - CT) - PLAY (C.2) +TSPC_MPS_3_4 False Sending PASS THROUGH command in Category 1 + (AVRCP - CT) - PAUSE (C.2) +TSPC_MPS_3_5 True (*) Transfer Control - Suspend (GAVDP - Initiator) + (C.3) +TSPC_MPS_3_6 True (*) Transfer Control - Suspend (GAVDP - Acceptor) + (C.4) +TSPC_MPS_3_7 False Accept an incoming voice call (in-band ring) + (C.5) +TSPC_MPS_3_8 True (*) Accept an incoming voice call (no in-band ring) + (C.5) +TSPC_MPS_3_9 False Place a call with a phone number supplied by + the HF (C.6) +TSPC_MPS_3_10 True (*) Register Notification: PLAYBACK_STATUS_CHANGED + (C.7) +TSPC_MPS_3_11 True (*) Ability to support parallel data and call + operation (O) +TSPC_MPS_3_12 True (*) PBAP Phone Book Download (C.8) +TSPC_MPS_3_13 True (*) Ability to support multiple concurrent device + connections (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_MPS_2_1 (A2DP Source role) and TSPC_MPS_2_4 (AVRCP + Target role) are supported, otherwise Excluded. +C.2: Mandatory if TSPC_MPS_2_2 (A2DP Sink role) and TSPC_MPS_2_3 (AVRCP + Controller role) are supported, otherwise Excluded. +C.3: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source + role) are supported; Optional if TSPC_MPS_1_4 (HFP 1.5 or later) and + TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise Excluded. +C.4: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and TSPC_MPS_2_1 (A2DP Source + role) or TSPC_MPS_2_2 (A2DP Sink role) are supported, otherwise + Excluded. +C.5: Mandatory to support at least one if TSPC_MPS_1_4 (HFP 1.5 or later) is + supported, otherwise Excluded. +C.6: Mandatory if TSPC_MPS_1_4 (HFP 1.5 or later) and HFP 3/8 (Place a call with + a phone number supplied by the HF) are supported, otherwise Excluded. +C.7: Mandatory if TSPC_MPS_2_3 (AVRCP Controller role) is supported, otherwise + Excluded. +C.8: Mandatory if TSPC_MPS_1_6 (PBAP 1.1 or later) and PBAP 2/1 (Phone Book + Download) are supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Device Capability Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_4_1 True Multiple Profiles Single Device (MPSD) (M) +TSPC_MPS_4_2 True (*) Multiple Profiles Multiple Devices (MPMD) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_MPS_3_13 (Ability to support multiple concurrent device + connections), otherwise Excluded. +------------------------------------------------------------------------------- + + + MPSD scenarios +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_6_1 True (*) HFP-AG and A2DP-SRC Implementation Answer + Incoming Call during Audio Streaming (C.1) +TSPC_MPS_6_2 False HFP-HF and A2DP-SNK Implementation Answer + Incoming Call during Audio Streaming (C.2) +TSPC_MPS_6_3 True (*) HFP-AG and A2DP-SRC Implementation Outgoing + Call during Audio Streaming (C.1) +TSPC_MPS_6_4 False HFP-HF and A2DP-SNK Implementation Outgoing + Call during Audio Streaming (C.2) +TSPC_MPS_6_5 True (*) HFP-AG and A2DP-SRC Implementation Reject/Ignore + Incoming Call during Audio Streaming (C.1) +TSPC_MPS_6_6 False HFP-HF and A2DP-SNK Implementation Reject/Ignore + Incoming Call during Audio Streaming (C.2) +TSPC_MPS_6_7 True (*) HFP-AG and A2DP-SRC Implementation HFP Call + Termination during AVP Connection (C.1) +TSPC_MPS_6_8 False HFP-HF and A2DP-SNK Implementation HFP Call + Termination during AVP Connection (C.2) +TSPC_MPS_6_9 True (*) HFP-AG and A2DP-SRC Implementation Press Play + on Audio Player during Active Call (C.1) +TSPC_MPS_6_10 False HFP-HF and A2DP-SNK Implementation Press Play + on Audio Player during Active Call (C.2) +TSPC_MPS_6_11 True (*) HFP-AG and A2DP-SRC Implementation Start Audio + Streaming after AVRCP Play Command (C.1) +TSPC_MPS_6_12 False HFP-HF and A2DP-SNK Implementation Start Audio + Streaming after AVRCP Play Command (C.2) +TSPC_MPS_6_13 True (*) HFP-AG and A2DP-SRC Implementation Suspend Audio + Streaming after AVRCP Pause/Stop (C.1) +TSPC_MPS_6_14 False HFP-HF and A2DP-SNK Implementation Suspend Audio + Streaming after AVRCP Pause/Stop (C.2) +TSPC_MPS_6_15 False HFP-AG and DUN-GW Implementation Data + Communication under PSDM (DUN) during Active + Voice Call (C.3) +TSPC_MPS_6_16 False HFP-HF and DUN-DT Implementation Data + Communication under PSDM (DUN) during Active + Voice call (C.4) +TSPC_MPS_6_17 False HFP-AG and DUN-GW Implementation Outgoing Voice + Call during Data Communication under PSDM (DUN) + (C.3) +TSPC_MPS_6_18 False HFP-HF and DUN-DT Implementation Outgoing Voice + Call during Data Communication under PSDM (DUN) + (C.4) +TSPC_MPS_6_19 False HFP-AG and DUN-GW Implementation Incoming Voice + Call during Data Communication under PSDM (DUN) + (C.3) +TSPC_MPS_6_20 False HFP-HF and DUN-DT Implementation Incoming Voice + Call during Data Communication under PSDM (DUN) + (C.4) +TSPC_MPS_6_21 False A2DP-SRC and DUN-GW Implementation Start Audio + Streaming during Data Communication under PSDM + (DUN) (C.5) +TSPC_MPS_6_22 False A2DP-SNK and DUN-DT Implementation Start Audio + Streaming during Data Communication under PSDM + (DUN) (C.6) +TSPC_MPS_6_23 False A2DP-SRC and DUN-GW Implementation Data + Communication Establishment under PSDM (DUN) + during Audio Streaming (C.5) +TSPC_MPS_6_24 False A2DP-SNK and DUN-DT Implementation Data + Communication Establishment under PSDM (DUN) + during Audio Streaming (C.6) +TSPC_MPS_6_25 False HFP-AG and DUN-GW Implementation Terminate + Voice Call/Data Call during Data Communication + and Voice Call (C.5) +TSPC_MPS_6_26 False HFP-HF and DUN-DT Implementation Terminate + Voice Call/Data Call during Data Communication + and Voice Call (C.6) +TSPC_MPS_6_27 True (*) HFP-AG and PAN-NAP Implementation Data + Communication in Personal Area Network during + Active Voice Call (C.7) +TSPC_MPS_6_28 False HFP-HF and PAN-PANU Implementation Data + Communication in Personal Area Network during + Active Voice Call (C.8) +TSPC_MPS_6_29 True (*) HFP-AG and PAN-NAP Implementation Outgoing + Voice Call during Data Communication in Personal + Area Network (C.7) +TSPC_MPS_6_30 False HFP-HF and PAN-PANU Implementation Outgoing + Voice Call during Data Communication in Personal + Area Network (C.8) +TSPC_MPS_6_31 True (*) HFP-AG and PAN-NAP Implementation Incoming Voice + Call during Data Communication in Personal Area + Network (C.7) +TSPC_MPS_6_32 False HFP-HF and PAN-PANU Implementation Incoming + Voice Call during Data Communication in Personal + Area Network (C.8) +TSPC_MPS_6_33 True (*) A2DP-SRC and PAN-NAP Implementation Start Audio + Streaming during Data Communication in Personal + Area Network (C.9) +TSPC_MPS_6_34 False A2DP-SNK and PAN-PANU Implementation Start Audio + Streaming during Data Communication in Personal + Area Network (C.10) +TSPC_MPS_6_35 True (*) A2DP-SRC and PAN-NAP Implementation Data + Communication Establishment in Personal Area + Network during Audio Streaming (C.9) +TSPC_MPS_6_36 False A2DP-SNK and PAN_PANU Implementation Data + Communication Establishment in Personal Area + Network during Audio Streaming (C.10) +TSPC_MPS_6_37 True (*) A2DP-SRC_PBAP-Server Implementation Phonebook + Download during Audio Streaming (C.11) +TSPC_MPS_6_38 False A2DP-SNK and PBAP-Client Implementation + Phonebook Download during Audio Streaming (C.12) +TSPC_MPS_6_39 True (*) HFP-AG and PBAP-Server Implementation PBAP and + HFP Connection Behavior (C.13) +------------------------------------------------------------------------------- +C.1: Mandatory if 2/1 (A2DP Source role), 2/4 (AVRCP Target role) and 2/7 + (HFP Audio Gateway role) are supported, otherwise Excluded. +C.2: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/8 + (HFP Hands-Free role) are supported, otherwise Excluded. +C.3: Mandatory if 2/5 (DUN Gateway role) and 2/7 (HFP Audio Gateway role) + are supported and 3/9 ( Ability to support parallel data and call + operation), otherwise Excluded. +C.4: Mandatory if 2/6 (DUN Data Terminal role) and 2/8 (HFP Hands-Free role) + are supported, otherwise Excluded. +C.5: Mandatory if 2/1 (A2DP Source role) and 2/5 (DUN Gateway role) are + supported, otherwise Excluded. +C.6: Mandatory if 2/2 (A2DP Sink role) and 2/6 (DUN Data Terminal role) are + supported, otherwise Excluded. +C.7: Mandatory if 2/7 (HFP Audio Gateway role) and 2/9 (PAN Network Access Point + role) and 3/11 (Ability to support parallel data and call operation) are + supported, otherwise Excluded. +C.8: Mandatory if 2/8 (HFP Hands-Free role) and 2/11 (PAN User role) are + supported and 3/11, otherwise Excluded. +C.9: Mandatory if 2/1 (A2DP Source role) and 2/9 (PAN Network Access Point role) + are supported, otherwise Excluded. +C.10: Mandatory if 2/2 (A2DP Sink role) and 2/11 (PAN User role) are supported, + otherwise Excluded. +C.11: Mandatory if 2/1 (A2DP Source role) and 2/13 (PBAP PSE role) are + supported, otherwise Excluded. +C.12: Mandatory if 2/2 (A2DP Sink role) and 2/12 (PBAP PCE role) are supported, + otherwise Excluded. +C.13: Mandatory if 2/7 (HFP Audio Gateway role) and 2/13 (PBAP PSE role) are + supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + MPMD Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_7_1 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + Answer Incoming Call during Audio Streaming + (C.1) +TSPC_MPS_7_2 True (*) A2DP-SRC and AVRCP-TG Implementation Answer + Incoming Call during Audio Streaming (C.2) +TSPC_MPS_7_3 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + Outgoing Call during Audio Streaming (C.1) +TSPC_MPS_7_4 True (*) A2DP-SRC and AVRCP-TG Implementation Outgoing + Call during Audio Streaming (C.2) +TSPC_MPS_7_5 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + Reject/Ignore Incoming Call during Audio + Streaming (C.1) +TSPC_MPS_7_6 True (*) A2DP-SRC and AVRCP-TG Implementation + Reject/Ignore Incoming Call during Audio + Streaming (C.2) +TSPC_MPS_7_7 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + HFP Call Termination during AVP Connection (C.1) +TSPC_MPS_7_8 True (*) A2DP-SRC and AVRCP-TG Implementation HFP Call + Termination during AVP Connection (C.2) +TSPC_MPS_7_9 False HFP-HF and A2DP-SNK and AVRCP-CT Implementation + Press Play on Audio Player during Active Call + (C.1) +TSPC_MPS_7_10 True (*) A2DP-SRC and AVRCP-TG Implementation Press Play + on Audio Player during Active Call (C.2) +TSPC_MPS_7_11 True (*) A2DP-SRC and AVRCP-TG Implementation Start Audio + Streaming during Data Communication under PSDM + (C.2) +TSPC_MPS_7_12 False A2DP-SNK and AVRCP-CT and DUN-DT Implementation + Start Audio Streaming during Data Communication + under PSDM (C.3) +TSPC_MPS_7_13 True (*) A2DP-SRC and AVRCP-TG Implementation Start + Packet Data Communication during Audio Streaming + (C.2) +TSPC_MPS_7_14 False A2DP-SNK and AVRCP-CT and DUN-DT Implementation + Start Packet Data Communication during Audio + Streaming (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/8 + (HFP Hands-Free role) are supported, otherwise Excluded. +C.2: Mandatory if 2/1 (A2DP Source role) and 2/4 (AVRCP Target role) are + supported, otherwise Excluded. +C.3: Mandatory if 2/2 (A2DP Sink role), 2/3 (AVRCP Controller role) and 2/6 + (DUN Data Terminal role) supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + MPS Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_8_1 True (*) AVP Suspension (C.1) +TSPC_MPS_8_2 True (*) Profile (Dis-)Connection behavior (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if 1/1 (A2DP 1.2 or later) and 1/2 (AVRCP 1.3 or later) are + supported, otherwise Excluded. +C.2: Mandatory if 1/1 (A2DP 1.2 or later), 1/2 (AVRCP 1.3 or later) and 1/4 (HFP + 1.5 or later) are supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + MPS Dependencies +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_9_1 True Implements Bluetooth Core Specification v2.1 + + EDR or later (M) +------------------------------------------------------------------------------- + + + MPS Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_MPS_10_1 True SDP Record (M) +TSPC_MPS_10_2 True (*) Media Stream Suspension (C.1) +TSPC_MPS_10_3 True (*) Sniff Mode during Streaming (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if 1/1 (A2DP1.2 or later) and 1/4 (HFP 1.5 or later) are + supported, otherwise Excluded. +C.2: Mandatory if 1/1 (A2DP 1.2 or later) is supported, otherwise Excluded. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-opp.txt bluez-5.23/android/pics-opp.txt --- bluez-4.101/android/pics-opp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-opp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,187 @@ +OPP PICS for the PTS tool. + +PTS version: 5.2 + +* - 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 True Client supports OPP version 1.1. (C.1) +TSPC_OPP_1b_2 False 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 False Client: Push multiple vCard objects using + different PUT operations. (C.5) +TSPC_OPP_2_9b True (*) Client: Push multiple vCard objects using the + same PUT operation. (C.5) +TSPC_OPP_2_10 False Client: Push multiple vCalender objects. (O) +TSPC_OPP_2_10a False 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 False Client: Push multiple vMsg objects. (O) +TSPC_OPP_2_11a False 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 False Client: Push multiple vNote objects. (O) +TSPC_OPP_2_12a False 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 False Client: Pull business card (O) +TSPC_OPP_2_14 False Client: vCard 2.1 (C.1) +TSPC_OPP_2_15 False Client: Exchange business card (O) +TSPC_OPP_2_16 False Client: vCard 2.1 (C.2) +TSPC_OPP_2_17 False GOEP v2 (C.9) +TSPC_OPP_2_18 False GOEP v2 Backward Compability (C.9) +TSPC_OPP_2_19 False 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 True Server supports OPP version 1.1. +TSPC_OPP_2b_2 False 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 False Server: Object Push vCal reject. (O) +TSPC_OPP_3_11 False Server: Object Push vMsg reject. (O) +TSPC_OPP_3_12 False Server: Object Push vNote reject. (O) +TSPC_OPP_3_13 False Server: Business card pull (O.1) +TSPC_OPP_3_14 False Server: vCard 2.1 (C.1) +TSPC_OPP_3_15 False Server: Business card pull reject. (O) +TSPC_OPP_3_16 False Server: Business card exchange (O.2) +TSPC_OPP_3_17 False Server: vCard 2.1 (C.2) +TSPC_OPP_3_18 False Server: Business card exchange reject. (O) +TSPC_OPP_3_19 False GOEP v2 (C.5) +TSPC_OPP_3_20 False GOEP v2 Backward Compability (C.5) +TSPC_OPP_3_21 False 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 False Multiple vCards transferred as a single vObject + (C.1) +TSPC_OPP_4_4 False Multiple vCards transfer (C.1) +TSPC_OPP_4_5 False vCards with multiple Phone Number Fields (C.1) +TSPC_OPP_4_6 False Push vCal to Different Time Zone Server (C.1) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_OPP_1_2 is supported, otherwise excluded. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-pan.txt bluez-5.23/android/pics-pan.txt --- bluez-4.101/android/pics-pan.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-pan.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,149 @@ +PAN PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PAN_1_1 True (*) Role: Network Access Point (O.1) +TSPC_PAN_1_2 False Role: Group Ad-hoc Network (O.1) +TSPC_PAN_1_3 True (*) Role: PAN User (O.1) +TSPC_PAN_1a_1 True BNEP: BNEP Connection Setup (M) +TSPC_PAN_1a_2 True BNEP: BNEP Data Packet Reception (M) +TSPC_PAN_1a_3 True BNEP: BNEP Data Packet Transmission (M) +TSPC_PAN_1a_4 True BNEP: BNEP Control Message Processing (M) +TSPC_PAN_1a_5 True BNEP: BNEP Extension Header Processing (M) +TSPC_PAN_1a_6 False BNEP: Network Protocol Filter Message + Transmission (O) +TSPC_PAN_1a_7 False BNEP: Multicast Address Filter Message + Transmission (O) +------------------------------------------------------------------------------- +O.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Network Access Point Application Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PAN_2_1 True NAP: Support BNEP (M) +TSPC_PAN_2_2 True NAP: Support BNEP Forwarding (M) +TSPC_PAN_2_3 False NAP: Support Layer 2-Bridging between PAN and + External Network (C.1) +TSPC_PAN_2_4 True (*) NAP: Support IP forwarding between PAN and + External Network (C.1) +TSPC_PAN_2_5 False NAP: Support BNEP Packet Filtering (O) +TSPC_PAN_2_6 False NAP: Support IPv4 (C.2) +TSPC_PAN_2_6a False NAP: Supports operable routable IPv4 address (O) +TSPC_PAN_2_6b False NAP: Support link-local address configuration + for IPv4 (C.4) +TSPC_PAN_2_7 False NAP: Support ping client for IPv4 (O) +TSPC_PAN_2_8 False NAP: Support DHCP Client for IPv4 (O) +TSPC_PAN_2_9 False NAP: Support DNS/LLMNR Resolver for IPv4 (O) +TSPC_PAN_2_9a False (*) NAP: Support LLMNR Sender for IPv4 (C.5) +TSPC_PAN_2_9b False NAP: Support LLMNR Responder for IPv4 (O) +TSPC_PAN_2_10 False NAP: Support HTTP Client for IPv4 (O) +TSPC_PAN_2_11 False NAP: Support WAP Client for IPv4 (O) +TSPC_PAN_2_12 False NAP: Support IPv6 (C.3) +TSPC_PAN_2_13 False NAP: Support ping client for IPv6 (O) +TSPC_PAN_2_14 False NAP: Support DNS/LLMNR Resolver for IPv6 (O) +TSPC_PAN_2_14a False (*) NAP: Support LLMNR Sender for IPv6 (C.6) +TSPC_PAN_2_14b False NAP: Support LLMNR Responder for IPv6 (O) +TSPC_PAN_2_15 False NAP: Support HTTP Client for IPv6 (O) +TSPC_PAN_2_16 False NAP: Support WAP Client for IPv6 (O) +TSPC_PAN_2_17 True NAP: Supports Connectable Mode (M) +TSPC_PAN_2_18 True NAP: NAP Service Record (M) +TSPC_PAN_2_19 False NAP: Support at least three PANUs (O) +TSPC_PAN_2_20 False NAP: Support at least two PANUs (O) +------------------------------------------------------------------------------- +Note that support for IP-related features only applies to the PAN interface of + the NAP (i.e. If the IP stack is accessible by PANUs). +C.1: Network Access Point devices MUST support either (TSPC_PAN_2_3) + OR (TSPC_PAN_2_4). +C.2: Mandatory to support IF any IPv4-based transport protocol OR + (TSPC_PAN_2_7-11) is supported, ELSE Optional. +C.3: Mandatory to support IF any IPv6-based transport protocol OR + (TSPC_PAN_2_13-16) is supported, ELSE Optional. +C.4: Mandatory if TSPC_PAN_2_6 is supported and TSPC_PAN_2_6a is not supported, + otherwise optional. +C.5: Mandatory if item (TSPC_PAN_2_6) supported. +C.6: Mandatory if item (TSPC_PAN_2_12) supported +------------------------------------------------------------------------------- + + + Group Ad-hoc Network Application Features + (GN Application Features) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PAN_3_1 False (*) GN: Support BNEP (M) +TSPC_PAN_3_2 False (*) GN: Support BNEP Forwarding (M) +TSPC_PAN_3_3 False GN: Support BNEP Packet Filtering (O) +TSPC_PAN_3_4 False GN: Support IPv4 (C.1) +TSPC_PAN_3_5 False GN: Support ping client for IPv4 (O) +TSPC_PAN_3_6 False GN: Support DHCP Client for IPv4 (O) +TSPC_PAN_3_7 False GN: Support DNS/LLMNR Resolver for IPv4 (O) +TSPC_PAN_3_7a False (*) GN: Support LLMNR Sender for IPv4 (C.3) +TSPC_PAN_3_7b False GN: Support LLMNR Responder for IPv4 (O) +TSPC_PAN_3_8 False GN: Support HTTP Client for IPv4 (O) +TSPC_PAN_3_9 False GN: Support WAP Client for IPv4 (O) +TSPC_PAN_3_10 False GN: Support IPv6 (C.2) +TSPC_PAN_3_11 False GN: Support ping client for IPv6 (O) +TSPC_PAN_3_12 False GN: Support DNS/LLMNR Resolver for IPv6 (O) +TSPC_PAN_3_12a False (*) GN: Support LLMNR Sender for IPv6 (C.4) +TSPC_PAN_3_12b False GN: Support LLMNR Responder for IPv6 (O) +TSPC_PAN_3_13 False GN: Support HTTP Client for IPv6 (O) +TSPC_PAN_3_14 False GN: Support WAP Client for IPv6 (O) +TSPC_PAN_3_15 False (*) GN: Supports Connectable Mode (M) +TSPC_PAN_3_16 False (*) GN: GN Service Record (M) +TSPC_PAN_3_17 False GN: Support at least three PANUs (O) +TSPC_PAN_3_18 False GN: Support at least two PANUs (O) +------------------------------------------------------------------------------- +C.1: Mandatory to support IF any IPv4-based transport protocol OR + (TSPC_PAN_3_5-9) is supported, ELSE Optional. +C.2: Mandatory to support IF any IPv6-based transport protocol OR + (TSPC_PAN_3_11-14) is supported, ELSE Optional. +C.3: Mandatory to support IF (TSPC_PAN_3_4) is supported. +C.4: Mandatory to support if (TSPC_PAN_3_10) is supported. +------------------------------------------------------------------------------- + + + PAN User Application Features +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PAN_4_1 True PANU: Support BNEP (M) +TSPC_PAN_4_2 True (*) PANU: Support IPv4 (C.1) +TSPC_PAN_4_3 False PANU: Support ping client for IPv4 (O) +TSPC_PAN_4_4 False PANU: Support DHCP client for IPv4 (O) +TSPC_PAN_4_5 False PANU: Support DNS/LLMNR Resolver for IPv4 (O) +TSPC_PAN_4_5a False (*) PANU: Support LLMNR Sender for IPv4 (C.2) + Reference: SE #3558, TSE #4382, TCW #448 +TSPC_PAN_4_5b False PANU: Support LLMNR Responder for IPv4 (O) +TSPC_PAN_4_6 False PANU: Support HTTP Client for IPv4 (O) +TSPC_PAN_4_7 False PANU: Support WAP Client for IPv4 (O) +TSPC_PAN_4_8 False PANU: Support IPv6 (C.1) +TSPC_PAN_4_9 False PANU: Support ping client for IPv6 (O) +TSPC_PAN_4_10 False PANU: Support DNS/LLMNR Resolver for IPv6 (O) +TSPC_PAN_4_10a False (*) PANU: Support LLMNR Sender for IPv6 (C.3) +TSPC_PAN_4_10b False PANU: Support LLMNR Responder for IPv6 (O) +TSPC_PAN_4_11 False PANU: Support HTTP Client for IPv6 (O) +TSPC_PAN_4_12 False PANU: Support WAP Client for IPv6 (O) +TSPC_PAN_4_13 False PANU: Support connections to multi-user + NAPs/GNs (O) +TSPC_PAN_4_14 False PANU: Supports Connectable Mode (O) +TSPC_PAN_4_15 False PANU: PANU Service Record (O) +TSPC_ALL False Turns on all the test cases +------------------------------------------------------------------------------- +C.1: PAN User devices must support at least One of items (TSPC_PAN_4_2) or + (TSPC_PAN_4_8). +C.2: Mandatory to support if (TSPC_PAN_4_2) is supported. +C.3: Mandatory to support if (TSPC_PAN_4_8) is supported. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-pbap.txt bluez-5.23/android/pics-pbap.txt --- bluez-4.101/android/pics-pbap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-pbap.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,442 @@ +PBAP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Major Profile Version (X.Y) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_0_1 False Role: PBAP 1.0 (C.1) +TSPC_PBAP_0_2 True (*) Role: PBAP 1.1 (C.1) +TSPC_PBAP_0_3 False Role: PBAP 1.2 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support one and only one major profile version. +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_1_1 False Role: PCE (C.1) +TSPC_PBAP_1_2 True (*) Role: PSE (C.1) +------------------------------------------------------------------------------- +C1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Supported features (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_2_1 False (*) PCE: Phone Book Download (C.1) +TSPC_PBAP_2_2 False (*) PCE: Phone Book Browsing (C.1) +TSPC_PBAP_2_3 False (*) PCE: Session Management (M) +TSPC_PBAP_2_4 False PCE: Able to Request Size of the Phonebook (O) +TSPC_PBAP_2_5 False PCE: Database Identifier (C.2) +TSPC_PBAP_2_6 False PCE: Folder Version Counters (C.2) +TSPC_PBAP_2_7 False PCE: vCard Selecting (C.2) +TSPC_PBAP_2_7a False PCE: Able to send vCardSelector (C.2) +TSPC_PBAP_2_7b False PCE: Able to send vCardSelectorOperator (C.2) +TSPC_PBAP_2_8 False (*) PCE: Enhanced Missed Calls (C.4) +TSPC_PBAP_2_8a False (*) PCE: Able to reset the missed Calls (C.2) +TSPC_PBAP_2_9 False PCE: X-BT-UCI vCard Field (C.2) +TSPC_PBAP_2_9a False PCE: Able to request X-BT-UCI Field (C.2) +TSPC_PBAP_2_10 False PCE: X-BT-UID vCard Field (C.2) +TSPC_PBAP_2_10a False PCE: Referencing Contacts (C.2) +TSPC_PBAP_2_12 False PCE: Contact Image Default Format (C.2) +TSPC_PBAP_2_12a False PCE: Able to request Contact Images (C.2) +TSPC_PBAP_2_13 False PCE: Supported Phonebook Objects (C.3) +TSPC_PBAP_2_13a False (*) PCE: Telecom/pb (C.3) +TSPC_PBAP_2_13b False PCE: Telecom/ich (C.3) +TSPC_PBAP_2_13c False PCE: Telecom/och (C.3) +TSPC_PBAP_2_13d False (*) PCE: Telecom/mch (C.3) +TSPC_PBAP_2_13e False (*) PCE: Telecom/cch (C.3) +TSPC_PBAP_2_13f False PCE: Telecom/spd (C.3) +TSPC_PBAP_2_13g False PCE: Telecom/fav (C.3) +TSPC_PBAP_2_13h False PCE: SIM1/Telecom/pb (C.3) +TSPC_PBAP_2_13i False PCE: SIM1/Telecom/ich (C.3) +TSPC_PBAP_2_13j False PCE: SIM1/Telecom/och (C.3) +TSPC_PBAP_2_13k False PCE: SIM1/Telecom/mch (C.3) +TSPC_PBAP_2_13l False PCE: SIM1/Telecom/cch (C.3) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined features. +C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.3: Mandatory to support at least one of the listed phonebook objects . +C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders + (13d,13e,13k,13l) are supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + Supported Phone Book Download functions (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_3_1 False (*) PCE: Pull Phone Book (C.1) +------------------------------------------------------------------------------- +C1: Mandatory for PCE if Phone Book Download (TSPC_PBAP_2_1) is supported, + otherwise excluded. +------------------------------------------------------------------------------- + + + Supported Phone Book Browsing functions (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_4_1 False (*) PCE: Set Phone Book (C.1) +TSPC_PBAP_4_2 False (*) PCE: Pull vCard Listing (C.1) +TSPC_PBAP_4_3 False (*) PCE: Pull vCard Entry (C.1) +------------------------------------------------------------------------------- +C1: Mandatory for PCE if Phone Book Browsing TSPC_PBAP_2_2 is supported, + otherwise excluded. +------------------------------------------------------------------------------- + + + Used vCard formats (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_5_1 False (*) PCE: vCard 2.1 (C.1) +TSPC_PBAP_5_2 False (*) PCE: vCard 3.0 (C.1) +------------------------------------------------------------------------------- +C1: It is mandatory to support at least one of the defined versions if PCE + supported. +------------------------------------------------------------------------------- + + + OBEX Functions for PCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_6_1 False (*) PCE: Connect (M) +TSPC_PBAP_6_2 False (*) PCE: Disconnect (M) +TSPC_PBAP_6_3 False (*) PCE: Get (M) +TSPC_PBAP_6_4 False PCE: Abort (O) +TSPC_PBAP_6_5 False (*) PCE: SetPath (C.1) +TSPC_PBAP_6_6 False PCE: Support for OBEX authentication initiation + (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_2_2 (Phone Book Browsing) is supported, + otherwise Excluded. +C.2: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or + TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + PCE OBEX Header Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_7_1 False (*) PCE: Name (M) +TSPC_PBAP_7_2 False (*) PCE: Type (M) +TSPC_PBAP_7_3 False (*) PCE: Body (M) +TSPC_PBAP_7_4 False (*) PCE: End of Body (M) +TSPC_PBAP_7_5 False (*) PCE: Target (M) +TSPC_PBAP_7_6 False (*) PCE: Who (M) +TSPC_PBAP_7_7 False (*) PCE: Connection ID (M) +TSPC_PBAP_7_8 False (*) PCE: Authentication Challenge (M) +TSPC_PBAP_7_9 False (*) PCE: Authentication Response (M) +TSPC_PBAP_7_10 False (*) PCE: Application Parameters (M) +TSPC_PBAP_7_11 False PCE: Single Response Mode (C.1) +TSPC_PBAP_7_12 False PCE: Single Response Mode Parameter + (ability to parse) (C.1) +TSPC_PBAP_7_13 False PCE: Single Response Mode Parameter + (ability to send) (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + OBEX Error Codes for PCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_8_1 False (*) PCE: Bad Request (M) +TSPC_PBAP_8_2 False (*) PCE: Not Implemented (M) +TSPC_PBAP_8_3 False (*) PCE: Unauthorized (M) +TSPC_PBAP_8_4 False (*) PCE: Precondition Failed (M) +TSPC_PBAP_8_5 False (*) PCE: Not Found (M) +TSPC_PBAP_8_6 False (*) PCE: Not Acceptable (M) +TSPC_PBAP_8_7 False (*) PCE: Service Unavailable (M) +TSPC_PBAP_8_8 False (*) PCE: Forbidden (M) +------------------------------------------------------------------------------- + + + Supported features ( PSE ) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_9_1 True PSE: Phone Book Download (M) +TSPC_PBAP_9_2 True PSE: Phone Book Browsing (M) +TSPC_PBAP_9_3 True PSE: Session Management (M) +TSPC_PBAP_9_4 True PSE: Able to request the size of the Phonebook + (M) +TSPC_PBAP_9_5 False PSE: Database Identifier (C.1) +TSPC_PBAP_9_5a False PSE: Able to keep a persistent Database + Identifier (C.2) +TSPC_PBAP_9_5b False PSE: Able to regenerate a Database Identifier + (C.2) +TSPC_PBAP_9_6 False PSE: Folder Version Counters (C.1) +TSPC_PBAP_9_6a False PSE: Able to Insert or Remove Entries (C.2) +TSPC_PBAP_9_6b False PSE: Able to Modify contact primary Fields (C.2) +TSPC_PBAP_9_6c False PSE: Able to Modify contact secondary Fields + (C.2) +TSPC_PBAP_9_7 False (*) PSE: vCard Selecting (C.1) +TSPC_PBAP_9_8 False (*) PSE: Enhanced Missed Calls (C.4) +TSPC_PBAP_9_9 False PSE: X-BT-UCI vCard Field (C.2) +TSPC_PBAP_9_10 False PSE: X-BT-UID vCard Field (C.2) +TSPC_PBAP_9_10a False PSE: Referencing Contacts (C.3) +TSPC_PBAP_9_12 False PSE: Contact Image Default Format (C.1) +TSPC_PBAP_9_12a False PSE: Able to request Contact Images (C.2) +TSPC_PBAP_9_13 False PSE: Supported Phonebook Objects +TSPC_PBAP_9_13a True PSE: Telecom/pb (M) +TSPC_PBAP_9_13b True (*) PSE: Telecom/ich (O) +TSPC_PBAP_9_13c True (*) PSE: Telecom/och (O) +TSPC_PBAP_9_13d True (*) PSE: Telecom/mch (O) +TSPC_PBAP_9_13e True PSE: Telecom/cch (O) +TSPC_PBAP_9_13f False PSE: Telecom/spd (C.2) +TSPC_PBAP_9_13g False PSE: Telecom/fav (C.2) +TSPC_PBAP_9_13h False (*) PSE: SIM1/Telecom/pb (O) +TSPC_PBAP_9_13i False PSE: SIM1/Telecom/ich (O) +TSPC_PBAP_9_13j False PSE: SIM1/Telecom/och (O) +TSPC_PBAP_9_13k False (*) PSE: SIM1/Telecom/mch (O) +TSPC_PBAP_9_13l False PSE: SIM1/Telecom/cch (O) +TSPC_PBAP_9_14 False PSE: Deleted Handles Behavior +TSPC_PBAP_9_14a True PSE: Error reporting (C.5) +TSPC_PBAP_9_14b False PSE: Change tracking (C.5) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.3: Optional if TSPC_PBAP_9_10 (X-BT-UID vCard Property) is supported, + otherwise Excluded. +C.4: Optional if TSPC_PBAP_0_3 (PBAP 1.2) and any of the mch or cch folders + (13d,13e,13k,13l) are supported, otherwise Excluded. +C.5: It is mandatory to support at least one of the defined deleted handles + behaviors. +------------------------------------------------------------------------------- + + + Supported Phone Book Download functions ( PSE ) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_10_1 True PSE: Pull Phone Book (M) +TSPC_PBAP_10_2 False PSE: Call History Function (O) +------------------------------------------------------------------------------- + + + Supported Phone Book Browsing functions ( PSE ) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_11_1 True PSE: Set Phone Book (M) +TSPC_PBAP_11_2 True PSE: Pull vCard Listing (M) +TSPC_PBAP_11_3 True PSE: Pull vCard Entry (M) +------------------------------------------------------------------------------- + + + Used vCard formats (PSE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_12_1 True PSE: vCard 2.1 (M) +TSPC_PBAP_12_2 True PSE: vCard 3.0 (M) +------------------------------------------------------------------------------- + + + OBEX Functions for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_13_1 True PSE: Connect (M) +TSPC_PBAP_13_2 True PSE: Disconnect (M) +TSPC_PBAP_13_3 True PSE: Get (M) +TSPC_PBAP_13_4 True PSE: Abort (M) +TSPC_PBAP_13_5 True PSE: SetPath (M) +TSPC_PBAP_13_6 False PSE: Support for OBEX authentication initiation + (C.1) +------------------------------------------------------------------------------- +C.1: Optional to support initiation if TSPC_PBAP_0_1 (PBAP 1.0) or + TSPC_PBAP_0_2 (PBAP 1.1) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + PSE OBEX Header Support +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_14_1 True PSE: Name (M) +TSPC_PBAP_14_2 True PSE: Type (M) +TSPC_PBAP_14_3 True PSE: Body (M) +TSPC_PBAP_14_4 True PSE: End of Body (M) +TSPC_PBAP_14_5 True PSE: Target (M) +TSPC_PBAP_14_6 True PSE: Who (M) +TSPC_PBAP_14_7 True PSE: Connection ID (M) +TSPC_PBAP_14_8 True PSE: Authentication Challenge (M) +TSPC_PBAP_14_9 True PSE: Authentication Response (M) +TSPC_PBAP_14_10 True PSE: Application Parameters (M) +TSPC_PBAP_14_11 False PSE: Single Response Mode (C.1) +TSPC_PBAP_14_12 False PSE: Single Response Mode Parameter + (ability to parse) (C.1) +TSPC_PBAP_14_13 False PSE: Single Response Mode Parameter + (ability to send) (C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +C.2: Optional if TSPC_PBAP_0_3 (PBAP 1.2) is supported, otherwise Excluded. +------------------------------------------------------------------------------- + + + OBEX Error Codes for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_15_1 True PSE: Bad Request (M) +TSPC_PBAP_15_2 True PSE: Not Implemented (M) +TSPC_PBAP_15_3 True (*) PSE: Unauthorized (O) +TSPC_PBAP_15_4 True (*) PSE: Precondition Failed (C.1) +TSPC_PBAP_15_5 True PSE: Not Found (M) +TSPC_PBAP_15_6 True (*) PSE: Not Acceptable (O) +TSPC_PBAP_15_7 True PSE: Service Unavailable (M) +TSPC_PBAP_15_8 True (*) PSE: Forbidden (O) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_PBAP_9_14a (Error reporting) is supported, otherwise + Optional. +------------------------------------------------------------------------------- + + + GAP Modes for PCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_16_1 False (*) PCE: General discoverable mode (M) +TSPC_PBAP_16_2 False (*) PCE: Pairable mode (M) +------------------------------------------------------------------------------- + + + GAP Modes for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_17_1 True PSE: General discoverable mode (M) +TSPC_PBAP_17_2 True PSE: Pairable mode (M) +------------------------------------------------------------------------------- + + + GAP Security Modes for PCE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_18_1 False (*) PCE: Authentication Procedure (M) +TSPC_PBAP_18_2 False (*) PCE: Initiate LMP-Authentication (M) +TSPC_PBAP_18_3 False PCE: Security mode 1 (C.1) +TSPC_PBAP_18_4 False PCE: Security mode 2 (C.1) +TSPC_PBAP_18_5 False PCE: Security mode 3 (C.1) +TSPC_PBAP_18_6 False PCE: Security mode 4 (C.1) +------------------------------------------------------------------------------- +C.1: At least one of TSPC_PBAP_18_4, TSPC_PBAP_18_5 or TSPC_PBAP_18_6 + (security mode 2, 3, or 4) shall be supported. +------------------------------------------------------------------------------- + + + GAP Security Modes for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_19_1 True PSE: Authentication Procedure (M) +TSPC_PBAP_19_2 True PSE: Initiate LMP-Authentication (M) +TSPC_PBAP_19_3 False PSE: Security mode 1 (C.2) +TSPC_PBAP_19_4 False PSE: Security mode 2 (C.1) +TSPC_PBAP_19_5 False PSE: Security mode 3 (C.1) +TSPC_PBAP_19_6 True (*) PSE: Security mode 4 (C.1) +------------------------------------------------------------------------------- +C.1: At least one of TSPC_PBAP_19_3, TSPC_PBAP_19_4, TSPC_PBAP_19_5 or + TSPC_PBAP_19_6 (security mode 2, 3, or 4) shall be supported. +C.2: Excluded in PSE. +------------------------------------------------------------------------------- + + + GAP Idle Modes for PSE +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_21_1 True PSE: Initiation of General Inquiry (M) +TSPC_PBAP_21_2 False PSE: Initiation of Limited Inquiry (O) +------------------------------------------------------------------------------- + + + SDP Attributes (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_22_1 False (*) PCE: BluetoothProfileDescriptorList (M) +------------------------------------------------------------------------------- + + SDP Attributes (PSE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_23_1 True PSE: ProtocolDescriptorList (M) +TSPC_PBAP_23_2 True PSE: BluetoothProfileDescriptorList (M) +------------------------------------------------------------------------------- + + + Additional PBAP Capabilities +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_24_1 False PCE: Retrieve Large Phone Book (C.1) +TSPC_PBAP_24_2 False PSE: Transfer Large Phone Book (C.2) +TSPC_PBAP_24_3 False PCE: Retrieve Empty Phone Book (C.1) +TSPC_PBAP_24_4 False PSE: Transfer Empty Phone Book (C.2) +TSPC_PBAP_24_5 False PSE: Return Phonebook – Limit number of entries + (C.2) +TSPC_PBAP_24_6 False PSE: Return vCard listing – Limit number of + entries (C.2) +TSPC_PBAP_24_7 False PSE: Phone Book Order (C.2) +TSPC_PBAP_24_8 False PSE: Call stack timestamps (C.3) +TSPC_PBAP_24_9 False PSE: No User Interaction (C.2) +TSPC_PBAP_24_10 False PSE: Special Character Handling (C.2) +------------------------------------------------------------------------------- +C.1: Optional if TSPC_PBAP_2_1 is supported, otherwise excluded. +C.2: Optional if TSPC_PBAP_1_2 is supported, otherwise excluded. +C.3: Optional if TSPC_PBAP_10_2 is supported, otherwise excluded. +------------------------------------------------------------------------------- + + + GOEP 2.0 or later Features (PCE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_25_1 False PCE: GOEP v2.0 or later (M) +TSPC_PBAP_25_2 False (*) PCE: GOEP v2 Backwards Compatibility (M) +TSPC_PBAP_25_3 False PCE: OBEX over L2CAP (M) +TSPC_PBAP_25_4 False PCE: OBEX SRM (M) +TSPC_PBAP_25_5 False (*) PCE: Send OBEX SRMP header (C.1) +TSPC_PBAP_25_6 False PCE: Receive OBEX SRMP header (M) +------------------------------------------------------------------------------- +C.1: Optional to support if TSPC_PBAP_25_4 (OBEX SRM) is supported, + otherwise Excluded. +------------------------------------------------------------------------------- + + + GOEP 2.0 or later Features (PSE) +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_PBAP_26_1 False (*) PSE: GOEP v2.0 or later (M) +TSPC_PBAP_26_2 False (*) PSE: GOEP v2 Backwards Compatibility (M) +TSPC_PBAP_26_3 False PSE: OBEX over L2CAP (M) +TSPC_PBAP_26_4 False PSE: OBEX SRM (M) +TSPC_PBAP_26_5 False (*) PSE: Send OBEX SRMP header (C.1) +TSPC_PBAP_26_6 False PSE: Receive OBEX SRMP header (M) +TSPC_ALL False Turns on all the test cases +------------------------------------------------------------------------------- +C.1: Optional if TSPC_PBAP_26_4 (OBEX SRM) is supported, otherwise Excluded. +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-rfcomm.txt bluez-5.23/android/pics-rfcomm.txt --- bluez-4.101/android/pics-rfcomm.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-rfcomm.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,67 @@ +RFCOMM PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + Protocol Version +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_RFCOMM_0_1 False RFCOMM 1.1 with TS 07.10 (C.1) +TSPC_RFCOMM_0_2 True (*) RFCOMM 1.2 with TS 07.10 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support one and only one of the protocol versions. +------------------------------------------------------------------------------- + + + Supported Procedures +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_RFCOMM_1_1 True (*) Initialize RFCOMM Session (C.2) +TSPC_RFCOMM_1_2 True (*) Respond to Initialization of an RFCOMM + Session (C.2) +TSPC_RFCOMM_1_3 True Shutdown RFCOMM Session (M) +TSPC_RFCOMM_1_4 True Respond to a Shutdown of an RFCOMM + Session (M) +TSPC_RFCOMM_1_5 True (*) Establish DLC (C.2) +TSPC_RFCOMM_1_6 True (*) Respond to Establishment of a DLC (C.1) +TSPC_RFCOMM_1_7 True Disconnect DLC (M) +TSPC_RFCOMM_1_8 True Respond to Disconnection of a DLC (M) +TSPC_RFCOMM_1_9 True Respond to and send MSC Command (M) +TSPC_RFCOMM_1_10 True Initiate Transfer Information (M) +TSPC_RFCOMM_1_11 True Respond to Test Command (M) +TSPC_RFCOMM_1_12 True (*) Send Test Command (O) +TSPC_RFCOMM_1_13 True React to Aggregate Flow Control (M) +TSPC_RFCOMM_1_14 True Respond to RLS Command (M) +TSPC_RFCOMM_1_15 False Send RLS Command (O) +TSPC_RFCOMM_1_16 True Respond to PN Command (M) +TSPC_RFCOMM_1_17 True (*) Send PN Command (C.3) +TSPC_RFCOMM_1_18 True (*) Send Non-Supported Command (NSC) + response (C.4) +TSPC_RFCOMM_1_19 True Respond to RPN Command (M) +TSPC_RFCOMM_1_20 True (*) Send RPN Command (O) +TSPC_RFCOMM_1_21 True (*) Closing Multiplexer by First Sending + a DISC Command (O) +TSPC_RFCOMM_1_22 True Support of Credit Based Flow Control (M) +TSPC_RFCOMM_1_23 True (*) IUT Responds to Establishment of + a DLC (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory if SPP 1/2 (Device B) is supported, otherwise Excluded. +C.2: Mandatory if SPP 1/1 (Device A) is supported, otherwise Excluded. +C.3: Mandatory if SPP 1/1 (Device A) is supported, otherwise Optional. +C.4: Mandatory to support if 0/2 (RFCOMM 1.2) is supported, otherwise Optional. +------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SPP_2_1 False Channel the tester should use when + connecting to the IUT (Default "") +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-scpp.txt bluez-5.23/android/pics-scpp.txt --- bluez-4.101/android/pics-scpp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-scpp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,143 @@ +ScPP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults + +M - mandatory +O - optional + + Connection Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_1_1 False (*) Scan Server (C.1) +TSPC_ScPP_1_2 True Scan Client (C.1) +------------------------------------------------------------------------------- +Note: C.1 is not explained in the spec for ScPP. +------------------------------------------------------------------------------- + + + Transport Requirements +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_2_1 False (*) Profile supported over BR/EDR (C.1) +TSPC_ScPP_2_2 True Profile supported over LE (M) +------------------------------------------------------------------------------- +C.1: Excluded for this profile. +------------------------------------------------------------------------------- + + + Services - Scan Server +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_3_1 False (*) Implements Scan Parameters Service (M) +------------------------------------------------------------------------------- + + + GAP Requirements - Scan Server Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_4_1 False (*) Peripheral (M) +TSPC_ScPP_4_2 False (*) Directed Connectable Mode (O) +TSPC_ScPP_4_3 False (*) Undirected Connectable Mode (M) +TSPC_ScPP_4_4 False (*) Bondable mode (peripheral) (O) +TSPC_ScPP_4_5 False (*) Bonding procedure (peripheral) (O) +TSPC_ScPP_4_6 False (*) LE Security Mode 1 (peripheral) (M) +------------------------------------------------------------------------------- + + + Scan Server +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_5_1 False (*) No security (LE Security Level 1) (M) +TSPC_ScPP_5_2 False (*) Unauthenticated no MITM protection. (LE Security + Level 2, Just Works) (O) +TSPC_ScPP_5_3 False (*) Authenticated MITM protection (LE Security + Level 3, Passkey) (O) +------------------------------------------------------------------------------- + + + Client Services Support - Scan Client Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_6_1 True Scan Parameters Service (M) +------------------------------------------------------------------------------- + + + Discover Services and Characteristics - Scan Client Role +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_7_1 True Discover Scan Parameters Service (M) +TSPC_ScPP_7_2 True Discover Scan Parameters characteristic: + Scan interval Window (M) +TSPC_ScPP_7_3 True Discover Scan Parameters characteristic: + Scan Refresh (M) +TSPC_ScPP_7_4 True Discover Scan Parameters characteristic: + Scan Refresh – Client Characteristic + Configuration Descriptor (M) +------------------------------------------------------------------------------- + + + Features - Client +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_8_1 True Write Scan Interval Window characteristic (M) +TSPC_ScPP_8_2 True Configure Scan Refresh characteristic: + Client Characteristic Configuration + characteristic descriptor with 0x0001 (M) +TSPC_ScPP_8_3 True Notify Scan Refresh characteristic (M) +------------------------------------------------------------------------------- + + + GATT Requirements - Scan Client +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_9_1 True Attribute Protocol supported over LE Transport + (M) +TSPC_ScPP_9_2 True Generic Attribute Profile Client (M) +TSPC_ScPP_9_3 True Discover All Primary Services (C.1) +TSPC_ScPP_9_4 True Discover Primary Services by Service UUID (C.1) +TSPC_ScPP_9_5 True Discover All Characteristics of a Service (C.2) +TSPC_ScPP_9_6 True Discover Characteristics by UUID (C.2) +TSPC_ScPP_9_7 True Discover All Characteristic Descriptors (M) +TSPC_ScPP_9_8 True Write without Response (M) +TSPC_ScPP_9_9 True Write Characteristic Descriptors (M) +TSPC_ScPP_9_10 True Notifications (M) +------------------------------------------------------------------------------- +C.1: Mandatory to support one of these sub-procedures for service discovery +C.2: Mandatory to support one of these sub-procedures for characteristic + discovery +------------------------------------------------------------------------------- + + + GAP Requirements - Scan Client +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_10_1 True Central (M) +TSPC_ScPP_10_2 True Bondable mode (central) (O) +TSPC_ScPP_10_3 True Bonding procedure (central) (O) +TSPC_ScPP_10_4 True Central (M) +------------------------------------------------------------------------------- + + + SM Requirements - Scan Client +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_ScPP_11_1 True No Security Requirements (LE Security Level 1, + No Security) (M) +TSPC_ScPP_11_2 True Unauthenticated no MITM protection (LE Security + Level 2, Just Works) (O) +TSPC_ScPP_11_3 True Authenticated MITM protection (LE Security + Level 3, Passkey) (O) +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-sdp.txt bluez-5.23/android/pics-sdp.txt --- bluez-4.101/android/pics-sdp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-sdp.txt 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,137 @@ + +* - different than BITE defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + + Support Different Size Capabilities on UUID +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.1.1 True Support for 128 bit UUID (M) +E.1.2 True Support for 32 bit UUID (M) +E.1.3 True Support for 16 bit UUID (M) +------------------------------------------------------------------------------- + + + Roles +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.1b.1 True Support for server role (C.1) +E.1b.2 True Support for client role (C.1) +------------------------------------------------------------------------------- +C.1 Mandatory to support at least one of the roles +------------------------------------------------------------------------------- + + + Valid Service Search Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.2.1 True Support for respond on search of single + Service, using ServiceSearchRequest (C.2) +E.2.2 True Support for respond on search of Service, + using continuation state (O) +E.2.3 True Search for services using the continuation + state (C.1) +------------------------------------------------------------------------------- +C.1 Mandatory to support if the client role is supported (1b/2) +C.2 Mandatory to support if the server role is supported (1b/1) +------------------------------------------------------------------------------- + + + Invalid Service Search Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.3.1 True Support for error response on Service search + request (M) +------------------------------------------------------------------------------- + + + Valid Service Attribute Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.4.1 True Support for respond on search of + Attribute(s) (M) +E.4.2 True Support for respond on search of + Attribute, using continuation state (O) +E.4.3 True Support for respond on search on + attribute AdditionalProtocolDescriptorList (O) +------------------------------------------------------------------------------- + + + Invalid Service Attribute Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.5.1 True Support for error response on Attribute + search request (M) +------------------------------------------------------------------------------- + + + Valid Service Search Attribute Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.6.1 True Support for respond on search for Service(s) + and Attribute(s) (M) +E.6.2 True Support for respond on search of Attribute, + using continuation state (O) +E.6.3 True Support for respond on search on attribute + AdditionalProtocolDescriptorList on existing + service (O) +------------------------------------------------------------------------------- + + + Invalid Service Search Attribute Request +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.7.1 True Support for error response on Service and + Attribute request (M) +------------------------------------------------------------------------------- + + + Service Browsing +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.8.1 True Support for browsing, using + SDP_ServiceSearchRequest and + SDP_ServiceAttributeRequest (O) +E.8.2 True Support for browsing, using + SDP_ServiceSearchAttributeRequest (O) +------------------------------------------------------------------------------- + + + Attributes Present in IUT +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +E.9.1 True ServiceID (O) +E.9.2 True ProtocolDescriptorList (O) +E.9.3 True ServiceRecordState (O) +E.9.4 True ServiceInfoTimeToLive (O) +E.9.5 True BrowseGroupList (O) +E.9.6 True LanguageBaseAttributeIdList (O) +E.9.7 True ServiceAvailability (O) +E.9.8 True IconURL (O) +E.9.9 True ServiceName (O) +E.9.10 True ServiceDescription (O) +E.9.11 True ProviderName (O) +E.9.12 True VersionNumberList (O) +E.9.13 True ServiceDataBaseState (O) +E.9.14 True BluetoothProfileDescriptorList (O) +E.9.15 True DocumentationURL (O) +E.9.16 True ClientExecutableURL (O) +E.9.17 True AdditionalProtocolDescriptorList (C.1) +E.9.18 True ServiceRecordHandle (M) +E.9.19 True ServiceClassIDList (M) +------------------------------------------------------------------------------- +C.1: Optional if 9/2 is supported, otherwise excluded +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-sm.txt bluez-5.23/android/pics-sm.txt --- bluez-4.101/android/pics-sm.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-sm.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,91 @@ +SM PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults + +M - mandatory +O - optional + + Connection Roles +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_1_1 True Master Role (Initiator) (C.1) +TSPC_SM_1_2 True Slave Role (Responder) (C.1) +------------------------------------------------------------------------------- +C.1: At least one of these features shall be supported. +------------------------------------------------------------------------------- + + + Security Properties +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_2_1 True Authenticated MITM protection (O) +TSPC_SM_2_2 True Unauthenticated no MITM protection (C.1) +TSPC_SM_2_3 True No security requirements (M) +TSPC_SM_2_4 False OOB supported (O) +------------------------------------------------------------------------------- +C.1: If TSPC_SM_2_1 is supported then Mandatory, else Optional +------------------------------------------------------------------------------- + + + Encryption Key Size +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_3_1 True Encryption Key Size Negotiation (M) +------------------------------------------------------------------------------- + + + Pairing Method +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_4_1 True Just Works (O) +TSPC_SM_4_2 True Passkey Entry (C.1) +TSPC_SM_4_3 False Out of Band (C.1) +------------------------------------------------------------------------------- +C.1: If TSPC_SM_2_1 is supported, at least one of these features shall be + supported. +------------------------------------------------------------------------------- + + + Security Initiation +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_5_1 True Encryption Setup using STK (C.3) +TSPC_SM_5_2 True Encryption Setup using LTK (O) +TSPC_SM_5_3 True Slave Initiated Security (C.1) +TSPC_SM_5_4 True Slave Initiated Security – Master response(C.2) +------------------------------------------------------------------------------- +C.1: Mandatory if TSPC_SM_1_2 is supported, otherwise Excluded +C.2: Mandatory if TSPC_SM_1_1 is supported, otherwise Excluded +C.3: Mandatory IF TSPC_SM_2_1 OR TSPC_SM_2_1 OR TSPC_SM_2_4 is supported, + otherwise Excluded +------------------------------------------------------------------------------- + + + Signing Algorithm +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_6_1 True Signing Algorithm - Generation (O) +TSPC_SM_6_2 True Signing Algorithm - Resolving (O) +------------------------------------------------------------------------------- + + + Key Distribution +------------------------------------------------------------------------------- +Parameter Name Selected Description +------------------------------------------------------------------------------- +TSPC_SM_7_1 True Encryption Key (C.1) +TSPC_SM_7_2 True Identity Key (C.2) +TSPC_SM_7_3 True Signing Key (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory if GAP (24/2 OR 42/6) is supported, ELSE Optional +C.2: Mandatory if GAP (26/3) is supported, ELSE Optional +C.3: Mandatory if GAP (25/6 OR 35/6) is supported, ELSE Optional +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pics-spp.txt bluez-5.23/android/pics-spp.txt --- bluez-4.101/android/pics-spp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pics-spp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,99 @@ +SPP PICS for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +# - not yet implemented/supported + +M - mandatory +O - optional + + + Profile Version +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +TSPC_SPP_0_1 False SPP v1.1 (C.1) +TSPC_SPP_0_2 True (*) SPP v1.2 (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory to support only one Profile version. +------------------------------------------------------------------------------- + + + Device Role +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +TSPC_SPP_1_1 True (*) Device A (DevA) (C.1) +TSPC_SPP_1_2 True (*) Device B (DevB) (C.1) +------------------------------------------------------------------------------- +C.1: It is mandatory to support at least one of the defined roles. +------------------------------------------------------------------------------- + + + Support of SPP Service +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +TSPC_SPP_2_1 True (*) Support of Serial Profile Service (C.1) +------------------------------------------------------------------------------- +C.1: Mandatory for devices that support Serial Profile for serial cable + emulation as a Bluetooth service. Irrelevant of devices that only + support Serial Profile for usage by another application profile + e.g. Fax Profile, Dun Profile, Hands free Profile, etc. +------------------------------------------------------------------------------- + + + Application Procedures +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +TSPC_SPP_3_1 True (*) Establish link and set up virtual serial + connection (C.1) +TSPC_SPP_3_2 True (*) Accept link and virtual serial connection + establishment (C.2) +TSPC_SPP_3_3 True (*) Register Service record for application in + local SDP database (C.2) +TSPC_SPP_3_4 True (*) No release in Sniff mode. Sniff mode enabled + in the Link Manager (O) +TSPC_SPP_3_5 True (*) No release in Hold mode. Hold mode enabled + in the Link Manager (O) +TSPC_SPP_3_6 True (*) No release in Park mode. Park mode enabled + in the Link Manager (O) +TSPC_SPP_3_7 True (*) No release after Master/Slave switch. M/S + switch enabled in the Link manager (O) +------------------------------------------------------------------------------- +C.1: Mandatory for Device A, Irrelevant for Device B. +C.2: Mandatory for Device B, Irrelevant for Device A. +------------------------------------------------------------------------------- + + + Service Port Profile Record Content (SerialPort UUID) +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +TSPC_SPP_4_1 True (*) SerialPort service class (UUID16: 0x1101) (C.1) +TSPC_SPP_4_2 True (*) Protocol0, L2CAP (C.1) +TSPC_SPP_4_3 True (*) Protocol1, RFCOMM (C.1) +TSPC_SPP_4_4 True (*) Server Channel number (C.1) +TSPC_SPP_4_5 True (*) Displayable text name (C.2) +TSPC_SPP_4_6 True (*) BluetoothProfileDescriptorList (C.3) +------------------------------------------------------------------------------- +C.1: Mandatory for role B, if capability 'Support of Serial Profile Service' + (SPP 2/1) supported. Irrelevant for role A. +C.2: Mandatory to support if 2/1 (Support of Serial Profile Service) AND 0/1 + (SPP v1.1) are supported, otherwise Optional. +C.3: Mandatory to support if 2/1 (Support of Serial Profile Service) AND 0/2 + (SPP v1.2) are supported, otherwise Optional. +------------------------------------------------------------------------------- + + + Encryption +------------------------------------------------------------------------------- +Item Selected Description +------------------------------------------------------------------------------- +TSPC_SPP_5_1 True (*) Initiate encryption (O) +TSPC_SPP_5_2 True Accept encryption request (M) +TSPC_SPP_5_3 True Point to point encryption (M) +TSPC_SPP_5_4 True Stop encryption (M) +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-a2dp.txt bluez-5.23/android/pixit-a2dp.txt --- bluez-4.101/android/pixit-a2dp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-a2dp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,30 @@ +A2DP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to PTS's bin/audio folder + +Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_SRC_class_of_device 080418 +TSPX_SNK_class_of_device 04041C +TSPX_pin_code 0000 +TSPX_delete_link_key FALSE +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ + bin\audio (#) +TSPX_no_avrcp TRUE +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_rfcomm_channel 8 +TSPX_l2cap_psm 1011 +TSPX_no_confirmations FALSE +TSPX_cover_art_uuid 3EEE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-avctp.txt bluez-5.23/android/pixit-avctp.txt --- bluez-4.101/android/pixit-avctp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-avctp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,39 @@ +AVCTP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to PTS's bin/audio folder + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_avctp_psm 0017 +TSPX_avctp_profile_id 110E +TSPX_connect_avdtp FALSE (*) +TSPX_avctp_tester_command_data +TSPX_avctp_tester_response_data +TSPX_avctp_iut_command_data +TSPX_avctp_iut_response_data +TSPX_bd_addr_iut 11223344556677 (*&) +TSPX_pin_code 0000 +TSPX_delete_link_key FALSE +TSPX_security_enabled FALSE +TSPX_class_of_device 20050C +TSPX_player_feature_bitmask 0000000000000007FFF00070000000000 +TSPX_avrcp_version +TSPX_establish_avdtp_stream TRUE +TSPX_tester_av_role +TSPX_time_guard 300000 +TSPX_avrcp_only FALSE +TSPX_use_implicit_send TRUE +TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ + bin\audio (#) +TSPX_no_confirmations FALSE +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_rfcomm_channel 8 +TSPX_l2cap_psm 1011 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-avdtp.txt bluez-5.23/android/pixit-avdtp.txt --- bluez-4.101/android/pixit-avdtp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-avdtp.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,31 @@ +AVDTP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to PTS's bin/audio folder + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_SNK_class_of_device 04041C +TSPX_SRC_class_of_device 080418 +TSPX_security_control_data +TSPX_content_protection_data +TSPX_content_protection_type +TSPX_no_avrcp TRUE +TSPX_media_directory +TSPX_bd_addr_iut 11223344556677 (*&) +TSPX_delete_link_key FALSE +TSPX_pin_code 1234 +TSPX_security_enabled TRUE (*) +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_l2cap_psm 1003 +TSPX_rfcomm_channel 8 +TSPX_no_confirmations FALSE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-avrcp.txt bluez-5.23/android/pixit-avrcp.txt --- bluez-4.101/android/pixit-avrcp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-avrcp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,36 @@ +AVRCP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to PTS's bin/audio folder + +Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_class_of_device 20050C +TSPX_player_feature_bitmask 0000000000000007FFF00070000000000 +TSPX_pin_code 0000 +TSPX_delete_link_key FALSE +TSPX_time_guard 300000 +TSPX_avrcp_only FALSE +TSPX_search_string tomorrow +TSPX_max_avc_fragments 10 +TSPX_establish_avdtp_stream TRUE +TSPX_use_implicit_send TRUE +TSPX_avrcp_version +TSPX_tester_av_role +TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\ + bin\audio (#) +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_rfcomm_channel 8 +TSPX_l2cap_psm 1011 +TSPX_no_confirmations FALSE +TSPX_no_cover_art_folder +TSPX_cover_art_folder +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-did.txt bluez-5.23/android/pixit-did.txt --- bluez-4.101/android/pixit-did.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-did.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,24 @@ +DID PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled False +TSPX_ClientExecutableURL False (*) +TSPX_ServiceDescription False (*) +TSPX_DocumentationURL False (*) +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_class_of_device_pts 200404 +TSPX_device_search_time 30 +TSPX_delete_link_key False +TSPX_pin_code 0000 +TSPX_time_guard 200000 +TSPX_use_implicit_send True +TSPX_secure_simple_pairing_pass_key_confirmation False +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-dis.txt bluez-5.23/android/pixit-dis.txt --- bluez-4.101/android/pixit-dis.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-dis.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,25 @@ +DIS PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_time_guard 180000 +TSPX_use_implicit_send TRUE +TSPX_tester_database_file C:/Program Files/... +TSPX_mtu_size 23 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_delete_link_key FALSE +TSPX_pin_code 0000 +TSPX_use_dynamic_pin FALSE +TSPX_delete_ltk FALSE +TSPX_security_enabled FALSE +TSPX_iut_setup_att_over_br_edr FALSE +TSPX_tester_appearance 0000 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-gap.txt bluez-5.23/android/pixit-gap.txt --- bluez-4.101/android/pixit-gap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-gap.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,58 @@ +GAP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_bd_addr_PTS 000000000000 +TSPX_broadcaster_class_of_device 100104 +TSPX_observer_class_of_device 100104 +TSPX_peripheral_class_of_device 100104 +TSPX_central_class_of_device 100104 +TSPX_security_enabled True +TSPX_delete_link_key True +TSPX_pin_code 0000 +TSPX_time_guard 300000 +TSPX_use_implicit_send True +TSPX_use_dynamic_pin False +TSPX_secure_simple_pairing_pass_key_confirmation False +TSPX_using_public_device_address True +TSPX_using_random_device_address False +TSPX_lim_adv_timeout 30720 +TSPX_gen_disc_adv_min 30720 +TSPX_lim_disc_scan_min 10240 +TSPX_gen_disc_scan_min 10240 +TSPX_database_file Database-GAP.sig +TSPX_iut_rx_mtu 23 +TSPX_iut_private_address_interval 10000 +TSPX_iut_privacy_enabled False +TSPX_psm 1001 +TSPX_iut_valid_connection_interval_min 00C8 +TSPX_iut_valid_conneciton_interval_max 0960 +TSPX_iut_valid_connection_latency 0007 +TSPX_iut_valid_timeout_multiplier 0960 +TSPX_iut_connection_parameter_timeout 30000 +TSPX_iut_invalid_connection_interval_min 0000 +TSPX_iut_invalid_conneciton_interval_max 0000 +TSPX_iut_invalid_connection_latency 0000 +TSPX_iut_invalid_timeout_multiplier 0000 +TSPX_LE_scan_interval 0010 +TSPX_LE_scan_window 0010 +TSPX_con_interval_min 0032 +TSPX_con_interval_max 0046 +TSPX_con_latency 0000 +TSPX_supervision_timeout 07D0 +TSPX_minimum_ce_length 0000 +TSPX_maximum_ce_length 0000 +TSPX_pairing_before_service_request False +TSPX_iut_mandates_mitm False +TSPX_encryption_before_service_request False +TSPX_tester_appearance 0000 +TSPX_iut_advertising_data_in_broadcasting_mode [set to default value] +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-gatt.txt bluez-5.23/android/pixit-gatt.txt --- bluez-4.101/android/pixit-gatt.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-gatt.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,31 @@ +GATT PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_security_enabled FALSE +TSPX_delete_link_key TRUE +TSPX_time_guard 180000 +TSPX_selected_handle 0012 +TSPX_use_implicit_send TRUE +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_iut_use_dynamic_bd_addr FALSE +TSPX_iut_setup_att_over_br_edr FALSE +TSPX_tester_database_file [Path to GATT Test + Database] +TSPX_iut_is_client_periphral FALSE +TSPX_iut_is_server_central FALSE +TSPX_mtu_size 23 +TSPX_pin_code 0000 +TSPX_use_dynamic_pin FALSE +TSPX_delete_ltk FALSE +TSPX_characteristic_readable FALSE +TSPX_tester_appearance 0000 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-hdp.txt bluez-5.23/android/pixit-hdp.txt --- bluez-4.101/android/pixit-hdp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-hdp.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,32 @@ +HDP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled TRUE +TSPX_delete_link_key FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_sink_device_class_of_device 00900 +TSPX_source_device_class_of_device 00900 +TSPX_pin_code 0000 +TSPX_use_dynamic_pin FALSE +TSPX_use_implicit_send TRUE +TSPX_MCAP_l2cap_psm_control 1001 +TSPX_MCAP_l2cap_psm_data 1003 +TSPX_MCAP_sync_lead_time 0BB8 +TSPX_MCAP_timestamp_native_resolution 2233 +TSPX_MCAP_timestamp_native_accuracy 1400 +TSPX_MCAP_timestamp_required_accuracy 6400 +TSPX_DC_max 1 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_time_guard 60000000 +TSPX_ieee_device_specialization 10417 +TSPX_ieee_standard_configuration TRUE +TSPX_MCAP_bluetooth_clock_access_resolution 55 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-hfp.txt bluez-5.23/android/pixit-hfp.txt --- bluez-4.101/android/pixit-hfp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-hfp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,38 @@ +HFP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to the respective phone numbers +^ - should be set according to the reported phone number's type + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled TRUE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_hf_class_of_device 200408 +TSPX_ag_class_of_device 400204 +TSPX_packet_type_sco 00A0 +TSPX_phone_number 1234567 (#) +TSPX_second_phone_number 7654321 (#) +TSPX_phone_number_type 145 (*^) +TSPX_second_phone_number_type 145 (*^) +TSPX_phone_number_memory 1 +TSPX_phone_number_memory_invalid_index 9999 +TSPX_scan_all_memory_dial_locations FALSE +TSPX_pin_code 0000 +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_verbose_implicit_send FALSE +TSPX_delete_link_key FALSE +TSPX_server_channel_tester 01 +TSPX_server_channel_iut 00 +TSPX_verify_CLIP_information TRUE +TSPX_no_fail_verdict FALSE +TSPX_network_supports_correct_call_and_callstatus TRUE +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_AG_match_tester_BRSF_codec_negotiation_bit FALSE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-hid.txt bluez-5.23/android/pixit-hid.txt --- bluez-4.101/android/pixit-hid.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-hid.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,30 @@ +HID PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled True +TSPX_delete_link_key True +TSPX_query_iut_sdp True +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_pointing_device_class_of_device 002580 +TSPX_keyboard_device_class_of_device 002540 +TSPX_host_class_of_device 100108 +TSPX_pts_device_role MOUSE +TSPX_pin_code 0000 +TSPX_use_dynamic_pin_code False +TSPX_time_guard 6000000 +TSPX_hid_data_interval 1000 +TSPX_use_implicit_send True +TSPX_verbose_implicit_send False +TSPX_no_fail_verdicts False +TSPX_time_reconnect 30000 +TSPX_secure_simple_pairing_pass_key_confirmation False +TSPX_hid_report_id 1 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-hogp.txt bluez-5.23/android/pixit-hogp.txt --- bluez-4.101/android/pixit-hogp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-hogp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,28 @@ +HOGP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_time_guard 180000 +TSPX_use_implicit_send TRUE +TSPX_tester_database_file [Path to HOGP Test + Database] +TSPX_mtu_size 23 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_delete_link_key FALSE +TSPX_pin_code 0000 +TSPX_use_dynamic_pin FALSE +TSPX_delete_ltk FALSE +TSPX_security_enabled TRUE +TSPX_input_report_data CDA6F8B3AA +TSPX_output_report_data 001234567890EF +TSPX_feature_report_data 872D3F45EA +TSPX_tester_appearance 03C0 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-hsp.txt bluez-5.23/android/pixit-hsp.txt --- bluez-4.101/android/pixit-hsp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-hsp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,30 @@ +HSP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled TRUE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_hs_class_of_device 200404 +TSPX_ag_class_of_device 400204 +TSPX_packet_type_sco 00A0 +TSPX_pin_code 0000 +TSPX_time_guard 20000000 +TSPX_use_implicit_send TRUE +TSPX_verbose_implicit_send FALSE +TSPX_delete_link_key FALSE +TSPX_server_channel_tester 01 +TSPX_server_channel_iut 00 +TSPX_no_fail_verdict FALSE +TSPX_remote_audio_volume_control TRUE +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_inband_ring_only FALSE +TSPX_no_ring_or_inband_ring_tone FALSE +TSPX_iut_establish_audio_before_RING FALSE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-iopt.txt bluez-5.23/android/pixit-iopt.txt --- bluez-4.101/android/pixit-iopt.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-iopt.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,23 @@ +IOPT PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_security_enabled FALSE +TSPX_delete_link_key FALSE +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_class_of_device_pts 200404 +TSPX_class_of_device_test_pts_initiator TRUE +TSPX_limited_inquiry_used FALSE +TSPX_pin_code 0000 +TSPX_time_guard 200000 +TSPX_device_search_time 20 +TSPX_use_implicit_send TRUE +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-l2cap.txt bluez-5.23/android/pixit-l2cap.txt --- bluez-4.101/android/pixit-l2cap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-l2cap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,41 @@ +L2CAP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_client_class_of_device 100104 +TSPX_server_class_of_device 100104 +TSPX_security_enabled TRUE (*) +TSPX_delete_link_key FALSE +TSPX_pin_code 0000 +TSPX_flushto FFFF +TSPX_inmtu 02A0 +TSPX_no_fail_verditcs FALSE +TSPX_oumtu 02A0 +TSPX_iut_role_initiator TRUE +TSPX_psm 1011 (*) +TSPX_time_guard 180000 +TSPX_timer_ertx 120000 +TSPX_timer_ertx_max 300000 +TSPX_timer_ertx_min 60000 +TSPX_timer_rtx 10000 +TSPX_timer_rtx_max 60000 +TSPX_timer_rtx_min 1000 +TSPX_rfc_mode_tx_window_size 08 +TSPX_rfc_mode_max_transmit 03 +TSPX_rfc_mode_retransmission_timeout 07D0 +TSPX_rfc_mode_monitor_timeout 2EE0 +TSPX_rfc_mode_maximum_pdu_size 02A0 +TSPX_extended_window_size 0012 +TSPX_use_implicit_send TRUE +TSPX_use_dynamic_pin FALSE +TSPX_iut_SDU_size_in_bytes 144 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-map.txt bluez-5.23/android/pixit-map.txt --- bluez-4.101/android/pixit-map.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-map.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,44 @@ +MAP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to tester's phone number +$ - should be set to IUT e-mail address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_client_class_of_device 100204 +TSPX_delete_link_key FALSE +TSPX_get_object_name put.gif +TSPX_initial_path +TSPX_l2cap_psm 1001 +TSPX_no_confirmations FALSE +TSPX_pin_code 0000 +TSPX_rfcomm_channel 8 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_security_enabled TRUE +TSPX_server_class_of_device 100204 +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_Message_Access_rfcomm_channel 1 +TSPX_Message_Notification_rfcomm_channel 2 +TSPX_SPP_rfcomm_channel 03 +TSPX_filter_period_begin 20100101T000000 +TSPX_filter_period_end 20111231T125959 +TSPX_filter_recipient PTS +TSPX_filter_originator PTS +TSPX_default_message_upload_folder_in_msg draft +TSPX_default_test_folder_in_msg inbox +TSPX_message_notification_l2cap_psm 1003 +TSPX_message_notification_rfcomm_channel 9 +TSPX_upload_msg_phonenumber 123456789 (#) +TSPX_upload_msg_emailaddress IUT-email ($) +TSPX_Automation FALSE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-mcap.txt bluez-5.23/android/pixit-mcap.txt --- bluez-4.101/android/pixit-mcap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-mcap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,37 @@ +MCAP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_delete_link_key FALSE +TSPX_MCAP_DC_max 1 +TSPX_MCAP_l2cap_psm_control 1003 +TSPX_MCAP_l2cap_psm_control_B +TSPX_MCAP_l2cap_psm_data 1005 +TSPX_MCAP_l2cap_psm_data_B +TSPX_MCAP_bluetooth_clock_access_resolution 55 +TSPX_MCAP_create_mdl_configuration +TSPX_MCAP_data_channel_setup_mode +TSPX_MCAP_iut_initiate_connection TRUE (*) +TSPX_MCAP_mdep_id 12 +TSPX_MCAP_profile_name +TSPX_MCAP_sync_lead_time 0BB8 +TSPX_tester_role +TSPX_MCAP_timestamp_native_accuracy 1400 +TSPX_MCAP_timestamp_native_resolution 2233 +TSPX_MCAP_timestamp_required_accuracy 6400 +TSPX_host_class_of_device 100108 +TSPX_pin_code 0000 +TSPX_security_enabled TRUE (*) +TSPX_time_guard 6000000 +TSPX_use_dynamic_pin FALSE +TSPX_use_implicit_send TRUE +TSPX_verbose_implicit_send TRUE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-mps.txt bluez-5.23/android/pixit-mps.txt --- bluez-4.101/android/pixit-mps.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-mps.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,46 @@ +MPS PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +^ - should be set accordingly + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_avrcp_revision 1.5 (^) +TSPX_class_of_device 20050C +TSPX_establish_avdtp_stream TRUE +TSPX_iut_establishes_initial_condition FALSE +TSPX_tester_device A (*) +TSPX_media_directory +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 +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_l2cap_psm 1003 +TSPX_rfcomm_channel 8 +TSPX_no_confirmations FALSE +TSPX_AG_match_tester_BRSF_codec_negotiation_bit FALSE +TSPX_network_supports_correct_call_and_callstatus TRUE +TSPX_no_fail_verdicts FALSE +TSPX_packet_type_sco 00A0 +TSPX_phone_number 1234567 (^) +TSPX_phone_number_memory 1 +TSPX_phone_number_memory_invalid_index 9999 +TSPX_phone_number_type 129 +TSPX_scan_all_memory_dial_locations FALSE +TSPX_second_phone_number 1234567 (^) +TSPX_second_phone_type 129 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_server_channel_iut 00 +TSPX_server_channel_tester 01 +TSPX_verbose_implicit_send FALSE +TSPX_verify_CLIP_information TRUE +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-opp.txt bluez-5.23/android/pixit-opp.txt --- bluez-4.101/android/pixit-opp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-opp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,27 @@ +OPP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_supported_extension jpg (*) +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 -Nru bluez-4.101/android/pixit-pan.txt bluez-5.23/android/pixit-pan.txt --- bluez-4.101/android/pixit-pan.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-pan.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,30 @@ +PAN PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT or PTS Bluetooth address respectively + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_GN_class_of_device 020104 +TSPX_NAP_class_of_device 020300 +TSPX_PANU_class_of_device 020104 +TSPX_time_guard 300000 +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_security_enabled False +TSPX_pin_code 0000 +TSPX_delete_link_key False +TSPX_use_implicit_send True +TSPX_iut_ip_address 192.168.1.100 (*&) +TSPX_iut_port_number 4242 +TSPX_PTS_ip_address 192.168.168.100 +TSPX_PTS_port_number 4242 +TSPX_bd_addr_PTS 112233445566 (*&) +TSPX_checksum E851 +TSPX_secure_simple_pairing_pass_key_confirmation False +TSPX_iut_friendly_bt_name gprs-pc +TSPX_PTS_role_when_iut_is_PANU default +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-pbap.txt bluez-5.23/android/pixit-pbap.txt --- bluez-4.101/android/pixit-pbap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-pbap.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,36 @@ +PBAP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_auth_password 0000 +TSPX_auth_user_id PTS +TSPX_security_enabled True +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_pin_code 0000 +TSPX_time_guard 100000 +TSPX_use_implicit_send True +TSPX_client_class_of_device 100204 +TSPX_server_class_of_device 100204 +TSPX_PSE_vCardSelector 0000000000000001 +TSPX_delete_link_key False +TSPX_PBAP_profile_version 1 +TSPX_PBAP_supported_repositories 1 +TSPX_PBAP_rfcomm_channel 1 +TSPX_telecom_folder_path telecom +TSPX_secure_simple_pairing_pass_key_confirmation False +TSPX_check_downloaded_contents_after_disconnect False +TSPX_SPP_rfcomm_channel 03 +TSPX_l2cap_psm 1005 +TSPX_rfcomm_channel 2 +TSPX_obex_auth_password 0000 +TSPX_no_confirmations False +TSPX_PSE_vCardSelector 0000000000000001 +TSPX_Automation False +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-rfcomm.txt bluez-5.23/android/pixit-rfcomm.txt --- bluez-4.101/android/pixit-rfcomm.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-rfcomm.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,28 @@ +RFCOMM PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address +# - should be set to PTS's bin/audio folder + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 11223344556677 (*&) +TSPX_delete_link_key FALSE +TSPX_pin_code 1234 +TSPX_security_enabled TRUE +TSPX_time_guard 300000 +TSPX_use_implicit_send TRUE +TSPX_service_name_tester COM5 +TSPX_class_of_device_tester 200408 +TSPX_server_channel_iut 01 (*) +TSPX_verification_pattern_length 4 +TSPX_T1_Acknowledgement_Timer 20000 +TSPX_T1_Acknowledgement_Timer_Dlc 300000 +TSPX_T2_Response_Timer 20000 +TSPX_max_frame_size_iut 127 +TSPX_RPN_parameters_iut 0302001113 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-scpp.txt bluez-5.23/android/pixit-scpp.txt --- bluez-4.101/android/pixit-scpp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-scpp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,24 @@ +ScPP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_time_guard 180000 +TSPX_use_implicit_send TRUE +TSPX_tester_database_file C:/Program Files/... +TSPX_mtu_size 23 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_delete_link_key FALSE +TSPX_pin_code 0000 +TSPX_use_dynamic_pin FALSE +TSPX_delete_ltk FALSE +TSPX_security_enabled FALSE +TSPX_tester_appearance 0000 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-sm.txt bluez-5.23/android/pixit-sm.txt --- bluez-4.101/android/pixit-sm.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-sm.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,72 @@ +SM PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_SMP_pin_code 111111 +TSPX_OOB_Data 000000000000FE12036E5A + 889F4D +TSPX_peer_addr_type 00 +TSPX_own_addr_type 00 +TSPX_conn_interval_min 0190 +TSPX_conn_interval_max 0190 +TSPX_con_latency 0000 +TSPX_client_class_of_device 100104 +TSPX_server_class_of_device 100104 +TSPX_security_enabled TRUE +TSPX_delete_link_key TRUE +TSPX_pin_code 1234 +TSPX_ATTR_HANDLE 0000 +TSPX_ATTR_VALUE 000000000000000 +TSPX_delay_variation_in FFFFFFFF +TSPX_delay_variation_out FFFFFFFF +TSPX_flushto FFFF +TSPX_inmtu 02A0 +TSPX_inquiry_length 17 +TSPX_latency_in FFFFFFFF +TSPX_latency_out FFFFFFFF +TSPX_linkto 3000 +TSPX_max_nbr_retransmission 10 +TSPX_no_fail_verdicts FALSE +TSPX_outmtu 02A0 +TSPX_tester_role_optional L2CAP_ROLE_INITIATOR +TSPX_page_scan_mode 00 +TSPX_page_scan_repetition_mode 00 +TSPX_peak_bandwidth_in 00000000 +TSPX_peak_bandwidth_out 00000000 +TSPX_psm 0011 +TSPX_service_type_in 01 +TSPX_service_type_out 01 +TSPX_support_retransmissions TRUE +TSPX_time_guard 180000 +TSPX_timer_ertx 120000 +TSPX_timer_ertx_max 300000 +TSPX_timer_ertx_min 60000 +TSPX_timer_rtx 10000 +TSPX_timer_rtx_max 60000 +TSPX_timer_rtx_min 1000 +TSPX_token_bucket_size_in 00000000 +TSPX_token_bucket_size_out 00000000 +TSPX_token_rate_in 00000000 +TSPX_token_rate_out 00000000 +TSPX_rfc_mode_mode 03 +TSPX_rfc_mode_tx_window_size 08 +TSPX_rfc_mode_max_transmit 03 +TSPX_rfc_mode_retransmission_timeout 07D0 +TSPX_rfc_mode_monitor_timeout 2EE0 +TSPX_rfc_mode_maximum_pdu_size 02A0 +TSPX_extended_window_size 0012 +TSPX_use_implicit_send TRUE +TSPX_use_dynamic_pin FALSE +TSPX_iut_SDU_size_in_bytes 144 +TSPX_secure_simple_pairing_pass_key_confirmation FALSE +TSPX_Min_Encryption_Key_Length 07 +TSPX_Bonding_Flags 00 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pixit-spp.txt bluez-5.23/android/pixit-spp.txt --- bluez-4.101/android/pixit-spp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pixit-spp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,19 @@ +SPP PIXIT for the PTS tool. + +PTS version: 5.2 + +* - different than PTS defaults +& - should be set to IUT Bluetooth address + + Required PIXIT settings +------------------------------------------------------------------------------- +Parameter Name Value +------------------------------------------------------------------------------- +TSPX_bd_addr_iut 112233445566 (*&) +TSPX_security_enabled TRUE +TSPX_pin_code 0000 +TSPX_time_guard 300000 +TSPX_delete_link_key FALSE +TSPX_stop_immediately_when_fail TRUE +TSPX_class_of_device_tester 200408 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-a2dp.txt bluez-5.23/android/pts-a2dp.txt --- bluez-4.101/android/pts-a2dp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-a2dp.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,60 @@ +PTS test results for A2DP + +PTS version: 5.2 +Tested: 02-September-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + + Source (SRC) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_SRC_CC_BV_09_I PASS Start streaming +TC_SRC_CC_BV_10_I N/A +TC_SRC_REL_BV_01_I PASS Connect to PTS from IUT. When requested + disconnect from IUT +TC_SRC_REL_BV_02_I PASS +TC_SRC_SET_BV_01_I PASS Connect to PTS (open a2dp) +TC_SRC_SET_BV_02_I PASS +TC_SRC_SET_BV_03_I PASS Start streaming +TC_SRC_SET_BV_04_I PASS Start streaming +TC_SRC_SET_BV_05_I PASS IUT must be moved out of range +TC_SRC_SET_BV_06_I PASS IUT must be moved out of range +TC_SRC_SUS_BV_01_I PASS Stop streaming +TC_SRC_SUS_BV_02_I PASS +TC_SRC_SDP_BV_01_I PASS +TC_SRC_AS_BV_01_I PASS Requires checking if the output on the IUT is + correct +------------------------------------------------------------------------------- + + + Sink (SNK) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_SNK_CC_BV_01_I N/A +TC_SNK_CC_BV_02_I N/A +TC_SNK_CC_BV_03_I N/A +TC_SNK_CC_BV_04_I N/A +TC_SNK_CC_BV_05_I N/A +TC_SNK_CC_BV_06_I N/A +TC_SNK_CC_BV_07_I N/A +TC_SNK_CC_BV_08_I N/A +TC_SNK_REL_BV_01_I N/A +TC_SNK_REL_BV_02_I N/A +TC_SNK_SET_BV_01_I N/A +TC_SNK_SET_BV_02_I N/A +TC_SNK_SET_BV_03_I N/A +TC_SNK_SET_BV_04_I N/A +TC_SNK_SET_BV_05_I N/A +TC_SNK_SET_BV_06_I N/A +TC_SNK_SUS_BV_01_I N/A +TC_SNK_SUS_BV_02_I N/A +TC_SNK_SDP_BV_02_I N/A +TC_SNK_AS_BV_01_I N/A +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-avctp.txt bluez-5.23/android/pts-avctp.txt --- bluez-4.101/android/pts-avctp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-avctp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,42 @@ +PTS test results for AVCTP + +PTS version: 5.2 +Tested: 10-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + + Controller (CT) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_CT_CCM_BV_01_C N/A +TC_CT_CCM_BV_02_C N/A +TC_CT_CCM_BV_03_C N/A +TC_CT_CCM_BV_04_C N/A +TC_CT_CCM_BI_01_C N/A +TC_CT_NFR_BV_01_C N/A +TC_CT_NFR_BV_04_C PASS haltest: rc set_volume 5 +TC_CT_FRA_BV_01_C N/A +TC_CT_FRA_BV_04_C N/A +------------------------------------------------------------------------------- + + + Target (TG) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_TG_CCM_BV_01_C PASS +TC_TG_CCM_BV_02_C PASS +TC_TG_CCM_BV_03_C PASS +TC_TG_CCM_BV_04_C PASS +TC_TG_NFR_BV_02_C PASS +TC_TG_NFR_BV_03_C PASS +TC_TG_NFR_BI_01_C PASS +TC_TG_FRA_BV_02_C N/A Fragmentation not supported +TC_TG_FRA_BV_03_C N/A Fragmentation not supported +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-avdtp.txt bluez-5.23/android/pts-avdtp.txt --- bluez-4.101/android/pts-avdtp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-avdtp.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,238 @@ +PTS test results for AVDTP + +PTS version: 5.2 +Tested: 03-September-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_ACP_SNK_L2C_EM_BV_02_C N/A +TC_ACP_SNK_SIG_FRA_BV_01_C N/A +TC_ACP_SNK_SIG_FRA_BV_02_C N/A +TC_ACP_SNK_SIG_SEC_BI_01_C N/A +TC_ACP_SNK_SIG_SEC_BV_02_C N/A +TC_ACP_SNK_SIG_SMG_BV_06_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_08_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_10_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_12_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_14_C N/A +TC_ACP_SNK_SIG_SMG_BV_16_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_18_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_20_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_22_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_24_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_26_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BV_27_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_ESR05_BV_14_C N/A +TC_ACP_SNK_SIG_SMG_BI_02_C N/A +TC_ACP_SNK_SIG_SMG_BI_03_C N/A +TC_ACP_SNK_SIG_SMG_BI_05_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BI_06_C N/A +TC_ACP_SNK_SIG_SMG_BI_08_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BI_09_C N/A +TC_ACP_SNK_SIG_SMG_BI_11_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BI_12_C N/A +TC_ACP_SNK_SIG_SMG_BI_14_C N/A +TC_ACP_SNK_SIG_SMG_BI_15_C N/A +TC_ACP_SNK_SIG_SMG_BI_17_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BI_18_C N/A +TC_ACP_SNK_SIG_SMG_BI_20_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BI_21_C N/A +TC_ACP_SNK_SIG_SMG_BI_23_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BI_24_C N/A +TC_ACP_SNK_SIG_SMG_BI_26_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BI_27_C N/A +TC_ACP_SNK_SIG_SMG_BI_28_C N/A +TC_ACP_SNK_SIG_SMG_BI_29_C N/A +TC_ACP_SNK_SIG_SMG_BI_33_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_BI_34_C N/A +TC_ACP_SNK_SIG_SMG_ESR04_BI_28_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SMG_ESR05_BI_15_C N/A +TC_ACP_SNK_SIG_SYN_BV_01_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SYN_BV_02_C PASS avdtptest -d SINK -l -p +TC_ACP_SNK_SIG_SYN_BV_03_C PASS avdtptest -d SINK -l +TC_ACP_SNK_SIG_SYN_BV_04_C PASS avdtptest -d SINK -l -p +TC_ACP_SNK_TRA_BTR_BI_01_C PASS avdtptest -d SINK -l +TC_ACP_SNK_TRA_BTR_BV_02_C PASS avdtptest -d SINK -l +TC_ACP_SNK_TRA_MUX_BI_01_C N/A +TC_ACP_SNK_TRA_MUX_BV_05_C N/A +TC_ACP_SNK_TRA_MUX_BV_06_C N/A +TC_ACP_SNK_TRA_REC_BI_01_C N/A +TC_ACP_SNK_TRA_REC_BV_01_C N/A +TC_ACP_SNK_TRA_REC_BV_02_C N/A +TC_ACP_SNK_TRA_REP_BI_01_C N/A +TC_ACP_SNK_TRA_REP_BV_01_C N/A +TC_ACP_SNK_TRA_REP_BV_02_C N/A +TC_ACP_SNK_TRA_REP_ESR02_BI_01_C N/A +TC_ACP_SNK_TRA_RHC_BI_01_C N/A +TC_ACP_SNK_TRA_RHC_BV_01_C N/A +TC_ACP_SNK_TRA_RHC_BV_02_C N/A +TC_ACP_SRC_L2C_EM_BV_02_C N/A +TC_ACP_SRC_SIG_FRA_BV_01_C N/A +TC_ACP_SRC_SIG_FRA_BV_02_C N/A +TC_ACP_SRC_SIG_SEC_BI_01_C N/A +TC_ACP_SRC_SIG_SEC_BV_02_C N/A +TC_ACP_SRC_SIG_SMG_BV_06_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_08_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_10_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_12_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_14_C N/A +TC_ACP_SRC_SIG_SMG_BV_16_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_18_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_20_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_22_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_24_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_26_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BV_27_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_ESR05_BV_14_C N/A +TC_ACP_SRC_SIG_SMG_BI_02_C N/A +TC_ACP_SRC_SIG_SMG_BI_03_C N/A +TC_ACP_SRC_SIG_SMG_BI_05_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BI_06_C N/A +TC_ACP_SRC_SIG_SMG_BI_08_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BI_09_C N/A +TC_ACP_SRC_SIG_SMG_BI_11_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BI_12_C N/A +TC_ACP_SRC_SIG_SMG_BI_14_C N/A +TC_ACP_SRC_SIG_SMG_BI_15_C N/A +TC_ACP_SRC_SIG_SMG_BI_17_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BI_18_C N/A +TC_ACP_SRC_SIG_SMG_BI_20_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BI_21_C N/A +TC_ACP_SRC_SIG_SMG_BI_23_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BI_24_C N/A +TC_ACP_SRC_SIG_SMG_BI_26_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BI_27_C N/A +TC_ACP_SRC_SIG_SMG_BI_28_C N/A +TC_ACP_SRC_SIG_SMG_BI_29_C N/A +TC_ACP_SRC_SIG_SMG_BI_33_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_BI_34_C N/A +TC_ACP_SRC_SIG_SMG_ESR04_BI_28_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SMG_ESR05_BI_15_C N/A +TC_ACP_SRC_SIG_SYN_BV_05_C PASS avdtptest -d SRC -l +TC_ACP_SRC_SIG_SYN_BV_06_C PASS avdtptest -d SRC -l +TC_ACP_SRC_TRA_BTR_BI_01_C PASS avdtptest -d SRC -l +TC_ACP_SRC_TRA_BTR_BV_01_C PASS avdtptest -d SRC -l -p -s start +TC_ACP_SRC_TRA_MUX_BI_01_C N/A +TC_ACP_SRC_TRA_MUX_BV_05_C N/A +TC_ACP_SRC_TRA_MUX_BV_06_C N/A +TC_ACP_SRC_TRA_REC_BI_01_C N/A +TC_ACP_SRC_TRA_REC_BV_01_C N/A +TC_ACP_SRC_TRA_REC_BV_02_C N/A +TC_ACP_SRC_TRA_REP_BI_01_C N/A +TC_ACP_SRC_TRA_REP_BV_01_C N/A +TC_ACP_SRC_TRA_REP_BV_02_C N/A +TC_ACP_SRC_TRA_REP_ESR02_BI_01_C N/A +TC_ACP_SRC_TRA_RHC_BI_01_C N/A +TC_ACP_SRC_TRA_RHC_BV_01_C N/A +TC_ACP_SRC_TRA_RHC_BV_02_C N/A +TC_INT_SNK_L2C_BM_BV_03_C N/A +TC_INT_SNK_L2C_BM_BV_06_C N/A +TC_INT_SNK_SIG_FRA_BV_01_C N/A +TC_INT_SNK_SIG_FRA_BV_02_C N/A +TC_INT_SNK_SIG_SEC_BV_01_C N/A +TC_INT_SNK_SIG_SMG_BV_05_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SMG_BV_07_C PASS avdtptest -d SINK -l -p + -v 0x0100 +TC_INT_SNK_SIG_SMG_BV_09_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SMG_BV_11_C PASS avdtptest -d SINK -l -s getconf +TC_INT_SNK_SIG_SMG_BV_13_C N/A +TC_INT_SNK_SIG_SMG_BV_15_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SMG_BV_19_C PASS avdtptest -d SINK -l -s close +TC_INT_SNK_SIG_SMG_BV_23_C PASS avdtptest -d SINK -l -p -s abort +TC_INT_SNK_SIG_SMG_BV_25_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SMG_BV_28_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SMG_BV_31_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SMG_ESR05_BV_13_C N/A +TC_INT_SNK_SIG_SMG_BI_01_C N/A +TC_INT_SNK_SIG_SMG_BI_04_C N/A +TC_INT_SNK_SIG_SMG_BI_07_C N/A +TC_INT_SNK_SIG_SMG_BI_10_C N/A +TC_INT_SNK_SIG_SMG_BI_13_C N/A +TC_INT_SNK_SIG_SMG_BI_16_C N/A +TC_INT_SNK_SIG_SMG_BI_19_C N/A +TC_INT_SNK_SIG_SMG_BI_22_C N/A +TC_INT_SNK_SIG_SMG_BI_25_C N/A +TC_INT_SNK_SIG_SMG_BI_30_C PASS avdtptest -d SINK -l -p + -v 0x0100 +TC_INT_SNK_SIG_SMG_BI_32_C N/A +TC_INT_SNK_SIG_SMG_BI_35_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SMG_BI_36_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SMG_ESR05_BI_13_C N/A +TC_INT_SNK_SIG_SYN_BV_01_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SYN_BV_02_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_SIG_SYN_BV_03_C PASS avdtptest -d SINK -l +TC_INT_SNK_SIG_SYN_BV_04_C PASS avdtptest -d SINK -l -p +TC_INT_SNK_TRA_BTR_BI_01_C PASS avdtptest -d SINK -l +TC_INT_SNK_TRA_BTR_BV_02_C PASS avdtptest -d SINK -l +TC_INT_SNK_TRA_MUX_BI_01_C N/A +TC_INT_SNK_TRA_MUX_BV_05_C N/A +TC_INT_SNK_TRA_MUX_BV_06_C N/A +TC_INT_SNK_TRA_REC_BI_01_C N/A +TC_INT_SNK_TRA_REC_BV_01_C N/A +TC_INT_SNK_TRA_REC_BV_02_C N/A +TC_INT_SNK_TRA_REP_BI_01_C N/A +TC_INT_SNK_TRA_REP_BV_01_C N/A +TC_INT_SNK_TRA_REP_BV_02_C N/A +TC_INT_SNK_TRA_REP_ESR02_BI_01_C N/A +TC_INT_SNK_TRA_RHC_BI_01_C N/A +TC_INT_SNK_TRA_RHC_BV_01_C N/A +TC_INT_SNK_TRA_RHC_BV_02_C N/A +TC_INT_SRC_L2C_BM_BV_03_C N/A +TC_INT_SRC_L2C_BM_BV_06_C N/A +TC_INT_SRC_SIG_FRA_BV_01_C N/A +TC_INT_SRC_SIG_FRA_BV_02_C N/A +TC_INT_SRC_SIG_SEC_BV_01_C N/A +TC_INT_SRC_SIG_SMG_BV_05_C PASS avdtptest -d SRC -l -p +TC_INT_SRC_SIG_SMG_BV_07_C PASS avdtptest -d SRC -l -p -v 0x0100 +TC_INT_SRC_SIG_SMG_BV_09_C PASS avdtptest -d SRC -l -p +TC_INT_SRC_SIG_SMG_BV_11_C PASS avdtptest -d SRC -l -s getconf +TC_INT_SRC_SIG_SMG_BV_13_C N/A +TC_INT_SRC_SIG_SMG_BV_15_C PASS avdtptest -d SRC -l -p +TC_INT_SRC_SIG_SMG_BV_17_C PASS avdtptest -d SRC -l -p -s start +TC_INT_SRC_SIG_SMG_BV_19_C PASS avdtptest -d SRC -l -s close +TC_INT_SRC_SIG_SMG_BV_21_C PASS avdtptest -d SRC -l -s suspend +TC_INT_SRC_SIG_SMG_BV_23_C PASS avdtptest -d SRC -l -p -s abort +TC_INT_SRC_SIG_SMG_BV_25_C PASS avdtptest -d SRC -l -p +TC_INT_SRC_SIG_SMG_BV_28_C PASS avdtptest -d SRC -l -p +TC_INT_SRC_SIG_SMG_BV_31_C PASS avdtptest -d SRC -l -p +TC_INT_SRC_SIG_SMG_ESR05_BV_13_C N/A +TC_INT_SRC_SIG_SMG_BI_01_C N/A +TC_INT_SRC_SIG_SMG_BI_04_C N/A +TC_INT_SRC_SIG_SMG_BI_07_C N/A +TC_INT_SRC_SIG_SMG_BI_10_C N/A +TC_INT_SRC_SIG_SMG_BI_13_C N/A +TC_INT_SRC_SIG_SMG_BI_16_C N/A +TC_INT_SRC_SIG_SMG_BI_19_C N/A +TC_INT_SRC_SIG_SMG_BI_22_C N/A +TC_INT_SRC_SIG_SMG_BI_25_C N/A +TC_INT_SRC_SIG_SMG_BI_30_C PASS avdtptest -d SRC -l -p -v 0x0100 +TC_INT_SRC_SIG_SMG_BI_32_C N/A +TC_INT_SRC_SIG_SMG_BI_35_C PASS avdtptest -d SRC -l -p +TC_INT_SRC_SIG_SMG_BI_36_C PASS avdtptest -d SRC -l -p +TC_INT_SRC_SIG_SMG_ESR05_BI_13_C N/A +TC_INT_SRC_SIG_SYN_BV_05_C PASS avdtptest -d SRC -l +TC_INT_SRC_SIG_SYN_BV_06_C PASS avdtptest -d SRC -l +TC_INT_SRC_TRA_BTR_BI_01_C PASS avdtptest -d SRC -l +TC_INT_SRC_TRA_BTR_BV_01_C PASS avdtptest -d SRC -l -p -s start +TC_INT_SRC_TRA_MUX_BI_01_C N/A +TC_INT_SRC_TRA_MUX_BV_05_C N/A +TC_INT_SRC_TRA_MUX_BV_06_C N/A +TC_INT_SRC_TRA_REC_BI_01_C N/A +TC_INT_SRC_TRA_REC_BV_01_C N/A +TC_INT_SRC_TRA_REC_BV_02_C N/A +TC_INT_SRC_TRA_REP_BI_01_C N/A +TC_INT_SRC_TRA_REP_BV_01_C N/A +TC_INT_SRC_TRA_REP_BV_02_C N/A +TC_INT_SRC_TRA_REP_ESR02_BI_01_C N/A +TC_INT_SRC_TRA_RHC_BI_01_C N/A +TC_INT_SRC_TRA_RHC_BV_01_C N/A +TC_INT_SRC_TRA_RHC_BV_02_C N/A +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-avrcp.txt bluez-5.23/android/pts-avrcp.txt --- bluez-4.101/android/pts-avrcp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-avrcp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,199 @@ +PTS test results for AVRCP + +PTS version: 5.2 +Tested: 15-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + + Controller (CT) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_CT_BGN_BV_01_I N/A +TC_CT_BGN_BV_02_I N/A +TC_CT_CEC_BV_01_I N/A +TC_CT_CEC_BV_02_I N/A +TC_CT_CFG_BV_01_C N/A +TC_CT_CON_BV_01_C N/A +TC_CT_CON_BV_03_C N/A +TC_CT_CRC_BV_01_I N/A +TC_CT_CRC_BV_02_I N/A +TC_CT_ICC_BV_01_I N/A +TC_CT_ICC_BV_02_I N/A +TC_CT_MCN_CB_BV_01_C N/A +TC_CT_MCN_CB_BV_01_I N/A +TC_CT_MCN_CB_BV_02_I N/A +TC_CT_MCN_CB_BV_03_I N/A +TC_CT_MCN_CB_BV_04_C N/A +TC_CT_MCN_CB_BV_04_I N/A +TC_CT_MCN_CB_BV_05_I N/A +TC_CT_MCN_CB_BV_06_I N/A +TC_CT_MCN_CB_BV_07_C N/A +TC_CT_MCN_CB_BV_07_I N/A +TC_CT_MCN_NP_BV_01_C N/A +TC_CT_MCN_NP_BV_01_I N/A +TC_CT_MCN_NP_BV_02_I N/A +TC_CT_MCN_NP_BV_03_C N/A +TC_CT_MCN_NP_BV_03_I N/A +TC_CT_MCN_NP_BV_04_I N/A +TC_CT_MCN_NP_BV_05_C N/A +TC_CT_MCN_NP_BV_05_I N/A +TC_CT_MCN_NP_BV_06_I N/A +TC_CT_MCN_NP_BV_07_I N/A +TC_CT_MCN_NP_BV_08_C N/A +TC_CT_MCN_SRC_BV_01_C N/A +TC_CT_MCN_SRC_BV_01_I N/A +TC_CT_MCN_SRC_BV_02_I N/A +TC_CT_MCN_SRC_BV_03_C N/A +TC_CT_MCN_SRC_BV_03_I N/A +TC_CT_MCN_SRC_BV_04_I N/A +TC_CT_MCN_SRC_BV_05_C N/A +TC_CT_MDI_BV_01_C N/A +TC_CT_MDI_BV_03_C N/A +TC_CT_MPS_BV_01_C N/A +TC_CT_MPS_BV_01_I N/A +TC_CT_MPS_BV_02_I N/A +TC_CT_MPS_BV_03_C N/A +TC_CT_MPS_BV_03_I N/A +TC_CT_MPS_BV_08_C N/A +TC_CT_NFY_BV_01_C N/A +TC_CT_PAS_BV_01_C N/A +TC_CT_PAS_BV_03_C N/A +TC_CT_PAS_BV_05_C N/A +TC_CT_PAS_BV_07_C N/A +TC_CT_PAS_BV_09_C N/A +TC_CT_PAS_BV_11_C N/A +TC_CT_PTH_BV_01_C N/A +TC_CT_PTH_BV_02_C N/A +TC_CT_PTT_BV_01_I N/A +TC_CT_PTT_BV_02_I N/A +TC_CT_PTT_BV_03_I N/A +TC_CT_PTT_BV_04_I N/A +TC_CT_PTT_BV_05_I N/A +TC_CT_RCR_BV_01_C N/A +TC_CT_RCR_BV_03_C N/A +TC_CT_VLH_BI_03_C PASS Send SetAbsolute Volume command by pressing + volume up or down buttons then adb logcat and + check VOLUME_CHANGED value +TC_CT_VLH_BI_04_C PASS adb logcat: check VOLUME_CHANGED value +TC_CT_VLH_BV_01_C PASS Send SetAbsolute Volume command by pressing + volume up or down buttons +TC_CT_VLH_BV_01_I PASS adb logcat: check VOLUME_CHANGED value +TC_CT_VLH_BV_02_I PASS Send SetAbsolute Volume command by pressing + volume up or down buttons +TC_CT_VLH_BV_03_C PASS +------------------------------------------------------------------------------- + + + Target (TG) +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_TG_BGN_BV_01_I N/A +TC_TG_BGN_BV_02_I N/A +TC_TG_CEC_BV_01_I PASS +TC_TG_CEC_BV_02_I PASS +TC_TG_CFG_BI_01_C PASS +TC_TG_CFG_BV_02_C PASS +TC_TG_CON_BV_02_C N/A +TC_TG_CON_BV_04_C N/A +TC_TG_CON_BV_05_C N/A +TC_TG_CRC_BV_01_I PASS +TC_TG_CRC_BV_02_I PASS Disconnect from PTS +TC_TG_ICC_BV_01_I PASS +TC_TG_ICC_BV_02_I PASS +TC_TG_INV_BI_01_C PASS +TC_TG_INV_BI_02_C N/A +TC_TG_MCN_CB_BI_01_C N/A +TC_TG_MCN_CB_BI_02_C N/A +TC_TG_MCN_CB_BI_03_C N/A +TC_TG_MCN_CB_BI_04_C N/A +TC_TG_MCN_CB_BI_05_C N/A +TC_TG_MCN_CB_BV_01_I N/A +TC_TG_MCN_CB_BV_02_C N/A +TC_TG_MCN_CB_BV_02_I N/A +TC_TG_MCN_CB_BV_03_C N/A +TC_TG_MCN_CB_BV_03_I N/A +TC_TG_MCN_CB_BV_04_I N/A +TC_TG_MCN_CB_BV_05_C N/A +TC_TG_MCN_CB_BV_05_I N/A +TC_TG_MCN_CB_BV_06_C N/A +TC_TG_MCN_CB_BV_06_I N/A +TC_TG_MCN_CB_BV_07_I N/A +TC_TG_MCN_CB_BV_08_C N/A +TC_TG_MCN_CB_BV_09_C N/A +TC_TG_MCN_CB_BV_10_C N/A +TC_TG_MCN_CB_BV_11_C N/A +TC_TG_MCN_NP_BI_01_C N/A +TC_TG_MCN_NP_BI_02_C N/A +TC_TG_MCN_NP_BV_01_I N/A +TC_TG_MCN_NP_BV_02_C N/A +TC_TG_MCN_NP_BV_02_I N/A +TC_TG_MCN_NP_BV_03_I N/A +TC_TG_MCN_NP_BV_04_C N/A +TC_TG_MCN_NP_BV_04_I N/A +TC_TG_MCN_NP_BV_05_I N/A +TC_TG_MCN_NP_BV_06_C N/A +TC_TG_MCN_NP_BV_06_I N/A +TC_TG_MCN_NP_BV_07_C N/A +TC_TG_MCN_NP_BV_07_I N/A +TC_TG_MCN_NP_BV_09_C N/A +TC_TG_MCN_SRC_BV_01_I N/A +TC_TG_MCN_SRC_BV_02_C N/A +TC_TG_MCN_SRC_BV_02_I N/A +TC_TG_MCN_SRC_BV_03_I N/A +TC_TG_MCN_SRC_BV_04_C N/A +TC_TG_MCN_SRC_BV_04_I N/A +TC_TG_MCN_SRC_BV_06_C N/A +TC_TG_MDI_BV_02_C PASS +TC_TG_MDI_BV_04_C PASS +TC_TG_MDI_BV_05_C PASS +TC_TG_MPS_BI_01_C N/A +TC_TG_MPS_BI_02_C N/A +TC_TG_MPS_BV_01_I N/A +TC_TG_MPS_BV_02_C N/A +TC_TG_MPS_BV_02_I N/A +TC_TG_MPS_BV_03_I N/A +TC_TG_MPS_BV_04_C N/A +TC_TG_MPS_BV_05_C N/A +TC_TG_MPS_BV_06_C N/A +TC_TG_MPS_BV_07_C N/A +TC_TG_MPS_BV_09_C N/A +TC_TG_MPS_BV_10_C N/A +TC_TG_NFY_BI_01_C PASS +TC_TG_NFY_BV_02_C PASS Change track when requested +TC_TG_NFY_BV_03_C N/A +TC_TG_NFY_BV_04_C PASS +TC_TG_NFY_BV_05_C PASS +TC_TG_NFY_BV_06_C N/A +TC_TG_NFY_BV_07_C N/A +TC_TG_NFY_BV_08_C PASS +TC_TG_PAS_BI_01_C N/A +TC_TG_PAS_BI_02_C N/A +TC_TG_PAS_BI_03_C N/A +TC_TG_PAS_BI_04_C N/A +TC_TG_PAS_BI_05_C N/A +TC_TG_PAS_BV_02_C N/A +TC_TG_PAS_BV_04_C N/A +TC_TG_PAS_BV_06_C N/A +TC_TG_PAS_BV_08_C N/A +TC_TG_PAS_BV_10_C N/A +TC_TG_PTT_BV_01_I PASS +TC_TG_PTT_BV_02_I PASS +TC_TG_PTT_BV_03_I N/A +TC_TG_PTT_BV_04_I N/A +TC_TG_PTT_BV_05_I N/A +TC_TG_RCR_BV_02_C PASS +TC_TG_RCR_BV_04_C PASS +TC_TG_VLH_BI_01_C N/A +TC_TG_VLH_BI_02_C N/A +TC_TG_VLH_BV_01_I N/A +TC_TG_VLH_BV_02_C N/A +TC_TG_VLH_BV_02_I N/A +TC_TG_VLH_BV_04_C N/A +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-did.txt bluez-5.23/android/pts-did.txt --- bluez-4.101/android/pts-did.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-did.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,20 @@ +PTS test results for DID + +PTS version: 5.2 +Tested: 09-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_SDI_BV_1_I PASS IUT must be discoverable +TC_SDI_BV_2_I PASS IUT must be discoverable +TC_SDI_BV_3_I PASS IUT must be discoverable +TC_SDI_BV_4_I PASS IUT must be discoverable +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-dis.txt bluez-5.23/android/pts-dis.txt --- bluez-4.101/android/pts-dis.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-dis.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,37 @@ +PTS test results for DIS + +PTS version: 5.2 +Tested: 29-July-2014 +Android version: 4.4.4 + +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_SD_BV_01_C PASS +TC_DEC_BV_01_C PASS +TC_DEC_BV_02_C PASS +TC_DEC_BV_03_C PASS +TC_DEC_BV_04_C PASS +TC_DEC_BV_05_C PASS +TC_DEC_BV_06_C PASS +TC_DEC_BV_07_C N/A +TC_DEC_BV_08_C N/A +TC_DEC_BV_09_C N/A +TC_CR_BV_01_C PASS +TC_CR_BV_02_C PASS +TC_CR_BV_03_C PASS +TC_CR_BV_04_C PASS +TC_CR_BV_05_C PASS +TC_CR_BV_06_C PASS +TC_CR_BV_07_C N/A +TC_CR_BV_08_C N/A +TC_CR_BV_09_C N/A +TC_SDP_BV_01_C PASS +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-gap.txt bluez-5.23/android/pts-gap.txt --- bluez-4.101/android/pts-gap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-gap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,432 @@ +PTS test results for GAP + +PTS version: 5.2 +Tested: 01-August-2014 +Android version: 4.4.4 +Kernel version: 3.18 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_MOD_NDIS_BV_01_C PASS IUT must be non-discoverable +TC_MOD_LDIS_BV_01_C PASS btmgmt discov limited 30 +TC_MOD_LDIS_BV_02_C PASS btmgmt discov limited 30 +TC_MOD_LDIS_BV_03_C PASS btmgmt discov limited 30 +TC_MOD_GDIS_BV_01_C PASS IUT must be discoverable +TC_MOD_GDIS_BV_02_C PASS IUT must be discoverable +TC_MOD_NCON_BV_01_C PASS btmgmt connectable off +TC_MOD_CON_BV_01_C PASS btmgmt connectable on +TC_BROB_BCST_BV_01_C N/A +TC_BROB_BCST_BV_02_C N/A +TC_BROB_BCST_BV_03_C N/A +TC_BROB_OBSV_BV_01_C N/A +TC_BROB_OBSV_BV_02_C N/A +TC_BROB_OBSV_BV_03_C N/A +TC_BROB_OBSV_BV_04_C N/A +TC_BROB_OBSV_BV_05_C N/A +TC_DISC_NONM_BV_01_C PASS btmgmt connectable off + btmgmt advertising on +TC_DISC_NONM_BV_02_C PASS btmgmt connectable on + btmgmt discov off + btmgmt advertising on +TC_DISC_LIMM_BV_01_C PASS btmgmt connectable on + btmgmt discov off + + btmgmt discov limited 30 +TC_DISC_LIMM_BV_02_C PASS btmgmt connectable on + btmgmt advertising on + btmgmt discov limited 30 +TC_DISC_LIMM_BV_03_C PASS btmgmt connectable on + btmgmt discov off + + btmgmt discov limited 30 +TC_DISC_LIMM_BV_04_C PASS btmgmt connectable on + btmgmt discov off + btmgmt power off + btmgmt bredr off + btmgmt power on + btmgmt discov limited 30 +TC_DISC_GENM_BV_01_C PASS btmgmt connectable on + btmgmt discov on + +TC_DISC_GENM_BV_02_C PASS btmgmt connectable on + btmgmt advertising on + btmgmt discov on +TC_DISC_GENM_BV_03_C PASS btmgmt connectable on + btmgmt discov on + +TC_DISC_GENM_BV_04_C PASS btmgmt connectable on + btmgmt power off + btmgmt le on + btmgmt bredr off + btmgmt power on + btmgmt discov on + btmgmt advertising on +TC_DISC_LIMP_BV_01_C PASS btmgmt find -l + PTS AD flags must have bit 1 unset and bit 0 set +TC_DISC_LIMP_BV_02_C PASS btmgmt find -l + PTS AD flags must have bit 1 set and bit 0 unset +TC_DISC_LIMP_BV_03_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_LIMP_BV_04_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_LIMP_BV_05_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_GENP_BV_01_C PASS btmgmt find -l + PTS AD flags must have bit 1 set and bit 0 unset +TC_DISC_GENP_BV_02_C PASS btmgmt find -l + PTS AD flags must have bit 1 unset and bit 0 set +TC_DISC_GENP_BV_03_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_GENP_BV_04_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_DISC_GENP_BV_05_C PASS btmgmt find -l + PTS AD flags must have bit 1 and bit 0 unset +TC_IDLE_GIN_BV_01_C PASS Start discovery from IUT +TC_IDLE_LIN_BV_01_C PASS hcitool scan --iac=liac +TC_IDLE_NAMP_BV_01_C PASS possible to PASS using haltest following steps: + gattc - register client, connect to PTS, search + all services, get characteristic and then read + characteristic (name) +TC_IDLE_NAMP_BV_02_C PASS haltest: gatts connect +TC_CONN_NCON_BV_01_C PASS btmgmt connectable off + btmgmt advertising on + +TC_CONN_NCON_BV_02_C PASS + Note: non-connectable and discoverable ? +TC_CONN_NCON_BV_03_C PASS + Note: non-connectable and discoverable ? +TC_CONN_DCON_BV_01_C PASS btmgmt connectable on + btmgmt advertising on +TC_CONN_DCON_BV_02_C N/A +TC_CONN_DCON_BV_03_C N/A +TC_CONN_UCON_BV_01_C PASS btmgmt connectable on + btmgmt advertising on +TC_CONN_UCON_BV_02_C PASS btmgmt connectable on + btmgmt discov on + btmgmt advertising on +TC_CONN_UCON_BV_03_C PASS btmgmt connectable on + btmgmt advertising on + btmgmt discov limited 30 +TC_CONN_UCON_BV_04_C N/A +TC_CONN_UCON_BV_05_C N/A +TC_CONN_ACEP_BV_01_C PASS debugfs: + echo -n "add 0 1" > /sys/kernel/ + debug/bluetooth/hciX/le_auto_conn + btmgmt le on + btmgmt power on + btmgmt disconnect -t 1 + debugfs: + echo -n "clr" > /sys/kernel/debug/bluetooth/ + hciX/le_auto_conn +TC_CONN_ACEP_BV_02_C INC Privacy feature - PTS issue #12308 + Note: PTS issue was closed. Test Spec Errata + was filled instead. + TSE issue #5825 +TC_CONN_GCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_GCEP_BV_02_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_GCEP_BV_03_C PASS gattc connect + bluetooth create_bond + gattc connect + gattc disconnect +TC_CONN_GCEP_BV_04_C INC Privacy feature - PTS issue #12308 +TC_CONN_SCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_SCEP_BV_02_C INC Privacy feature - PTS issue #12308 +TC_CONN_DCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS +TC_CONN_DCEP_BV_02_C INC Privacy feature - PTS issue #12308 +TC_CONN_DCEP_BV_03_C PASS gattc connect +TC_CONN_DCEP_BV_04_C PASS gattc connect + gattc create_bond + gattc connect + gattc disconnect +TC_CONN_CPUP_BV_01_C N/A +TC_CONN_CPUP_BV_02_C N/A +TC_CONN_CPUP_BV_03_C N/A +TC_CONN_CPUP_BV_04_C PASS gattc register_client + gattc connect + gattc disconnect +TC_CONN_CPUP_BV_05_C PASS gattc register_client + gattc connect + gattc disconnect +TC_CONN_CPUP_BV_06_C PASS gattc register_client + gattc connect 1 + hcitool lecup 0x0008 0x0C7B 0x0010 + 0x0014 + gattc disconnect + +TC_CONN_TERM_BV_01_C PASS +TC_CONN_PRDA_BV_01_C INC PTS issue #12207 +TC_CONN_PRDA_BV_02_C INC PTS issue #12310 + Note: PTS issues #12207 & #12310 are claimed + to be resolved by the ETS provided in PTS issue + #12312 however it does not solve the problem +TC_BOND_NBON_BV_01_C PASS +TC_BOND_NBON_BV_02_C PASS haltest: gattc register_client + gattc connect
+ bluetooth create_bond
+ bluetooth remove_bond
+TC_BOND_NBON_BV_03_C PASS haltest: gattc listen +TC_BOND_BON_BV_01_C PASS PTS issue #12503 + possible to pass without MITM: + btmgmt power on + btmgmt le on + btmgmt ssp on + btmgmt connectable on + btmgmt discov on + btmgmt advertising on + btmgmt pairable on + To bond with PTS execute + btmgmt pair -t 0x01 -c 0x03 +TC_BOND_BON_BV_02_C PASS +TC_BOND_BON_BV_03_C PASS gattc register_client + gattc listen 1 +TC_BOND_BON_BV_04_C PASS haltest: gattc_register_client + gattc connect
+ gattc disconnect + gattc connect
+ gattc test_command 226 0 2 +TC_SEC_AUT_BV_11_C PASS haltest: gattc register_client + gatts register_server + gatts add_service 2 3 + gatts add_characteristic 2 1b 10 68 + gatts start_service 2 1b 1 + gattc listen + PTS asks for handle with Insufficient auth + bluetooth ssp_reply + gatts send_response +TC_SEC_AUT_BV_12_C PASS haltest: gatts register_server + gatts add_service 2 3 + gatts add_characteristic 2 + 10 68 + gatts start_service 2 1 + gatts connect + PTS asks for handle with Insufficient auth + bluetooth ssp_reply + gatts send_response +TC_SEC_AUT_BV_13_C PASS haltest: gatts register_server + gatts add_service 2 3 + gatts add_characteristic 2 + 10 68 + gatts start_service 2 1 + gatts connect + PTS asks for handle with Insufficient auth + bluetooth ssp_reply + gatts send_response +TC_SEC_AUT_BV_14_C PASS haltest:gattc register_client + gatts register_server + gatts add_service 2 3 + gatts add_characteristic 2 1b 10 68 + gatts start_service 2 1b 1 + gattc listen + PTS asks for handle with Insufficient auth + bluetooth ssp_reply + gatts send_response +TC_SEC_AUT_BV_15_C N/A +TC_SEC_AUT_BV_16_C PASS haltest: gatts register_server + gatts add_service 2 3 + gatts add_characteristic 2 + 10 34 + gatts start_service 2 1 + gatts connect + gatts disconnect + gatts connect + PTS asks for handle with Insufficient encrypt + bluetooth ssp_reply + gatts send_response +TC_SEC_AUT_BV_17_C PASS +TC_SEC_AUT_BV_18_C PASS haltest: gattc register_client + gattc listen + gattc search_service + gattc get_characteristic + gattc read_characteristic + bluetooth create_bond + gattc read_characteristic +TC_SEC_AUT_BV_19_C PASS +TC_SEC_AUT_BV_20_C INC PTS issue #12284 +TC_SEC_AUT_BV_21_C PASS haltest: gattc register_client + gattc connect + bluetooth create_bond + gattc connect +TC_SEC_AUT_BV_22_C PASS btmgmt io-cap 3 + haltest: gattc register_client + gattc listen + gattc test_command 226 1 +TC_SEC_AUT_BV_23_C PASS haltest: gatts register_server + gatts add_service 2 3 + gatts add_characteristic 2 + 10 34 + gatts start_service 2 1 + gattc register_client + gattc listen + bluetooth ssp_reply + gatts send_response +TC_SEC_AUT_BV_24_C PASS haltest: gatts register_server + gatts add_service 2 3 + gatts add_characteristic 2 + 10 34 + gatts start_service 2 1 + gatts connect + bluetooth ssp_reply + gatts disconnect + gatts connect + PTS asks for handle with insufficient encryption + gatts send_response +TC_SEC_CSIGN_BV_01_C PASS haltest: + gattc connect + bluetooth create_bond + gattc connect + gattc write_characteristic: 4 + gattc disconnect +TC_SEC_CSIGN_BV_02_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 66 + 129 + gatts start_service + gatts disconnect + gattc disconnect +TC_SEC_CSIGN_BI_01_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 66 + 129 + gatts start_service + gatts disconnect + gattc disconnect +TC_SEC_CSIGN_BI_02_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 66 + 129 + gatts start_service + gatts disconnect + gattc disconnect +TC_SEC_CSIGN_BI_03_C PASS haltest: + gatts add_service + gatts add_characteristic: + 64 + 128 + gatts start_service + gattc listen + bluetooth ssp_reply + gatts disconnect + bluetooth remove_bond +TC_SEC_CSIGN_BI_04_C PASS haltest: + gatts add_service + gatts add_characteristic: + 64 + 256 + gatts start_service + gattc listen + bluetooth ssp_reply + gatts disconnect +TC_PRIV_CONN_BV_01_C PASS gattc connect + gattc create_bond + gattc search service + gattc get_characteristic + gattc write_characteristic (privacy flag) + gattc write_characteristic (reconnection addr.) + gattc disconnect +TC_PRIV_CONN_BV_02_C PASS gattc connect + gattc search service + gattc get_characteristic + gattc write_characteristic (reconnection addr.) + gattc disconnect +TC_PRIV_CONN_BV_03_C INC Privacy feature - PTS issue #12308 +TC_PRIV_CONN_BV_04_C PASS gattc connect + gattc create_bond + gattc search service + gattc get_characteristic + gattc write_characteristic (privacy flag) + gattc write_characteristic (reconnection addr.) + gattc disconnect + gattc connect + gattc disconnect +TC_PRIV_CONN_BV_05_C N/A +TC_PRIV_CONN_BV_06_C N/A +TC_PRIV_CONN_BV_07_C N/A +TC_PRIV_CONN_BV_08_C N/A +TC_PRIV_CONN_BV_09_C N/A +TC_PRIV_CONN_BV_10_C INC PTS issue #12312 +TC_PRIV_CONN_BV_11_C INC PTS issue #12310 +TC_ADV_BV_01_C N/A +TC_ADV_BV_02_C FAIL PTS issue #12254 +TC_ADV_BV_03_C PASS gattc register_client + gattc listen 1 1 +TC_ADV_BV_04_C N/A +TC_ADV_BV_05_C PASS gattc register_client + gattc listen 1 1 +TC_ADV_BV_06_C N/A +TC_ADV_BV_07_C N/A +TC_ADV_BV_08_C N/A +TC_ADV_BV_09_C N/A +TC_ADV_BV_10_C N/A +TC_ADV_BV_11_C N/A +TC_ADV_BV_12_C N/A +TC_ADV_BV_13_C N/A +TC_ADV_BV_14_C N/A +TC_ADV_BV_15_C N/A +TC_ADV_BV_16_C N/A +TC_GAT_BV_01_C PASS haltest: + gattc register_client + gattc listen +TC_GAT_BV_02_C N/A +TC_GAT_BV_03_C N/A +TC_GAT_BV_04_C N/A +TC_GAT_BV_05_C N/A +TC_GAT_BV_06_C N/A +TC_GAT_BV_07_C N/A +TC_GAT_BV_08_C N/A +TC_DM_NCON_BV_01_C PASS btmgmt connectable off +TC_DM_CON_BV_01_C PASS btmgmt connectable on +TC_DM_NBON_BV_01_C PASS +TC_DM_BON_BV_01_C PASS haltest: + create_bond and remove_bond when requested +TC_DM_GIN_BV_01_C PASS +TC_DM_LIN_BV_01_C PASS +TC_DM_NAD_BV_01_C PASS Start discovery from IUT +TC_DM_NAD_BV_02_C PASS +TC_DM_LEP_BV_01_C PASS gattc register_client + gattc listen 1 1 +TC_DM_LEP_BV_02_C PASS Use basic rate PTS dongle + haltest: + bluetooth set_adapter_property +TC_DM_LEP_BV_04_C PASS l2test -n +TC_DM_LEP_BV_05_C PASS btmgmt find -b + l2test -n 00:1B:DC:06:06:22 +TC_DM_LEP_BV_06_C PASS +TC_DM_LEP_BV_07_C PASS +TC_DM_LEP_BV_08_C PASS +TC_DM_LEP_BV_09_C PASS haltest: + bluetooth enable + bluetooth set_adapter_property + BT_PROPERTY_ADAPTER_SCAN_MODE + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE + gattc register_client + bluetooth start_discovery + gattc connect + l2test -n -P 31 + disconnect +TC_DM_LEP_BV_10_C PASS haltest: + bluetooth enable + bluetooth set_adapter_property + BT_PROPERTY_ADAPTER_SCAN_MODE + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE + gattc register_client + gattc listen + bluetooth start_discovery + l2test -n -P 31 +TC_DM_LEP_BV_11_C PASS haltest: + bluetooth enable + bluetooth set_adapter_property + BT_PROPERTY_ADAPTER_SCAN_MODE + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE + gattc register_client + gattc connect +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-gatt.txt bluez-5.23/android/pts-gatt.txt --- bluez-4.101/android/pts-gatt.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-gatt.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,1231 @@ +PTS test results for GATT + +PTS version: 5.2 +Tested: 22-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_GAC_CL_BV_01_C PASS haltest: + gattc scan + gattc search_service + gattc get_characteristic + gattc write_characteristic: type 3 +TC_GAC_SR_BV_01_C FAIL PTS issue #12357 + haltest: + gatts add_service + gatts add_chaaracteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: + value greater than MTU + repeat with correct offset +TC_GAD_CL_BV_01_C PASS haltest: + gattc register_client + gattc scan + gattc connect + gattc search_service + gattc disconnect + gattc connect + gattc refresh - NOTE: refresh should be called + otherwise services are being read from the cache +TC_GAD_CL_BV_02_C PASS haltest: + gattc register_client + gattc scan + gattc connect + gattc search_service with given uuid + gattc disconnect + gattc connect + gattc refresh +TC_GAD_CL_BV_03_C PASS haltest: + when requested: gattc get_characteristic +TC_GAD_CL_BV_04_C PASS haltest: + when requested: gattc get_characteristic +TC_GAD_CL_BV_05_C PASS haltest: + when requested: gattc get_characteristic + handle: check from btmon logs +TC_GAD_CL_BV_06_C PASS haltest: + when requested: gattc get_descriptor +TC_GAD_CL_BV_07_C N/A +TC_GAD_CL_BV_08_C N/A +TC_GAD_SR_BV_01_C PASS haltest: + gattc register_client + gattc listen + gatts register_server + gatts add_service + gatts add_characteristic + gatts start_service + gatts add_service + gatts add_included_service + gatts start_service +TC_GAD_SR_BV_02_C PASS haltest: + gattc register_client + gattc listen + gatts register_server + gatts add_service + gatts add_characteristic + gatts start_service + gatts add_service + gatts add_included_service + gatts start_service +TC_GAD_SR_BV_03_C PASS haltest: + gattc register_client + gattc listen + gatts register_server + gatts add_service + gatts add_characteristic + gatts start_service + gatts add_service + gatts add_included_service + gatts start_service +TC_GAD_SR_BV_04_C PASS haltest: + gattc register_client + gattc listen + gatts register_server + gatts add_service + gatts add_characteristic + gatts start_service + gatts add_service + gatts add_included_service + gatts start_service +TC_GAD_SR_BV_05_C PASS haltest: + gattc register_client + gattc listen + gatts register_server + gatts add_service + gatts add_characteristic + gatts start_service + gatts add_service + gatts add_included_service + gatts start_service +TC_GAD_SR_BV_06_C PASS haltest: + gattc register_client + gattc listen + gatts register_server + gatts add_service + gatts add_characteristic + gatts start_service + gatts add_service + gatts add_included_service + gatts start_service +TC_GAD_SR_BV_07_C PASS haltest: + when requested: + bluetooth get_remote_services + NOTE: check if found requested service +TC_GAD_SR_BV_08_C PASS haltest: + when requested: + bluetooth get_remote_services + NOTE: check if found requested service +TC_GAR_CL_BV_01_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_01_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_02_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_03_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_04_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_05_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc read_characteristic + gattc disconnect +TC_GAR_CL_BV_03_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + test_command: 224 [u1] 8 + gattc disconnect +TC_GAR_CL_BI_06_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GAR_CL_BI_07_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GAR_CL_BI_09_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GAR_CL_BI_10_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GAR_CL_BI_11_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GAR_CL_BV_04_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_12_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_13_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_14_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_15_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_16_C PASS haltest: + gattc read_characteristic +TC_GAR_CL_BI_17_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc read_characteristic + gattc disconnect +TC_GAR_CL_BV_05_C N/A +TC_GAR_CL_BI_18_C N/A +TC_GAR_CL_BI_19_C N/A +TC_GAR_CL_BI_20_C N/A +TC_GAR_CL_BI_21_C N/A +TC_GAR_CL_BI_22_C N/A +TC_GAR_CL_BV_06_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_23_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_24_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_25_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_26_C PASS haltest: + gattc read_descriptor + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAR_CL_BI_27_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor: srvc_id based on + handle from logs + gattc read_descriptor + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAR_CL_BV_07_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_28_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_29_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_30_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_31_C PASS haltest: + gattc read_descriptor +TC_GAR_CL_BI_32_C PASS haltest: + gattc read_descriptor + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAR_CL_BI_33_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor: srvc_id based on + handle from logs + gattc read_descriptor + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAR_CL_BI_34_C PASS haltest: + gattc connect + gattc test_command 224 0 0x0a + gattc disconnect +TC_GAR_CL_BI_35_C PASS haltest: + gattc read_characteristic +TC_GAR_SR_BV_01_C PASS +TC_GAR_SR_BI_01_C PASS +TC_GAR_SR_BI_02_C PASS +TC_GAR_SR_BI_03_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts start_service + gatts send_response: 8 +TC_GAR_SR_BI_04_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 3 + gatts start_service + gatts send_response +TC_GAR_SR_BI_05_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts start_service + gatts send_response: 12 +TC_GAR_SR_BV_03_C PASS +TC_GAR_SR_BI_06_C PASS PTS issue #12346 tested with ETS + haltest: + gatts add_service + gatts add_chaaracteristic: + 2 16 + gatts start_service +TC_GAR_SR_BI_07_C PASS +TC_GAR_SR_BI_08_C PASS +TC_GAR_SR_BI_09_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts start_service + gatts send_response: 8 +TC_GAR_SR_BI_10_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts start_service + gatts send_response: 5 +TC_GAR_SR_BI_11_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts start_service + gatts send_response: 12 +TC_GAR_SR_BV_04_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset +TC_GAR_SR_BI_12_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 8 16 + gatts start_service + gatts send_response +TC_GAR_SR_BI_13_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 7 +TC_GAR_SR_BI_14_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts start_service + gatts send_response: 1 +TC_GAR_SR_BI_15_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts start_service + gatts send_response: 8 +TC_GAR_SR_BI_16_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts start_service + gatts send_response: 5 +TC_GAR_SR_BI_17_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts start_service + gatts send_response: 12 +TC_GAR_SR_BV_05_C N/A +TC_GAR_SR_BI_18_C N/A +TC_GAR_SR_BI_19_C N/A +TC_GAR_SR_BI_20_C N/A +TC_GAR_SR_BI_21_C N/A +TC_GAR_SR_BI_22_C N/A +TC_GAR_SR_BV_06_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts add_descriptor + gatts start_service + gatts send_response +TC_GAR_SR_BI_23_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 16 + gatts start_service +TC_GAR_SR_BI_24_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts add_descriptor + gatts start_service + gatts send_response: 1 +TC_GAR_SR_BI_25_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: 8 +TC_GAR_SR_BI_26_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: 5 +TC_GAR_SR_BI_27_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: 12 +TC_GAR_SR_BV_07_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset +TC_GAR_SR_BV_08_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset +TC_GAR_SR_BI_28_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 16 + gatts start_service +TC_GAR_SR_BI_29_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 7 +TC_GAR_SR_BI_30_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: 1 +TC_GAR_SR_BI_31_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: 8 +TC_GAR_SR_BI_32_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: 5 +TC_GAR_SR_BI_33_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: 12 +TC_GAR_SR_BI_34_C PASS haltest: + gatts add_service + gatts add_characteristic + gatts start_service + gatts send_response 0x80-0x9F +TC_GAR_SR_BI_35_C PASS haltest: + gatts add_service + gatts add_characteristic + gatts start_service + gatts send_response 0x80-0x9F +TC_GAW_CL_BV_01_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic + gattc disconnect +TC_GAW_CL_BV_02_C PASS haltest: + gattc connect + bluetooth create_bond + gattc disconnect + gattc connect + gattc search_service + gattc get_characteristics + gattc write_characteristics: 4 + gattc disconnect + +TC_GAW_CL_BV_03_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic + gattc disconnect +TC_GAW_CL_BI_02_C PASS haltest: + gattc connect + test_command: 225 [u1] 18 + gattc disconnect +TC_GAW_CL_BI_03_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_04_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_05_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BI_06_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BV_05_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_07_C PASS haltest: + gattc connect + test_command: 225 [u1] 22 + gattc disconnect +TC_GAW_CL_BI_08_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_09_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_11_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_12_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BI_13_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BV_06_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_14_C PASS haltest: + gattc connect + test_command: 225 [u1] 22 + gattc disconnect +TC_GAW_CL_BI_15_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_17_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_18_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BI_19_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BV_08_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 + gattc disconnect +TC_GAW_CL_BI_20_C PASS haltest: + gattc connect + test_command: 225 [u1] 18 + gattc disconnect +TC_GAW_CL_BI_21_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 + gattc disconnect +TC_GAW_CL_BI_22_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 + gattc disconnect +TC_GAW_CL_BI_23_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BI_24_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BV_09_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 + gattc disconnect +TC_GAW_CL_BI_25_C PASS haltest: + gattc connect + test_command: 225 [u1] 22 + gattc disconnect +TC_GAW_CL_BI_26_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_27_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_29_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_30_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BI_31_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect + NOTE: TSPX_delete_ltk must be set to true + PTS issue #12371 +TC_GAW_CL_BI_32_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc execute_write + gattc disconnect +TC_GAW_CL_BI_33_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_34_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characteristic 2 + gattc disconnect +TC_GAW_CL_BI_35_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 + gattc disconnect +TC_GAW_CL_BI_36_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 + gattc disconnect +TC_GAW_SR_BV_01_C PASS haltest: + gatts add_service + gatts add_characteristic: + 4 17 + gatts start_service +TC_GAW_SR_BV_02_C PASS haltest: + gatts add service + gatts add_characteristics: + 66 147 + gatts start_service + gattc listen + gatts send_response: (twice) + NOTE: gatts_request_write_cb shall be called + (verify it) +TC_GAW_SR_BI_01_C PASS haltest: + gatts add_service + gatts add_characteristic: + 68 + 129 + gatts start_service + gatts send_response: repeat with 1 +TC_GAW_SR_BV_03_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 +TC_GAW_SR_BI_02_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 1 +TC_GAW_SR_BI_03_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 1 + gatts start_service +TC_GAW_SR_BI_04_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 8 +TC_GAW_SR_BI_05_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 5 +TC_GAW_SR_BI_06_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 12 +TC_GAW_SR_BV_05_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: + repeat with correct value +TC_GAW_SR_BI_07_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response +TC_GAW_SR_BI_08_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts start_service +TC_GAW_SR_BI_09_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 7 +TC_GAW_SR_BI_11_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 8 +TC_GAW_SR_BI_12_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 5 +TC_GAW_SR_BI_13_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 12 +TC_GAW_SR_BV_06_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + repeat with correct value +TC_GAW_SR_BV_10_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: + repeat with correct value +TC_GAW_SR_BI_14_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 1 +TC_GAW_SR_BI_15_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 3 +TC_GAW_SR_BI_17_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 8 +TC_GAW_SR_BI_18_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 5 +TC_GAW_SR_BI_19_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: 12 +TC_GAW_SR_BV_07_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + repeat with correct value +TC_GAW_CL_BV_08_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response +TC_GAW_SR_BI_20_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: 1 +TC_GAW_SR_BI_21_C PASS haltest: + gatts add_service + gatts add_characteristic: + 2 1 + gatts add_descriptor: 1 + gatts start_service +TC_GAW_SR_BI_22_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: 8 + +TC_GAW_SR_BI_23_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: 5 +TC_GAW_SR_BI_24_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: 12 +TC_GAW_SR_BV_09_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: + repeat with correct value +TC_GAW_SR_BI_25_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: 1 +TC_GAW_SR_BI_26_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 1 + gatts start_service +TC_GAW_SR_BI_27_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 1 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 7 +TC_GAW_SR_BI_29_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: 8 +TC_GAW_SR_BI_30_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: 5 +TC_GAW_SR_BI_31_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: 12 +TC_GAW_SR_BI_32_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response + gatts send_response: 13 +TC_GAW_SR_BI_33_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 13 +TC_GAW_SR_BI_34_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response + gatts send_response: 13 +TC_GAW_SR_BI_35_C PASS haltest: + gatts add_service + gatts add_characteristic: + 10 17 + gatts add_descriptor: 17 + gatts start_service + gatts send_response: + value greater than MTU + repeat with correct offset + gatts send_response: 13 +TC_GAN_CL_BV_01_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 0100 + gattc disconnect +TC_GAN_SR_BV_01_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 26 17 + gatts add_descriptor: 2902 + gatts start_service + gatts send_response + gatts send_response + gatts send_indication: + char value handle + 0 +TC_GAI_CL_BV_01_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc get_descriptor + gattc write_descriptor 2 0200 + gattc disconnect +TC_GAI_SR_BV_01_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 42 17 + gatts add_descriptor: 17 + gatts start_service + gatts add_service + gatts start_service +TC_GAS_CL_BV_01_C PASS haltest: + gattc connect + gattc disconnect +TC_GAS_SR_BV_01_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 42 17 + gatts add_descriptor: 17 + gatts start_service + gatts add_service + gatts start_service +TC_GAT_CL_BV_01_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc read_characcteristic + gattc disconnect +TC_GAT_CL_BV_02_C PASS haltest: + gattc connect + gattc search_service + gattc get_characteristic: srvc_id based on + handle from logs + gattc write_characcteristic 2 + gattc disconnect +TC_GAT_SR_BV_01_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 42 17 + gatts add_descriptor: 17 + gatts start_service + gatts add_service + gatts start_service +TC_GPA_CL_BV_01_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GPA_CL_BV_02_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GPA_CL_BV_03_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GPA_CL_BV_04_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GPA_CL_BV_05_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GPA_CL_BV_06_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GPA_CL_BV_07_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GPA_CL_BV_08_C PASS haltest: + gattc connect + test_command: 224 [u1] 8 + gattc disconnect +TC_GPA_CL_BV_11_C PASS haltest: + gattc connect + Repeat following steps 5 times: + 1.Find Characteristic Aggregate Format + gattc test_command 224 [u1] 8 + 2.Read aggregate descriptor + gattc test_command 224 [u1] 10 + 3.Read 3 handles from aggregate descriptor + value + gattc test_command 224 [u1] 10 + 4.Compare descriptors values + gattc disconnect + +TC_GPA_CL_BV_12_C PASS haltest: + gattc connect + Repeat following steps 5 times: + 1.Find Characteristic Presentation Format + gattc test_command 224 [u1] 8 + 2.Find characteristic in this range + gattc test_command 224 2803 [u1] 8 + 3.Read characteristic declaration + gattc test_command 224 [u1] 10 + 4.Read characteristic value + gattc test_command 224 [u1] 10 + 5.Compare characteristic value and + presentation format + gattc disconnect +TC_GPA_SR_BV_01_C PASS +TC_GPA_SR_BV_02_C PASS haltest: + gatts add_service + gatts start_service +TC_GPA_SR_BV_03_C PASS haltest: + gatts add_service + gatts add_service + add_included_service + gatts start_service + gatts start_service +TC_GPA_SR_BV_04_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 10 17 + gatts start_service +TC_GPA_SR_BV_05_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 138 17 + gatts add_descriptor 2900 + gatts start_service + gatts send_response +TC_GPA_SR_BV_06_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 138 17 + gatts add_descriptor 2901 + gatts start_service + gatts send_response +TC_GPA_SR_BV_07_C PASS +TC_GPA_SR_BV_08_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 138 17 + gatts add_descriptor 2903 + gatts start_service + gatts send_response +TC_GPA_SR_BV_11_C PASS haltest: + gatts add_service + gatts add_chaaracteristic: + 138 17 + gatts add_descriptor 2905 + gatts start_service + gatts send_response: repeat with correct offset + and data +TC_GPA_SR_BV_12_C PASS PTS issue #12262 + haltest: + gatts add_service + gatts add_chaaracteristic: + 10 17 + gatts add_descriptor 2904 + gatts start_service + gatts send_response: repeat with correct data +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-hdp.txt bluez-5.23/android/pts-hdp.txt --- bluez-4.101/android/pts-hdp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-hdp.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,279 @@ +PTS test results for HDP + +PTS version: 5.2 +Tested: 25-August-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_SRC_CON_BV_01_I PASS haltest: + bluetooth enable + + bluetooth set_adapter_property + BT_PROPERTY_ADAPTER_SCAN_MODE + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE + + hl register_application + for instance: + hl register_application health intel heartrate + heartrate-monitor 1 BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE testing + + when prompted: + bluetooth ssp_reply + for instance: + bluetooth ssp_reply + BT_SSP_VARIANT_CONSENT 1 +TC_SRC_CON_BV_02_I PASS +TC_SRC_CON_BV_03_I PASS when prompted: bluetooth ssp_reply +TC_SRC_CON_BV_04_I PASS haltest: + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SRC_CON_BV_05_I PASS when prompted: bluetooth ssp_reply +TC_SRC_CON_BV_06_I PASS haltest: + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SRC_CON_BV_07_I PASS when prompted: bluetooth start_discovery +TC_SRC_CON_BV_08_I PASS when prompted: bluetooth ssp_reply +TC_SRC_CON_BV_09_I PASS haltest: + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SRC_CON_BV_10_I N/A +TC_SRC_CC_BV_01_C PASS haltest: + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SRC_CC_BV_02_C PASS when prompted: bluetooth ssp_reply +TC_SRC_CC_BV_03_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + + hl connect_channel + + + when prompted: bluetooth ssp_reply + +TC_SRC_CC_BV_05_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SRC_CC_BV_07_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 2 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter + + hl connect_channel + + + when prompted: bluetooth ssp_reply + + when prompted: + hl connect_channel + +TC_SRC_CC_BV_09_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 2 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter + + when prompted: bluetooth ssp_reply +TC_SRC_CC_BI_12_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SRC_HCT_BV_01_I PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SRC_HCT_BV_02_I PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SRC_HCT_BV_03_I N/A +TC_SRC_HCT_BV_04_I PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + + when prompted: bluetooth ssp_reply +TC_SRC_HCT_BV_05_C N/A +TC_SRC_HCT_BV_06_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + + when prompted: bluetooth ssp_reply +TC_SRC_HCT_BV_07_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SRC_DE_BV_01_I INC PTS issue #12340 +TC_SRC_DE_BV_02_I PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SOURCE 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SRC_DEP_BV_01_I N/A +TC_SRC_DEP_BV_02_I N/A +TC_SNK_CON_BV_01_I PASS haltest: + hl register_application + for instance: + hl register_application health intel heartrate + heartrate-monitor 1 BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE testing + + when prompted: + bluetooth ssp_reply + for instance: + bluetooth ssp_reply + BT_SSP_VARIANT_CONSENT 1 +TC_SNK_CON_BV_02_I PASS +TC_SNK_CON_BV_03_I PASS when prompted: bluetooth ssp_reply +TC_SNK_CON_BV_04_I PASS haltest: + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SNK_CON_BV_05_I PASS when prompted: bluetooth ssp_reply +TC_SNK_CON_BV_06_I PASS haltest: + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SNK_CON_BV_07_I PASS when prompted: bluetooth start_discovery +TC_SNK_CON_BV_08_I PASS when prompted: bluetooth ssp_reply +TC_SNK_CON_BV_09_I PASS haltest: + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SNK_CON_BV_10_I N/A +TC_SNK_CC_BV_01_C PASS haltest: + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SNK_CC_BV_02_C PASS when prompted: bluetooth ssp_reply +TC_SNK_CC_BV_04_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + + hl connect_channel + + + when prompted: bluetooth ssp_reply + +TC_SNK_CC_BV_06_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 2 + BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE + pulse-oximeter + BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_STREAMING + pulse-oximeter +TC_SNK_CC_BV_08_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 2 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter + + hl connect_channel + + + when prompted: bluetooth ssp_reply + + when prompted: + hl connect_channel + + +TC_SNK_CC_BV_10_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 2 + BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_RELIABLE + pulse-oximeter + BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_STREAMING + pulse-oximeter +TC_SNK_CC_BI_11_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SNK_HCT_BV_01_I PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + + hl connect_channel + + + when prompted: bluetooth ssp_reply +TC_SNK_HCT_BV_02_I PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SNK_HCT_BV_03_I N/A +TC_SNK_HCT_BV_04_I PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter + + when prompted: bluetooth ssp_reply +TC_SNK_HCT_BV_05_C N/A +TC_SNK_HCT_BV_06_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SNK_HCT_BV_07_C PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SNK_DE_BV_01_I N/A +TC_SNK_DE_BV_02_I PASS haltest: + hl register_application bluez-android Bluez + bluez-hdp health-device-profile 1 + BTHL_MDEP_ROLE_SINK 4100 + BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter +TC_SNK_DEP_BV_03_I N/A +TC_SNK_DEP_BV_04_I N/A +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-hfp.txt bluez-5.23/android/pts-hfp.txt --- bluez-4.101/android/pts-hfp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-hfp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,248 @@ +PTS test results for HFP + +PTS version: 5.2 +Tested: 14-Jul-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_AG_OOR_BV_01_I PASS +TC_AG_OOR_BV_02_I PASS +TC_AG_TRS_BV_01_I PASS +TC_AG_PSI_BV_01_I PASS +TC_AG_PSI_BV_02_I N/A +TC_AG_PSI_BV_03_I PASS +TC_AG_PSI_BV_04_I PASS +TC_AG_PSI_BV_05_I PASS +TC_AG_ACS_BV_02_I N/A +TC_AG_ACS_BV_04_I PASS +TC_AG_ACS_BV_06_I N/A +TC_AG_ACS_BV_08_I PASS +TC_AG_ACS_BV_10_I N/A +TC_AG_ACS_BV_11_I PASS +TC_AG_ACS_BI_14_I PASS +TC_AG_ACR_BV_01_I PASS +TC_AG_ACR_BV_02_I PASS +TC_AG_CLI_BV_01_I PASS +TC_AG_ICA_BV_01_I N/A +TC_AG_ICA_BV_02_I N/A +TC_AG_ICA_BV_04_I PASS +TC_AG_ICA_BV_05_I N/A +TC_AG_ICA_BV_06_I PASS +TC_AG_ICR_BV_01_I PASS +TC_AG_ICR_BV_02_I PASS +TC_AG_TCA_BV_01_I PASS +TC_AG_TCA_BV_02_I PASS +TC_AG_TCA_BV_03_I PASS +TC_AG_TCA_BV_04_I PASS +TC_AG_TCA_BV_05_I PASS +TC_AG_ATH_BV_03_I PASS +TC_AG_ATH_BV_04_I PASS +TC_AG_ATH_BV_05_I PASS +TC_AG_ATH_BV_06_I PASS +TC_AG_ATA_BV_01_I PASS +TC_AG_ATA_BV_02_I PASS +TC_AG_OCN_BV_01_I PASS +TC_AG_OCM_BV_01_I PASS +TC_AG_OCM_BV_02_I PASS +TC_AG_OCL_BV_01_I PASS +TC_AG_OCL_BV_02_I PASS +TC_AG_TWC_BV_01_I PASS +TC_AG_TWC_BV_02_I PASS +TC_AG_TWC_BV_03_I PASS +TC_AG_TWC_BV_04_I PASS +TC_AG_TWC_BV_05_I PASS +TC_AG_TWC_BV_06_I N/A +TC_AG_CIT_BV_01_I PASS +TC_AG_ENO_BV_01_I PASS +TC_AG_ENO_BV_02_I N/A +TC_AG_VRA_BV_01_I PASS +TC_AG_VRA_BV_02_I PASS +TC_AG_VRA_BI_01_I PASS +TC_AG_VRD_BV_01_I PASS +TC_AG_VTG_BV_01_I N/A +TC_AG_TDC_BV_01_I PASS +TC_AG_RSV_BV_01_I PASS +TC_AG_RSV_BV_02_I PASS +TC_AG_RSV_BV_03_I PASS +TC_AG_RMV_BV_01_I N/A +TC_AG_RMV_BV_02_I N/A +TC_AG_RMV_BV_03_I N/A +TC_AG_ECS_BV_01_I PASS +TC_AG_ECS_BV_02_I PASS +TC_AG_ECS_BV_03_I PASS +TC_AG_ECC_BV_01_I N/A +TC_AG_ECC_BV_02_I N/A +TC_AG_ECC_BI_03_I PASS +TC_AG_ECC_BI_04_I PASS +TC_AG_RHH_BV_01_I N/A +TC_AG_RHH_BV_02_I N/A +TC_AG_RHH_BV_03_I N/A +TC_AG_RHH_BV_04_I N/A +TC_AG_RHH_BV_05_I N/A +TC_AG_RHH_BV_06_I N/A +TC_AG_RHH_BV_07_I N/A +TC_AG_RHH_BV_08_I N/A +TC_AG_NUM_BV_01_I PASS +TC_AG_SLC_BV_01_C PASS +TC_AG_SLC_BV_02_C PASS +TC_AG_SLC_BV_03_C PASS +TC_AG_SLC_BV_04_C PASS +TC_AG_SLC_BV_05_I PASS +TC_AG_SLC_BV_06_I PASS +TC_AG_SLC_BV_07_I PASS +TC_AG_ACC_BV_08_I INC Possible PTS issue #12039 +TC_AG_ACC_BV_09_I PASS +TC_AG_ACC_BV_10_I INC Possible PTS issue #12039 +TC_AG_ACC_BV_11_I INC Possible PTS issue #12039 +TC_AG_ACC_BI_12_I PASS +TC_AG_ACC_BI_13_I PASS +TC_AG_ACC_BI_14_I PASS +TC_AG_ACC_BV_15_I PASS +TC_AG_WBS_BV_01_I PASS +TC_AG_DIS_BV_01_I PASS +TC_AG_SDP_BV_01_I PASS +TC_AG_IIA_BV_01_I PASS +TC_AG_IIA_BV_02_I PASS +TC_AG_IIA_BV_03_I N/A +TC_AG_IIA_BV_05_I PASS +TC_AG_IID_BV_01_I PASS +TC_AG_IID_BV_02_I N/A +TC_AG_IID_BV_03_I PASS +TC_AG_IID_BV_04_I PASS +TC_AG_IIC_BV_01_I PASS +TC_AG_IIC_BV_02_I PASS +TC_AG_IIC_BV_03_I PASS + +TC_HF_OOR_BV_01_I N/A +TC_HF_OOR_BV_02_I N/A +TC_HF_TRS_BV_01_I N/A +TC_HF_PSI_BV_01_I N/A +TC_HF_PSI_BV_02_I N/A +TC_HF_PSI_BV_03_I N/A +TC_HF_PSI_BV_04_I N/A +TC_HF_ACS_BV_01_I N/A +TC_HF_ACS_BV_03_I N/A +TC_HF_ACS_BV_05_I N/A +TC_HF_ACS_BV_07_I N/A +TC_HF_ACS_BV_09_I N/A +TC_HF_ACS_BV_12_I N/A +TC_HF_ACS_BI_13_I N/A +TC_HF_ACR_BV_01_I N/A +TC_HF_ACR_BV_02_I N/A +TC_HF_CLI_BV_01_I N/A +TC_HF_ICA_BV_01_I N/A +TC_HF_ICA_BV_02_I N/A +TC_HF_ICA_BV_03_I N/A +TC_HF_ICA_BV_04_I N/A +TC_HF_ICA_BV_05_I N/A +TC_HF_ICA_BV_06_I N/A +TC_HF_ICA_BV_07_I N/A +TC_HF_ICR_BV_01_I N/A +TC_HF_ICR_BV_02_I N/A +TC_HF_TCA_BV_01_I N/A +TC_HF_TCA_BV_02_I N/A +TC_HF_TCA_BV_03_I N/A +TC_HF_TCA_BV_04_I N/A +TC_HF_ATH_BV_03_I N/A +TC_HF_ATH_BV_04_I N/A +TC_HF_ATH_BV_05_I N/A +TC_HF_ATH_BV_06_I N/A +TC_HF_ATH_BV_09_I N/A +TC_HF_ATA_BV_01_I N/A +TC_HF_ATA_BV_02_I N/A +TC_HF_ATA_BV_03_I N/A +TC_HF_OCN_BV_01_I N/A +TC_HF_OCM_BV_01_I N/A +TC_HF_OCM_BV_02_I N/A +TC_HF_OCL_BV_01_I N/A +TC_HF_OCL_BV_02_I N/A +TC_HF_TWC_BV_01_I N/A +TC_HF_TWC_BV_02_I N/A +TC_HF_TWC_BV_03_I N/A +TC_HF_TWC_BV_04_I N/A +TC_HF_TWC_BV_05_I N/A +TC_HF_TWC_BV_06_I N/A +TC_HF_CIT_BV_01_I N/A +TC_HF_ENO_BV_01_I N/A +TC_HF_VRA_BV_01_I N/A +TC_HF_VRA_BV_02_I N/A +TC_HF_VRA_BV_03_I N/A +TC_HF_VRD_BV_01_I N/A +TC_HF_VTG_BV_01_I N/A +TC_HF_TDC_BV_01_I N/A +TC_HF_RSV_BV_01_I N/A +TC_HF_RSV_BV_02_I N/A +TC_HF_RSV_BV_03_I N/A +TC_HF_RMV_BV_01_I N/A +TC_HF_RMV_BV_02_I N/A +TC_HF_RMV_BV_03_I N/A +TC_HF_ECS_BV_01_I N/A +TC_HF_ECS_BV_02_I N/A +TC_HF_ECS_BV_03_I N/A +TC_HF_ECC_BV_01_I N/A +TC_HF_ECC_BV_02_I N/A +TC_HF_RHH_BV_01_I N/A +TC_HF_RHH_BV_02_I N/A +TC_HF_RHH_BV_03_I N/A +TC_HF_RHH_BV_04_I N/A +TC_HF_RHH_BV_05_I N/A +TC_HF_RHH_BV_06_I N/A +TC_HF_RHH_BV_07_I N/A +TC_HF_RHH_BV_08_I N/A +TC_HF_NUM_BV_01_I N/A +TC_HF_NUM_BI_01_I N/A +TC_HF_SLC_BV_01_C N/A +TC_HF_SLC_BV_02_C N/A +TC_HF_SLC_BV_03_C N/A +TC_HF_SLC_BV_04_C N/A +TC_HF_SLC_BV_05_I N/A +TC_HF_SLC_BV_06_I N/A +TC_HF_SLC_BV_08_I N/A +TC_HF_ACC_BV_01_I N/A +TC_HF_ACC_BV_02_I N/A +TC_HF_ACC_BV_03_I N/A +TC_HF_ACC_BV_04_I N/A +TC_HF_ACC_BV_05_I N/A +TC_HF_ACC_BV_06_I N/A +TC_HF_ACC_BV_07_I N/A +TC_HF_WBS_BV_02_I N/A +TC_HF_WBS_BV_03_I N/A +TC_HF_DIS_BV_01_I N/A +TC_HF_DIS_BV_02_I N/A +TC_HF_SDP_BV_01_I N/A +TC_HF_SDP_BV_02_C N/A +TC_HF_SDP_BV_03_C N/A +TC_HF_ATAH_BV_01_I N/A +TC_HF_OCA_BV_01_I N/A +TC_HF_IIA_BV_04_I N/A + +TC_AG_COD_BV_02_I PASS +TC_AG_ATAH_BV_03_I PASS +TC_AG_ATA_BV_03_I PASS +TC_AG_ATH_BV_09_I PASS +TC_AG_SDP_BV_02_C PASS +TC_AG_SDP_BV_03_C PASS +TC_AG_ICA_BV_07_I PASS +TC_AG_ICA_BV_08_I PASS +TC_AG_ICA_BV_09_I PASS +TC_AG_VRA_BV_03_I PASS +TC_AG_OCA_BV_01_I PASS +TC_AG_TCA_BV_06_I PASS + +TC_HF_ATAH_BV_03_I N/A +TC_HF_ATA_BV_03_I N/A +TC_HF_ATH_BV_09_I N/A +TC_HF_SDP_BV_02_C N/A +TC_HF_SDP_BV_03_C N/A +TC_HF_DIS_BV_02_I N/A +TC_HF_ICA_BV_07_I N/A +TC_HF_VRA_BV_03_I N/A +TC_HF_OCA_BV_01_I N/A diff -Nru bluez-4.101/android/pts-hid.txt bluez-5.23/android/pts-hid.txt --- bluez-4.101/android/pts-hid.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-hid.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,77 @@ +PTS test results for HID + +PTS version: 5.2 +Tested: 18-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_HOS_HCE_BV_01_I PASS +TC_HOS_HCE_BV_02_I PASS +TC_HOS_HCE_BV_03_I PASS +TC_HOS_HCE_BV_04_I PASS +TC_HOS_HCR_BV_01_I PASS +TC_HOS_HCR_BV_02_I PASS +TC_HOS_HCR_BV_03_I N/A +TC_HOS_HCR_BV_04_I N/A +TC_HOS_HDT_BV_01_I PASS +TC_HOS_HDT_BV_02_I PASS from shell execute: + haltest + bluetooth enable + hidhost connect + hidhost send_data ff00 +TC_HOS_HDT_BV_03_I N/A +TC_HOS_HDT_BV_04_I N/A +TC_HOS_HID_BV_01_C N/A +TC_HOS_HID_BV_02_C N/A +TC_HOS_HID_BV_03_C N/A +TC_HOS_HID_BV_04_C N/A +TC_HOS_HID_BV_05_C N/A +TC_HOS_HID_BV_06_C N/A +TC_HOS_HID_BV_08_C N/A +TC_HOS_HID_BV_09_C N/A +TC_HOS_HID_BV_10_C N/A +TC_HOS_DAT_BV_01_C PASS from shell execute: + haltest + bluetooth enable + hidhost connect + hidhost send_data ff00 +TC_HOS_DAT_BV_02_C N/A +TC_HOS_DAT_BI_01_C N/A +TC_HOS_DAT_BI_02_C N/A +TC_DEV_HCE_BV_01_I N/A +TC_DEV_HCE_BV_02_I N/A +TC_DEV_HCE_BV_03_I N/A +TC_DEV_HCE_BV_04_I N/A +TC_DEV_HCE_BV_05_I N/A +TC_DEV_HCR_BV_01_I N/A +TC_DEV_HCR_BV_02_I N/A +TC_DEV_HCR_BV_03_I N/A +TC_DEV_HCR_BV_04_I N/A +TC_DEV_HDT_BV_01_I N/A +TC_DEV_HDT_BV_02_I N/A +TC_DEV_HDT_BV_03_I N/A +TC_DEV_HDT_BV_04_I N/A +TC_DEV_HID_BV_01_C N/A +TC_DEV_HID_BV_03_C N/A +TC_DEV_HID_BV_04_C N/A +TC_DEV_HID_BV_05_C N/A +TC_DEV_HID_BV_06_C N/A +TC_DEV_HID_BV_08_C N/A +TC_DEV_HID_BV_09_C N/A +TC_DEV_HID_BV_10_C N/A +TC_DEV_HID_BI_01_C N/A +TC_DEV_HID_BI_02_C N/A +TC_DEV_DAT_BV_01_C N/A +TC_DEV_SDD_BV_01_C N/A +TC_DEV_SDD_BV_02_C N/A +TC_DEV_SDD_BV_03_C N/A +TC_DEV_SDD_BV_04_I N/A +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-hogp.txt bluez-5.23/android/pts-hogp.txt --- bluez-4.101/android/pts-hogp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-hogp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,102 @@ +PTS test results for HoG + +PTS version: 5.2 +Tested: 22-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_HGDS_HH_BV_01_I PASS +TC_HGDS_HH_BV_02_I PASS +TC_HGDS_HH_BV_03_I PASS +TC_HGDS_HD_BV_01_I N/A +TC_HGDS_HD_BV_02_I N/A +TC_HGDR_RH_BV_01_I PASS +TC_HGDC_RH_BV_01_I PASS +TC_HGDC_RH_BV_02_I PASS +TC_HGDC_RH_BV_03_I PASS +TC_HGDC_RH_BV_04_I PASS +TC_HGDC_RH_BV_05_I PASS +TC_HGDC_RH_BV_06_I PASS +TC_HGDC_RH_BV_07_I PASS +TC_HGDC_HH_BV_08_I PASS +TC_HGDC_HH_BV_14_I PASS +TC_HGDC_HH_BV_15_I PASS +TC_HGDC_HH_BV_16_I PASS +TC_HGDC_BH_BV_09_I N/A +TC_HGDC_BH_BV_10_I N/A +TC_HGDC_BH_BV_11_I N/A +TC_HGDC_BH_BV_12_I N/A +TC_HGDC_BH_BV_13_I N/A +TC_HGRF_RH_BV_01_I PASS +TC_HGRF_RH_BV_02_I PASS +TC_HGRF_RH_BV_03_I PASS +TC_HGRF_RH_BV_04_I PASS +TC_HGRF_RH_BV_05_I PASS +TC_HGRF_RH_BV_19_I PASS +TC_HGRF_RH_BV_06_I PASS +TC_HGRF_RH_BV_07_I PASS +TC_HGRF_RH_BV_08_I PASS +TC_HGRF_RH_BV_09_I PASS +TC_HGRF_HH_BV_10_I PASS +TC_HGRF_HH_BV_11_I PASS +TC_HGRF_HH_BV_12_I PASS +TC_HGRF_BH_BV_13_I N/A +TC_HGRF_BH_BV_14_I N/A +TC_HGRF_BH_BV_15_I N/A +TC_HGRF_BH_BV_16_I N/A +TC_HGRF_BH_BV_17_I N/A +TC_HGRF_HH_BV_18_I N/A +TC_HGWF_RH_BV_01_I PASS haltest: hidhost connect + hidhost set_report BTHH_INPUT_REPORT + AAB3F8A6CD + hidhost disconnect +TC_HGWF_RH_BV_02_I PASS haltest: hidhost connect + hidhost set_report BTHH_OUTPUT_REPORT + EF907856341200 + hidhost disconnect +TC_HGWF_RH_BV_03_I PASS haltest: hidhost connect + hidhost set_report BTHH_OUTPUT_REPORT + EF907856341200 + hidhost disconnect +TC_HGWF_RH_BV_04_I PASS haltest: hidhost connect + hidhost set_report BTHH_FEATURE_REPORT + EA453F2D87 + hidhost disconnect +TC_HGWF_RH_BV_05_I N/A +TC_HGWF_RH_BV_06_I N/A +TC_HGWF_RH_BV_07_I N/A +TC_HGWF_BH_BV_08_I N/A +TC_HGWF_BH_BV_09_I N/A +TC_HGWF_BH_BV_10_I N/A +TC_HGWF_BH_BV_11_I N/A +TC_HGCF_RH_BV_01_I PASS +TC_HGCF_RH_BV_02_I PASS haltest: hidhost connect + gattc search_service 1 + gattc get_characteristic 1 {1812,2,1} + gattc get_descriptor 1 {1812,2,1} {2a4d,5} + gattc write_descriptor 1 {1812,2,1} {2a4d,5} + {2902,0} 2 0x0000 0 + gattc get_characteristic 1 {1812,5,1} + gattc get_descriptor 1 {1812,5,1} {2a4d,4} + gattc write_descriptor 1 {1812,5,1} + {2a4d,4} {2902,0} 2 0x0000 0 + hidhost disconnect +TC_HGCF_BH_BV_03_I N/A +TC_HGCF_BH_BV_04_I N/A +TC_HGCF_BH_BV_05_I N/A +TC_HGCF_BH_BV_06_I N/A +TC_HGNF_RH_BV_01_I PASS +TC_HGNF_RH_BI_01_I PASS +TC_HGNF_RH_BI_01_I PASS +TC_HGNF_BH_BV_02_I N/A +TC_HGNF_BH_BV_03_I N/A +TC_HGNF_BH_BI_01_I N/A +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-hsp.txt bluez-5.23/android/pts-hsp.txt --- bluez-4.101/android/pts-hsp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-hsp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,41 @@ +PTS test results for HSP + +PTS version: 5.2 +Tested: 15-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_AG_IAC_BV_01_I PASS +TC_AG_IAC_BV_02_I N/A +TC_AG_OAC_BV_01_I PASS +HSP_TC_AG_ACR_BV_01_I PASS +HSP_TC_AG_ACR_BV_02_I PASS +TC_AG_ACT_BV_01_I PASS +TC_AG_ACT_BV_02_I PASS +TC_AG_RAV_BV_01_I PASS +TC_AG_RAV_BV_02_I PASS +TC_AG_RAV_BV_03_I PASS +TC_AG_RAV_BV_04_I N/A +TC_AG_RAV_BV_05_I N/A +TC_AG_RAV_BV_06_I N/A +TC_HS_IAC_BV_01_I N/A +TC_HS_IAC_BV_02_I N/A +TC_HS_OAC_BV_01_I N/A +TC_HS_ACR_BV_01_I N/A +TC_HS_ACR_BV_02_I N/A +TC_HS_ACT_BV_01_I N/A +TC_HS_ACT_BV_02_I N/A +TC_HS_RAV_BV_01_I N/A +TC_HS_RAV_BV_02_I N/A +TC_HS_RAV_BV_03_I N/A +TC_HS_RAV_BV_04_I N/A +TC_HS_RAV_BV_05_I N/A +TC_HS_RAV_BV_06_I N/A diff -Nru bluez-4.101/android/pts-iopt.txt bluez-5.23/android/pts-iopt.txt --- bluez-4.101/android/pts-iopt.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-iopt.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,25 @@ +PTS test results for IOPT + +PTS version: 5.2 +Tested: 11-August-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_COD_BV_01_I PASS IUT must be discoverable +TC_COD_BV_02_I N/A Under PTS 5.1 test shall be disabled as there is + matching test case in HFP test suit (test No. 15) + PICS settings for HFP shall be disabled for IOPT +TC_SDSS_BV_02_I PASS +TC_SDSA_BV_03_I PASS +TC_SDR_BV_04_I PASS for every PTS bt profile: + haltest: bluetooth get_remote_service_record + +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-l2cap.txt bluez-5.23/android/pts-l2cap.txt --- bluez-4.101/android/pts-l2cap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-l2cap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,152 @@ +PTS test results for L2CAP + +PTS version: 5.2 +Tested: 14-August-2014 +Android version: 4.4.4 +Kernel version: 3.18 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_COS_CED_BV_01_C PASS l2test -n -P 4113 +TC_COS_CED_BV_03_C PASS l2test -w -N 1 -P 4113 +TC_COS_CED_BV_04_C PASS l2test -r -P 4113 +TC_COS_CED_BV_05_C PASS l2test -r -P 4113 +TC_COS_CED_BV_07_C PASS l2test -r -P 4113 +TC_COS_CED_BV_08_C PASS l2test -r -P 4113 +TC_COS_CED_BV_09_C PASS l2test -n -P 4113 +TC_COS_CED_BV_10_C N/A +TC_COS_CED_BV_11_C PASS l2test -r -P 4113 +TC_COS_CED_BI_01_C PASS +TC_COS_CFD_BV_01_C PASS l2test -r -P 4113 +TC_COS_CFD_BV_02_C PASS l2test -r -P 4113 +TC_COS_CFD_BV_03_C PASS l2test -r -P 4113 +TC_COS_CFD_BV_08_C PASS l2test -n -P 4113 +TC_COS_CFD_BV_09_C PASS l2test -n -P 4113 +TC_COS_CFD_BV_10_C N/A +TC_COS_CFD_BI_11_C PASS l2test -r -P 4113 +TC_COS_CFD_BV_12_C PASS l2test -r -P 4113 +TC_COS_CFD_BV_13_C N/A +TC_COS_IEX_BV_01_C PASS l2test -n -P 4113 +TC_COS_IEX_BV_02_C PASS +TC_COS_ECH_BV_01_C PASS +TC_COS_ECH_BV_02_C PASS l2ping -c 1 +TC_CLS_CLR_BV_01_C N/A +TC_CLS_UCD_BV_01_C PASS +TC_CLS_UCD_BV_02_C PASS l2test -s -G -N 1 -P 4113 +TC_CLS_UCD_BV_03_C PASS l2test -s -E -G -N 1 -P 4113 +TC_EXF_BV_01_C PASS +TC_EXF_BV_02_C PASS +TC_EXF_BV_03_C PASS +TC_EXF_BV_04_C N/A +TC_EXF_BV_05_C PASS +TC_EXF_BV_06_C N/A +TC_CMC_BV_01_C PASS l2test -r -X ertm -P 4113 +TC_CMC_BV_02_C PASS l2test -r -X ertm -P 4113 +TC_CMC_BV_03_C PASS l2test -r -X ertm -P 4113 +TC_CMC_BV_04_C PASS l2test -r -X streaming -P 4113 +TC_CMC_BV_05_C PASS l2test -r -X streaming -P 4113 +TC_CMC_BV_06_C PASS l2test -r -X streaming -P 4113 +TC_CMC_BV_07_C PASS l2test -r -X ertm -P 4113 +TC_CMC_BV_08_C PASS l2test -r -X streaming -P 4113 +TC_CMC_BV_09_C PASS l2test -r -X basic -P 4113 +TC_CMC_BV_10_C PASS l2test -n -P 4113 +TC_CMC_BV_11_C PASS l2test -n -P 4113 +TC_CMC_BV_12_C PASS l2test -z -X ertm +TC_CMC_BV_13_C PASS l2test -z -X ertm +TC_CMC_BV_14_C PASS l2test -r -X streaming -P 4113 +TC_CMC_BV_15_C PASS l2test -r -X streaming -P 4113 +TC_CMC_BI_01_C PASS l2test -r -X ertm -P 4113 +TC_CMC_BI_02_C PASS l2test -r -X ertm -P 4113 +TC_CMC_BI_03_C PASS l2test -r -X streaming -P 4113 +TC_CMC_BI_04_C PASS l2test -r -X streaming -P 4113 +TC_CMC_BI_05_C PASS l2test -r -P 4113 +TC_CMC_BI_06_C PASS l2test -r -P 4113 +TC_FOC_BV_01_C PASS l2test -r -X ertm -P 4113 -F 0 +TC_FOC_BV_02_C PASS l2test -r -X ertm -P 4113 -F 0 +TC_FOC_BV_03_C PASS l2test -r -X ertm -P 4113 -F 0 +TC_OFS_BV_01_C PASS l2test -x -X ertm -P 4113 -F 0 -N 1 +TC_OFS_BV_02_C PASS l2test -r -X ertm -P 4113 -F 0 +TC_OFS_BV_03_C PASS l2test -x -X streaming -P 4113 -F 0 -N 1 +TC_OFS_BV_04_C PASS l2test -r -X streaming -P 4113 -F 0 +TC_OFS_BV_05_C PASS l2test -x -X ertm -P 4113 -N 1 +TC_OFS_BV_06_C PASS l2test -r -X ertm -P 4113 +TC_OFS_BV_07_C PASS l2test -x -X streaming -P 4113 -F 0 -N 1 +TC_OFS_BV_08_C PASS l2test -r -X streaming -P 4113 +TC_ERM_BV_01_C PASS l2test -x -X ertm -P 4113 -N 3 -Y 3 +TC_ERM_BV_02_C PASS l2test -r -X ertm -P 4113 +TC_ERM_BV_03_C PASS l2test -r -X ertm -P 4113 +TC_ERM_BV_05_C PASS l2test -x -X ertm -P 4113 -N 2 -Y 2 +TC_ERM_BV_06_C PASS l2test -x -X ertm -P 4113 -N 2 -Y 2 +TC_ERM_BV_07_C PASS l2test -r -H 1000 -K 10000 -X ertm -P 4113 +TC_ERM_BV_08_C PASS l2test -x -X ertm -P 4113 -N 1 +TC_ERM_BV_09_C PASS l2test -X ertm -P 4113 +TC_ERM_BV_10_C PASS l2test -x -X ertm -P 4113 -N 1 +TC_ERM_BV_11_C PASS l2test -x -X ertm -P 4113 -N 1 -Q 1 +TC_ERM_BV_12_C PASS l2test -x -X ertm -P 4113 -R -N 1 -Q 1 +TC_ERM_BV_13_C PASS l2test -x -X ertm -P 4113 -N 2 +TC_ERM_BV_14_C PASS l2test -x -X ertm -P 4113 -N 4 +TC_ERM_BV_15_C PASS l2test -X ertm -P 4113 -N 4 +TC_ERM_BV_16_C N/A +TC_ERM_BV_17_C PASS l2test -X ertm -P 4113 +TC_ERM_BV_18_C PASS l2test -x -X ertm -P 4113 -N 1 +TC_ERM_NV_19_C PASS l2test -x -X ertm -P 4113 -N 1 +TC_ERM_BV_20_C PASS l2test -x -X ertm -P 4113 -N 1 +TC_ERM_BV_21_C PASS l2test -x -X ertm -P 4113 -D 2000 -N 2 +TC_ERM_BV_22_C PASS l2test -r -H 1000 -K 5000 -X ertm -P 4113 +TC_ERM_BV_23_C PASS l2test -x -X ertm -P 4113 -N 2 +TC_ERM_BI_01_C N/A +TC_ERM_BI_02_C PASS l2test -X ertm -P 4113 +TC_ERM_BI_03_C PASS l2test -x -X ertm -P 4113 -N 2 +TC_ERM_BI_04_C PASS l2test -x -X ertm -P 4113 -N 2 +TC_ERM_BI_05_C PASS l2test -x -X ertm -P 4113 -N 2 +TC_STM_BV_01_C PASS l2test -x -X streaming -P 4113 -N 3 -Y 3 +TC_STM_BV_02_C PASS l2test -r -X streaming -P 4113 +TC_STM_BV_03_C PASS l2test -x -X streaming -P 4113 -N 2 +TC_STM_BV_11_C N/A +TC_STM_BV_12_C N/A +TC_STM_BV_13_C N/A +TC_FIX_BV_01_C PASS l2test -z -P 4113 +TC_FIX_BV_02_C N/A +TC_EWC_BV_01_C N/A +TC_EWC_BV_02_C N/A +TC_EWC_BV_03_C N/A +TC_LSC_BV_01_C N/A +TC_LSC_BV_02_C N/A +TC_LSC_BV_03_C N/A +TC_LSC_BI_04_C N/A +TC_LSC_BI_05_C N/A +TC_LSC_BV_06_C N/A +TC_LSC_BV_07_C N/A +TC_LSC_BV_08_C N/A +TC_LSC_BV_09_C N/A +TC_LSC_BI_10_C N/A +TC_LSC_BI_11_C N/A +TC_LSC_BV_12_C N/A +TC_CCH_BV_01_C N/A +TC_CCH_BV_02_C N/A +TC_CCH_BV_03_C N/A +TC_CCH_BV_04_C N/A +TC_ECF_BV_01_C N/A +TC_ECF_BV_02_C N/A +TC_ECF_BV_03_C N/A +TC_ECF_BV_04_C N/A +TC_ECF_BV_05_C N/A +TC_ECF_BV_06_C N/A +TC_ECF_BV_07_C N/A +TC_ECF_BV_08_C N/A + NOTE: for LE tests daemon should be stopped + then: + setprop ctl.start hciattach +TC_LE_CPU_BV_01_C PASS l2test -r -V le_public -J 4 +TC_LE_CPU_BV_02_C PASS l2test -n -V le_public -J 4 +TC_LE_CPU_BI_01_C PASS l2test -n -V le_public -J 4 +TC_LE_CPU_BI_02_C INC PTS issue #12339 + l2test -r -V le_public -J 4 +TC_LE_REJ_BV_01_C PASS l2test -n -V le_public -J 4 diff -Nru bluez-4.101/android/pts-map.txt bluez-5.23/android/pts-map.txt --- bluez-4.101/android/pts-map.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-map.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,95 @@ +PTS test results for MAP + +PTS version: 5.2 +Tested: 16-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_MCE_MSM_BV_01_I N/A +TC_MCE_MSM_BV_02_I N/A +TC_MCE_MSM_BV_03_I N/A +TC_MCE_MSM_BV_04_I N/A +TC_MCE_MSM_BV_13_I N/A +TC_MCE_MSM_BV_14_I N/A +TC_MCE_MNR_BV_01_I N/A +TC_MCE_MNR_BV_02_I N/A +TC_MCE_MMB_BV_01_I N/A +TC_MCE_MMB_BV_02_I N/A +TC_MCE_MMB_BV_03_I N/A +TC_MCE_MMB_BV_19_I N/A +TC_MCE_MMB_BV_04_I N/A +TC_MCE_MMB_BV_17_I N/A +TC_MCE_MMB_BV_06_I N/A +TC_MCE_MMB_BV_07_I N/A +TC_MCE_MMB_BV_08_I N/A +TC_MCE_MMD_BV_01_I N/A +TC_MCE_MMU_BV_01_I N/A +TC_MCE_MMN_BV_01_I N/A +TC_MCE_MMN_BV_03_I N/A +TC_MCE_MMI_BV_01_I N/A +TC_MCE_MFB_BV_01_I N/A +TC_MCE_MFB_BV_03_I N/A +TC_MCE_MFB_BV_04_I N/A +TC_MCE_BC_BV_02_I N/A +TC_MCE_BC_BV_04_I N/A +TC_MCE_CON_BV_01_I N/A +TC_MCE_CON_BV_02_I N/A +TC_MCE_ROB_BV_01_I N/A +TC_MCE_SRM_BV_03_I N/A +TC_MCE_SRM_BV_07_I N/A +TC_MCE_SRMP_BI_01_I N/A +TC_MCE_SRMP_BV_01_I N/A +TC_MCE_SRMP_BV_04_I N/A +TC_MCE_SRMP_BV_05_I N/A +TC_MCE_SRMP_BV_06_I N/A +TC_MSE_MSM_BV_05_I PASS +TC_MSE_MSM_BV_06_I PASS +TC_MSE_MSM_BV_07_I PASS +TC_MSE_MSM_BV_08_I PASS +TC_MSE_MSM_BV_09_I N/A +TC_MSE_MSM_BV_10_I N/A +TC_MSE_MSM_BV_11_I N/A +TC_MSE_MSM_BV_12_I N/A +TC_MSE_MNR_BV_03_I PASS +TC_MSE_MNR_BV_04_I PASS +TC_MSE_MMB_BV_09_I PASS +TC_MSE_MMB_BV_10_I PASS +TC_MSE_MMB_BV_11_I PASS +TC_MSE_MMB_BV_20_I PASS +TC_MSE_MMB_BV_12_I N/A +TC_MSE_MMB_BV_18_I N/A +TC_MSE_MMB_BV_13_I PASS +TC_MSE_MMB_BV_14_I PASS +TC_MSE_MMB_BV_15_I PASS +TC_MSE_MMB_BV_16_I PASS Android MAP server bug, fix provided at + android-review.googlesource.com/#/c/82757 +TC_MSE_MMD_BV_02_I PASS +TC_MSE_MMU_BV_02_I PASS At least one SMS must be present in inbox +TC_MSE_MMU_BV_03_I PASS +TC_MSE_MMN_BV_02_I PASS +TC_MSE_MMN_BV_04_I N/A +TC_MSE_MMI_BV_02_I N/A +TC_MSE_MFB_BV_02_I N/A +TC_MSE_MFB_BV_05_I N/A +TC_MSE_BC_BV_01_I N/A +TC_MSE_BC_BV_03_I N/A +TC_MSE_CON_BV_01_I N/A +TC_MSE_CON_BV_02_I N/A +TC_MSE_ROB_BV_01_I N/A +TC_MSE_SRM_BI_02_I N/A +TC_MSE_SRM_BI_03_I N/A +TC_MSE_SRM_BI_05_I N/A +TC_MSE_SRM_BV_04_I N/A +TC_MSE_SRM_BV_08_I N/A +TC_MSE_SRMP_BI_02_I N/A +TC_MSE_SRMP_BV_02_I N/A +TC_MSE_SRMP_BV_03_I N/A +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-mcap.txt bluez-5.23/android/pts-mcap.txt --- bluez-4.101/android/pts-mcap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-mcap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,83 @@ +PTS test results for MCAP + +PTS version: 5.2 +Tested: 07-August-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +Note: Test were done with ssp enabled and in most of the cases requires pairing + confirmation. This can be done easily by using 'btmgmt monitor'. + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_MCAP_CE_BV_01_C PASS mcaptest -C 4099 -D 4101 -dc +TC_MCAP_CE_BV_02_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CE_BV_03_C PASS mcaptest -C 4099 -D 4101 -c +TC_MCAP_CE_BV_04_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_CM_ABT_BV_01_C N/A +TC_MCAP_CM_ABT_BV_02_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CM_ABT_BV_03_C N/A +TC_MCAP_CM_DEL_BV_01_C N/A +TC_MCAP_CM_DEL_BV_02_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CM_DEL_BV_02_C N/A +TC_MCAP_CM_DEL_BV_04_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CM_DIS_BV_01_C PASS mcaptest -C 4099 -D 4101 -e 2 -f 2 +TC_MCAP_CM_DIS_BV_02_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CM_DIS_BV_03_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CM_DIS_BV_04_C PASS mcaptest -C 4099 -D 4101 -e 2 -f 2 +TC_MCAP_CM_DIS_BV_05_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CM_REC_BV_01_C N/A +TC_MCAP_CM_REC_BV_02_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CM_REC_BV_03_C N/A +TC_MCAP_CM_REC_BV_04_C PASS mcaptest -C 4099 -D 4101 + Note: It may be necessary to confirm pairing + twice (second time when IUT is back in + range). +TC_MCAP_CM_REC_BV_05_C N/A +TC_MCAP_CM_REC_BV_06_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_CS_ERR_BI_01_C N/A +TC_MCAP_CS_ERR_BI_02_C N/A +TC_MCAP_CS_ERR_BI_03_C N/A +TC_MCAP_CS_ERR_BI_04_C N/A +TC_MCAP_CS_I_BV_01_I N/A +TC_MCAP_CS_I_BV_02_I N/A +TC_MCAP_CS_I_BV_03_C N/A +TC_MCAP_CS_I_BV_04_C N/A +TC_MCAP_CS_R_BV_01_I N/A +TC_MCAP_CS_R_BV_02_I N/A +TC_MCAP_CS_R_BV_03_C N/A +TC_MCAP_CS_T_BV_04_C N/A +TC_MCAP_ERR_BI_01_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_02_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_03_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_04_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_05_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_06_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_07_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_08_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_09_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_10_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_11_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_12_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_13_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_14_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_15_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_16_C PASS mcaptest -C 4099 -D 4101 -u +TC_MCAP_ERR_BI_17_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_18_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_ERR_BI_19_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_ERR_BI_20_C PASS mcaptest -C 4099 -D 4101 -g +TC_MCAP_INV_BI_01_C PASS mcaptest -C 4099 -D 4101 -dc +TC_MCAP_INV_BI_02_C PASS mcaptest -C 4099 -D 4101 -d +TC_MCAP_INV_BI_03_C PASS mcaptest -C 4099 -D 4101 -dc +TC_MCAP_INV_BI_04_C PASS mcaptest -C 4099 -D 4101 -c +TC_MCAP_INV_BI_05_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_INV_BI_06_C PASS mcaptest -C 4099 -D 4101 +TC_MCAP_INV_BI_07_C PASS mcaptest -C 4099 -D 4101 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-mps.txt bluez-5.23/android/pts-mps.txt --- bluez-4.101/android/pts-mps.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-mps.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,54 @@ +PTS test results for MPS + +PTS version: 5.2 +Tested: 31.07.2014 +Android version: 4.4.4 + +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_AG_SRC_HFAV_ACT_SD_BV_01_I PASS +TC_AG_SRC_HFAV_ACT_SD_BV_02_I PASS +TC_AG_SRC_HFAV_ACT_SD_BV_03_I PASS +TC_AG_SRC_HFAV_CLH_SD_BV_01_I PASS +TC_AG_SRC_HFAV_CLH_SD_BV_02_I N/A +TC_AG_SRC_HFAV_CLH_SD_BV_03_I PASS +TC_AG_SRC_HFAV_CLH_SD_BV_04_I PASS +TC_AG_SRC_HFAV_CLH_SD_BV_05_I PASS +TC_AG_SRC_HFAV_CLH_SD_BV_06_I PASS +TC_AVP_CTH_SD_BI_01_I PASS +TC_AVP_CTH_SD_BI_02_I PASS +TC_HF_SNK_HFAV_ACT_SD_BV_01_I N/A +TC_HF_SNK_HFAV_ACT_SD_BV_02_I N/A +TC_HF_SNK_HFAV_ACT_SD_BV_03_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_01_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_02_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_03_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_04_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_05_I N/A +TC_HF_SNK_HFAV_CLH_SD_BV_06_I N/A +TC_HF_SNK_CT_HFAV_ACT_MD_BV_01_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_01_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_02_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_03_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_04_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_05_I N/A +TC_HF_SNK_CT_HFAV_CLH_MD_BV_06_I N/A +TC_SDP_CTH_SD_BV_01_I PASS +TC_SRC_TG_HFAV_ACT_MD_BV_01_I PASS +TC_SRC_TG_HFAV_CLH_MD_BV_01_I PASS +TC_SRC_TG_HFAV_CLH_MD_BV_02_I PASS +TC_SRC_TG_HFAV_CLH_MD_BV_03_I PASS +TC_SRC_TG_HFAV_CLH_MD_BV_04_I PASS Do not use AOSP Music player. + Use e.g. MortPlayer or Poweramp +TC_SRC_TG_HFAV_CLH_MD_BV_05_I PASS +TC_SRC_TG_HFAV_CLH_MD_BV_06_I PASS +TC_PAIRING_HF_SNK_CT PASS +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-opp.txt bluez-5.23/android/pts-opp.txt --- bluez-4.101/android/pts-opp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-opp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,119 @@ +PTS test results for OPP + +PTS version: 5.2 +Tested: 05-August-2014 +Android version: 4.4.4 + +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 N/A +TC_CLIENT_BC_BV_04_I N/A +TC_CLIENT_BCE_BV_01_I N/A +TC_CLIENT_BCE_BV_03_I N/A +TC_CLIENT_BCE_BV_04_I N/A +TC_CLIENT_BCE_BV_05_I N/A +TC_CLIENT_BCE_BV_06_I N/A +TC_CLIENT_BCE_BV_07_I N/A +TC_CLIENT_BCP_BV_01_I N/A +TC_CLIENT_BCP_BV_02_I N/A +TC_CLIENT_BCP_BV_03_I N/A +TC_CLIENT_BCP_BV_04_I N/A +TC_CLIENT_BCP_BV_05_I N/A +TC_CLIENT_CON_BV_01_C N/A +TC_CLIENT_OPH_BI_01_C PASS +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 N/A +TC_CLIENT_OPH_BV_05_I PASS +TC_CLIENT_OPH_BV_07_I N/A +TC_CLIENT_OPH_BV_08_I N/A +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 N/A +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 N/A +TC_CLIENT_OPH_BV_17_I N/A +TC_CLIENT_OPH_BV_18_I N/A +TC_CLIENT_OPH_BV_19_I PASS +TC_CLIENT_OPH_BV_20_I PASS +TC_CLIENT_OPH_BV_22_I PASS +TC_CLIENT_OPH_BV_23_I PASS +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 N/A +TC_SERVER_BC_BV_03_I N/A +TC_SERVER_BCE_BV_01_I N/A +TC_SERVER_BCE_BV_03_I N/A +TC_SERVER_BCE_BV_04_I N/A +TC_SERVER_BCE_BV_05_I N/A +TC_SERVER_BCE_BV_06_I N/A +TC_SERVER_BCE_BV_07_I N/A +TC_SERVER_BCP_BV_01_I N/A +TC_SERVER_BCP_BV_02_I PASS +TC_SERVER_BCP_BV_03_I N/A +TC_SERVER_BCP_BV_04_I N/A +TC_SERVER_BCP_BV_05_I N/A +TC_SERVER_CON_BV_02_C N/A +TC_SERVER_OPH_BV_01_I PASS +TC_SERVER_OPH_BV_02_I PASS +TC_SERVER_OPH_BV_03_I PASS +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 N/A +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 N/A +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 N/A +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_I N/A +TC_SERVER_ROB_BV_02_I N/A +TC_SERVER_SRM_BI_03_I N/A +TC_SERVER_SRM_BI_05_I N/A +TC_SERVER_SRM_BV_04_I N/A +TC_SERVER_SRM_BV_08_I N/A +TC_SERVER_SRMP_BV_02_I N/A +TC_SERVER_SRMP_BV_03_I 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 N/A +TC_SERVER_OPH_BV_31_I N/A +TC_SERVER_OPH_BV_32_I N/A +TC_SERVER_OPH_BV_33_I N/A +TC_SERVER_OPH_BV_34_I PASS +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-pan.txt bluez-5.23/android/pts-pan.txt --- bluez-4.101/android/pts-pan.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-pan.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,73 @@ +PTS test results for PAN + +PTS version: 5.2 +Tested: 05-August-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +-------------------------------------------------------------------------------- +Test Name Result Notes +-------------------------------------------------------------------------------- +TC_BNEP_GN_BROADCAST_0_BV_03_C N/A +TC_GN_Ipv4_Autonet_BV_01_I N/A +TC_GN_Ipv6_Autonet_BV_02_I N/A +TC_GN_IP_DHCP_BV_03_I N/A +TC_GN_IP_LLMNR_BV_01_I N/A +TC_GN_IP_LLMNR_BV_02_I N/A +TC_GN_IP_DNS_BV_01_I N/A +TC_GN_IP_APP_BV_01_I N/A +TC_GN_IP_APP_BV_02_I N/A +TC_GN_IP_APP_BV_03_I N/A +TC_GN_IP_APP_BV_04_I N/A +TC_GN_IP_APP_BV_05_I N/A +TC_SDP_GN_BV_02_C N/A +TC_MISC_GN_UUID_BV_01_C N/A +TC_MISC_GN_UUID_BV_02_C N/A +TC_BNEP_NAP_BROADCAST_0_BV_01_C N/A +TC_BNEP_NAP_BROADCAST_0_BV_02_C N/A +TC_BNEP_NAP_FORWARD_UNICAST_BV_05_C N/A +TC_BNEP_NAP_FORWARD_UNICAST_BV_06_C N/A +TC_BNEP_NAP_MULTICAST_0_BV_03_C N/A +TC_BNEP_NAP_MULTICAST_0_BV_04_C N/A +TC_BNEP_BRIDGE_RX_BV_02_I INC PTS issue #12464 +TC_BNEP_BRIDGE_TX_BV_01_I PASS +TC_NAP_Ipv4_Autonet_BV_01_I N/A +TC_NAP_Ipv6_Autonet_BV_02_I N/A +TC_NAP_IP_DHCP_BV_03_I N/A +TC_NAP_IP_LLMNR_BV_01_I N/A +TC_NAP_IP_LLMNR_BV_02_I N/A +TC_NAP_IP_DNS_BV_01_I N/A +TC_NAP_IP_APP_BV_01_I N/A +TC_NAP_IP_APP_BV_02_I N/A +TC_NAP_IP_APP_BV_03_I N/A +TC_NAP_IP_APP_BV_04_I N/A +TC_NAP_IP_APP_BV_05_I N/A +TC_SDP_NAP_BV_01_C PASS +TC_MISC_NAP_UUID_BV_01_C PASS +TC_MISC_NAP_UUID_BV_02_C PASS +TC_BNEP_PANU_BROADCAST_0_BV_04_C N/A +TC_PANU_Ipv4_Autonet_BV_01_I PASS After DHCP request fail send + DHCP address request. + From IUT: send address request + dhcpd -r 169.254.x.x bt-pan +TC_PANU_Ipv6_Autonet_BV_02_I N/A +TC_PANU_IP_LLMNR_BV_01_I N/A SE #3558, TSE #4382, TCW #448 +TC_PANU_IP_LLMNR_BV_02_I N/A +TC_PANU_IP_DHCP_BV_03_I N/A +TC_PANU_IP_DNS_BV_01_I N/A +TC_PANU_IP_APP_BV_01_I N/A +TC_PANU_IP_APP_BV_02_I N/A +TC_PANU_IP_APP_BV_03_I N/A +TC_PANU_IP_APP_BV_04_I N/A +TC_PANU_IP_APP_BV_05_I PASS +TC_SDP_PANU_BV_01_C N/A +TC_MISC_PANU_UUID_BV_01_C N/A +TC_MISC_PANU_UUID_BV_02_C N/A +TC_MISC_ROLE_BV_01_C N/A +TC_MISC_ROLE_BV_BV_02_C N/A +-------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-pbap.txt bluez-5.23/android/pts-pbap.txt --- bluez-4.101/android/pts-pbap.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-pbap.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,145 @@ +PTS test results for PBAP + +PTS version: 5.2 +Tested: 04-August-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_PCE_SSM_BV_01_C N/A +TC_PCE_SSM_BV_02_C N/A +TC_PCE_SSM_BV_06_C N/A +TC_PCE_SSM_BV_08_C N/A +TC_PCE_SSM_BI_01_C N/A +TC_PCE_SSM_BV_09_C N/A +TC_PCE_SSM_BV_10_C N/A +TC_PCE_PBD_BV_01_C N/A +TC_PCE_PBD_BV_04_C N/A +TC_PCE_PBD_BV_38_C N/A +TC_PCE_PBD_BV_29_C N/A +TC_PCE_PBD_BV_40_C N/A +TC_PCE_PBD_BV_41_C N/A +TC_PCE_PBD_BV_42_C N/A +TC_PCE_PBD_BV_43_C N/A +TC_PCE_PBD_BV_44_C N/A +TC_PCE_PBD_BV_45_C N/A +TC_PCE_PBD_BV_46_C N/A +TC_PCE_PBD_BV_47_C N/A +TC_PCE_PBD_BV_48_C N/A +TC_PCE_PBB_BV_01_C N/A +TC_PCE_PBB_BV_02_C N/A +TC_PCE_PBB_BV_03_C N/A +TC_PCE_PBB_BV_05_C N/A +TC_PCE_PBB_BV_39_C N/A +TC_PCE_PBB_BV_40_C N/A +TC_PCE_PBB_BV_41_C N/A +TC_PCE_PBB_BV_42_C N/A +TC_PCE_PBB_BV_33_C N/A +TC_PCE_PBB_BV_34_C N/A +TC_PCE_PBB_BV_35_C N/A +TC_PCE_PBB_BV_36_C N/A +TC_PCE_PBB_BV_43_C N/A +TC_PCE_PBB_BV_37_C N/A +TC_PCE_PBB_BV_38_C N/A +TC_PCE_PBF_BV_01_I N/A +TC_PCE_PBF_BV_02_I N/A +TC_PCE_PBF_BV_03_I N/A +TC_PCE_PDF_BV_01_I N/A +TC_PCE_PDF_BV_06_I N/A +TC_PSE_SSM_BV_03_C PASS Tester must accept obex request +TC_PSE_SSM_BV_05_C PASS +TC_PSE_SSM_BV_07_C PASS Tester must accept obex request with + TSPX_auth_password set in PIXITs +TC_PSE_SSM_BI_02_C PASS +TC_PSE_SSM_BI_03_C N/A +TC_PSE_SSM_BV_08_I PASS Tester must compare passkey on IUT and PTS +TC_PSE_SSM_BV_11_C N/A + +TC_PSE_PBD_BV_02_C PASS Tester must compare phone book size with the + value given by PTS +TC_PSE_PBD_BV_03_C PASS Tester must compare phone book size with the + value given by PTS +TC_PSE_PBD_BV_05_C N/A +TC_PSE_PBD_BI_01_C PASS +TC_PSE_PBD_BV_06_C N/A +TC_PSE_PBD_BV_07_C N/A +TC_PSE_PBD_BV_08_C N/A +TC_PSE_PBD_BV_09_C N/A +TC_PSE_PBD_BV_10_C N/A +TC_PSE_PBD_BV_17_C PASS +TC_PSE_PBD_BV_18_C N/A +TC_PSE_PBD_BV_19_C N/A +TC_PSE_PBD_BV_20_C N/A +TC_PSE_PBD_BV_21_C N/A +TC_PSE_PBD_BV_22_C N/A +TC_PSE_PBD_BV_23_C N/A +TC_PSE_PBD_BV_24_C N/A +TC_PSE_PBD_BV_25_C N/A +TC_PSE_PBD_BV_26_C N/A +TC_PSE_PBD_BV_27_C N/A +TC_PSE_PBD_BV_28_C N/A +TC_PSE_PBD_BV_29_C N/A +TC_PSE_PBD_BV_30_C N/A +TC_PSE_PBD_BV_31_C N/A +TC_PSE_PBD_BV_32_C N/A +TC_PSE_PBD_BV_33_C N/A +TC_PSE_PBD_BV_34_C N/A +TC_PSE_PBD_BV_35_C N/A +TC_PSE_PBD_BV_36_C PASS +TC_PSE_PBD_BV_37_C N/A +TC_PSE_PBB_BV_06_C PASS +TC_PSE_PBB_BV_07_C PASS +TC_PSE_PBB_BV_08_C PASS Tester must compare phone book size with the + value given by PTS +TC_PSE_PBB_BV_09_C PASS +TC_PSE_PBB_BV_10_C PASS Tester must verify vcard content received by PTS +TC_PSE_PBB_BV_11_C PASS Tester must verify number of new missed calls + with value given by PTS +TC_PSE_PBB_BI_01_C PASS +TC_PSE_PBB_BI_07_C PASS +TC_PSE_PBB_BV_12_C PASS +TC_PSE_PBB_BV_13_C N/A +TC_PSE_PBB_BV_14_C N/A +TC_PSE_PBB_BV_15_C N/A +TC_PSE_PBB_BV_16_C N/A +TC_PSE_PBB_BV_17_C N/A +TC_PSE_PBB_BV_18_C N/A +TC_PSE_PBB_BV_19_C N/A +TC_PSE_PBB_BV_20_C N/A +TC_PSE_PBB_BV_21_C N/A +TC_PSE_PBB_BV_22_C N/A +TC_PSE_PBB_BV_23_C N/A +TC_PSE_PBB_BV_24_C N/A +TC_PSE_PBB_BV_25_C N/A +TC_PSE_PBB_BV_26_C N/A +TC_PSE_PBB_BV_27_C N/A +TC_PSE_PBB_BV_44_C N/A +TC_PSE_PBB_BV_45_C N/A +TC_PSE_PBB_BV_46_C N/A +TC_PSE_PBB_BV_28_C N/A +TC_PSE_PBB_BV_29_C N/A +TC_PSE_PBB_BV_30_C N/A +TC_PSE_PBB_BV_31_C PASS Requires entries in IUT's /och and /ich folders +TC_PSE_PBB_BV_32_C N/A +TC_PSE_PBF_BV_01_I PASS +TC_PSE_PBF_BV_02_I PASS Tester must verify vcard content received by PTS +TC_PSE_PBF_BV_03_I PASS +TC_PSE_PDF_BV_01_I PASS Tester must compare phone book size with the + value given by PTS +TC_PSE_PDF_BV_06_I PASS +TC_PSE_BC_BV_03_I N/A +TC_PSE_CON_BV_02_I N/A +TC_PSE_ROB_BV_01_I N/A +TC_PSE_SRM_BI_03_I N/A +TC_PSE_SRM_BI_05_I N/A +TC_PSE_SRM_BV_08_I N/A +TC_PSE_SRMP_BI_02_I N/A +TC_PSE_SRMP_BV_02_I N/A +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-rfcomm.txt bluez-5.23/android/pts-rfcomm.txt --- bluez-4.101/android/pts-rfcomm.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-rfcomm.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,45 @@ +PTS test results for RFCOMM + +PTS version: 5.2 +Tested: 17-July-2014 +Android version: 4.4.4 + +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_RFC_BV_01_C PASS rctest -n -P 1 +TC_RFC_BV_02_C PASS rctest -r -P 1 +TC_RFC_BV_03_C PASS rctest -r -P 1 +TC_RFC_BV_04_C FAIL PTS issue #12421 + PTS issue #12414 + NOTE: DISC on DLC is expected; IUT disconnects + rfcomm session (close) +TC_RFC_BV_05_C PASS rctest -n -P 4 + Note: test requires IUT to connect on the given + channel. sdptool browse to check the + channel. +TC_RFC_BV_06_C PASS rctest -r -P 1 +TC_RFC_BV_07_C PASS rctest -r -P 1 +TC_RFC_BV_08_C INC PTS issue #12397 + NOTE: Incorrect frame type sent in response to + the DISC command +TC_RFC_BV_11_C PASS rctest -r -P 1 +TC_RFC_BV_13_C FAIL PTS issue #12397 + NOTE: IUT sent the correct frame type (UIH) + but the parameters did not match the + requirements +TC_RFC_BV_14_C N/A +TC_RFC_BV_15_C PASS rctest -r -P 1 +TC_RFC_BV_17_C PASS rctest -d -P 1 +TC_RFC_BV_19_C PASS +TC_RFC_BV_21_C INC PTS issue #12421 +TC_RFC_BV_22_C INC PTS issue #12421 +TC_RFC_BV_25_C INC PTS issue #12421 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-scpp.txt bluez-5.23/android/pts-scpp.txt --- bluez-4.101/android/pts-scpp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-scpp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,24 @@ +PTS test results for ScPP + +PTS version: 5.2 +Tested: 29-July-2014 +Android version: 4.4.4 + +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_SPDS_SC_BV_01_I PASS PTS issue #12119 +TC_SPDC_SC_BV_01_I PASS PTS issue #12119 +TC_SPDC_SC_BV_02_I PASS PTS issue #12119 +TC_SPDC_SC_BV_03_I PASS PTS issue #12119 +TC_SPWF_SC_BV_03_I PASS PTS issue #12119 +TC_SPCF_SC_BV_03_I PASS PTS issue #12119 +TC_SPNF_SC_BV_03_I PASS PTS issue #12119 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-sm.txt bluez-5.23/android/pts-sm.txt --- bluez-4.101/android/pts-sm.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-sm.txt 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,101 @@ +PTS test results for SM + +PTS version: 5.2 +Tested: 25-July-2014 +Android version: 4.4.4 +kernel version: 3.17 + +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_PROT_BV_01_C PASS btmgmt pair -c 0x03 -t 0x01 +TC_PROT_BV_02_C PASS btmgmt advertising on + btmgmt pair -c 0x03 -t 0x01 +TC_JW_BV_01_C PASS btmgmt pairable off + btmgmt pair -c 0x03 -t 0x01 +TC_JW_BV_02_C PASS +TC_JW_BV_05_C PASS btmgmt pair -c 0x03 -t 0x01 +TC_JW_BI_01_C PASS +TC_JW_BI_02_C PASS +TC_JW_BI_03_C PASS bluetoothd is NOT running + btmgmt power on + btmgmt le on + btmgmt connectable on + btmgmt pairable on + btmgmt discov on + btmgmt advertising on +TC_JW_BI_04_C PASS btmgmt pairable off + btmgmg pair -c 0x03 -t 0x01 +TC_PKE_BV_01_C PASS btmgmt pairable off + btmgmt pair -c 0x04 -t 0x01 + Note: provide passkey to PTS +TC_PKE_BV_02_C PASS btmgmt pairable off + btmgmt io-cap 0x04 + btmgmt advertising on + btmgmt monitor + Note: provide passkey +TC_PKE_BV_04_C PASS btmgmt pair -c 0x04 -t 0x01 +TC_PKE_BV_05_C PASS btmgmt io-cap 0x04 + l2test -r -J4 -AES -V le_public +TC_PKE_BI_01_C PASS btmgmt pair -c 0x04 -t 0x01 + Note: Enter invalid passkey in PTS +TC_PKE_BI_02_C PASS btmgmt pair -c 0x04 -t 0x01 + Note: provide passkey +TC_PKE_BI_03_C PASS btmgmt io-cap 0x04 + btmgmt advertising on + btmgmt monitor + Note: Enter invalid passkey in PTS +TC_OOB_BV_01_C N/A +TC_OOB_BV_02_C N/A +TC_OOB_BV_03_C N/A +TC_OOB_BV_04_C N/A +TC_OOB_BV_05_C PASS +TC_OOB_BV_06_C PASS +TC_OOB_BV_07_C PASS +TC_OOB_BV_08_C PASS +TC_OOB_BV_09_C N/A +TC_OOB_BV_10_C N/A +TC_OOB_BI_01_C N/A +TC_OOB_BI_02_C N/A +TC_EKS_BV_01_C PASS +TC_EKS_BV_02_C PASS +TC_EKS_BI_01_C INC PTS issue #12449 + btmgmt io-cap 0x03 +TC_EKS_BI_02_C PASS +TC_SIGN_BV_01_C INC PTS issue #12305 +TC_SIGN_BV_03_C PASS haltest + gattc listen + +TC_SIGN_BI_01_C PASS haltest + gattc listen +TC_KDU_BV_01_C PASS btmgmt pairable on +TC_KDU_BV_02_C PASS PTS issue #12302 + Note: Can pass it with following instructions: + btmgmt privacy on + btmgmt advetising on + Check our random address (valid for 15 min) + Set PIXIT TSPX_bd_addr_iut to random address + Set PIXIT TSPX_peer_type to 01 +TC_KDU_BV_03_C PASS btmgmt pairable on + btmgmt advertising on +TC_KDU_BV_04_C PASS btmgmt pair -c 0x03 -t 0x01 +TC_KDU_BV_05_C PASS PTS issue #12302 + Note: Can pass it with following instructions: + btmgmt privacy on + Check our random address (valid for 15 min) + Set PIXIT TSPX_bd_addr_iut to random address + Set PIXIT TSPX_peer_type to 01 +TC_KDU_BV_06_C PASS btmgmt pair -c 0x03 -t 0x01 +TC_KDU_BV_07_C PASS btmgmt pairable on +TC_SIP_BV_01_C PASS btmgmt pair -c 0x03 -t 0x01 +TC_SIP_BV_02_C INC PTS issue #12460 + l2test -n -J4 -V le_public +TC_SIE_BV_01_C PASS btmgmt pair -c 0x03 -t 0x01 +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/pts-spp.txt bluez-5.23/android/pts-spp.txt --- bluez-4.101/android/pts-spp.txt 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/pts-spp.txt 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,28 @@ +PTS test results for SPP + +PTS version: 5.2 +Tested: 21-July-2014 +Android version: 4.4.4 + +Results: +PASS test passed +FAIL test failed +INC test is inconclusive +N/A test is disabled due to PICS setup + +------------------------------------------------------------------------------- +Test Name Result Notes +------------------------------------------------------------------------------- +TC_DevA_APP_BV_01_C PASS rctest -n -P + Note: IUT should sdp query PTS in order to + check given channel for COM5 +TC_DevB_APP_BV_02_C PASS haltest: socket listen BTSOCK_RFCOMM + +TC_APP_BV_03_C N/A Missing in PTS + PTS issue #12388 + Note: tests BV_03 : BV_6 currently not supported + ETA: December 2014 (PTS 6.0) +TC_APP_BV_04_C N/A Missing in PTS +TC_APP_BV_05_C N/A Missing in PTS +TC_APP_BV_06_C N/A Missing in PTS +------------------------------------------------------------------------------- diff -Nru bluez-4.101/android/README bluez-5.23/android/README --- bluez-4.101/android/README 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/README 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,399 @@ +BlueZ for Android +***************** + +Since Android 4.2 there exists a well standardized HAL interface that the +Bluetooth stack is expected to provide and which enables the easy replacement +of the stack of choice on Android. Android BlueZ is intended as a drop-in +replacement to Android provided Bluetooth stack. + +More details about BlueZ for Android architecture and components can be found +in android/hal-ipc-api.txt file. + +Supported Android version: 4.4 + + +Building and running on Android +=============================== + +Steps needed to build and run Android Open Source Project 4.4 with +integrated BlueZ. + + +Build requirements +------------------ + +- GLib - Android 4.2 or later don't provide GLib and one must provide it in +'external/bluetooth/glib' folder of Android tree. Sample Android GLib port +is available at https://code.google.com/p/aosp-bluez.glib/ + +- SBC - A2DP code requires SBC library (version 1.2 or higher) present in +'external/bluetooth/sbc' directory. Library is build from Android.mk provided +by BlueZ. SBC code is available at git://git.kernel.org/pub/scm/bluetooth/sbc + +- Bionic support - Currently only 'master' branch available at +https://android.googlesource.com/platform/bionic provides all required +functionality and running BlueZ on release branch requires backporting missing +features (currently epoll_create1 and ppoll calls for Android 4.4). Sample +Bionic for Android 4.4 with all required features backported is available at +https://code.google.com/p/aosp-bluez.platform-bionic/ + + +Runtime requirements +-------------------- + +BlueZ HAL library requires 'bluetoothd' and 'bluetoothd-snoop' services to be +available on Android system. Some permissions settings are also required. + +This can be done by importing init.bluetooth.rc file in init.rc file of targeted +board: +import init.bluetooth.rc + +For convenience examples are provided at: +https://code.google.com/p/aosp-bluez.device-lge-mako/ (Nexus 4) +https://code.google.com/p/aosp-bluez.device-asus-flo/ (Nexus 7 2013) + + +Downloading and building +------------------------ + +Building for Android requires full Android AOSP source tree. Sample Android +4.4 tree with all required components present is available at +http://code.google.com/p/aosp-bluez/ + +This tree provides support for Nexus4 (target aosp_mako-userdebug) and +Nexus 7 2013 (target aosp_flo-userdebug). Tree does not provide binary blobs +needed to run Android on supported devices. Those can be obtained from +https://developers.google.com/android/nexus/drivers. Binary blobs needs to be +unpacked (EULA acceptance required) into 'vendor' directory of Android tree. + +Downloading: +repo init -u https://code.google.com/p/aosp-bluez.platform-manifest -b kitkat +repo sync + +Building: +source build/envsetup.sh +lunch aosp_mako-userdebug or lunch aosp_flo-userdebug +make -j8 + +Flashing: +adb reboot bootloader +fastboot flashall -w + +After full build is done it is possible to rebuild only BlueZ: +'cd external/bluetooth/bluez/android/' +'mm' (or 'mm -B' to force rebuilding of all files) +'adb sync' to update target device. + +Downloading and building for Intel devices +------------------------------------------ + +Sample Android tree with all required components for Intel devices based on +Intel reference image (https://01.org/android-ia) can be reconstructed following +instructions below. + +This tree provides support for Dell XPS12, Minnowboard MAX, Intel NUC, +Acer Iconia W700 and other devices mentioned in: +https://01.org/android-ia/guides/devices + +Downloading: +repo init -u https://github.com/01org/android-bluez-manifest.git -b android-ia \ + -m topic/bluez +repo sync + +Building: +source build/envsetup.sh +lunch haswell_generic-eng +make -j8 + +Installing: +Live and Install image is $OUT/live.img +Flash live.img to USB flash and boot from it. More instructions here: +https://01.org/android-ia/guides/developers/build-and-install + +Linux Kernel requirements +------------------------- + +BlueZ for Android uses Linux Bluetooth subsystem and it must be enabled in +kernel. Minimal required version of management interface is 1.3. This +corresponds to Linux 3.9 but latest available version is recommended. Other +requirements include UHID and network bridge support. + +Following kernel options should be enabled: +CONFIG_BT +CONFIG_BT_RFCOMM +CONFIG_BT_RFCOMM_TTY +CONFIG_BT_BNEP +CONFIG_BT_BNEP_MC_FILTER +CONFIG_BT_BNEP_PROTO_FILTER +CONFIG_BRIDGE +CONFIG_UHID +CONFIG_CRYPTO_CMAC +CONFIG_CRYPTO_USER_API +CONFIG_CRYPTO_USER_API_HASH +CONFIG_CRYPTO_USER_API_SKCIPHER + +Also BT chip driver needs to be enabled e.g: +CONFIG_BT_HCIBTUSB + +If it is not possible to use new enough Linux kernel one can use updated +bluetooth subsystem from Backports project. More information about Backports can +be found at https://backports.wiki.kernel.org. Sample kernels using backports +for running BlueZ on Android are available at +https://code.google.com/p/aosp-bluez. + + +Running with Valgrind +--------------------- + +BlueZ for Android is preconfigured to be easily run under Valgrind memcheck. +Appropriate configuration and required modules are automatically included when +building either userdebug or eng variant of Android platform. + +Valgrind can be enabled in runtime by setting "persist.sys.bluetooth.valgrind" +property to either literal "true" or any numeric value >0. For example: +adb root +adb shell setprop persist.sys.bluetooth.valgrind true + +After changing property value Bluetooth need to be restarted to apply changes +(this can be done using UI, just disable and enable it again). Property is +persistent, i.e. there's no need to enable Valgrind again after reboot. + +It's recommended to have unstripped libglib.so installed which will enable +complete backtraces in Valgrind output. Otherwise, in many cases backtrace +will break at e.g. g_free() function without prior callers. It's possible to +have proper library installed automatically by appropriate entry in Android.mk, +see https://code.google.com/p/aosp-bluez.glib/ for an example. + + +Enabling BlueZ debugs +--------------------- + +BlueZ debug logs can be enabled in runtime by setting +"persist.sys.bluetooth.debug" property to either literal "true" or any +numeric value >0. For example: +adb root +adb shell setprop persist.sys.bluetooth.debug 1 + +After changing property value Bluetooth needs to be restarted to apply changes. + +There is also a possibility to enable mgmt debug logs which also enables debugs +as above. To enable it proceed in the same way as described above but use +system properties called: persist.sys.bluetooth.mgmtdbg + +Note: Debugs are only available on NON USER build variants + + +Customization +------------- + +It is possible to customize BlueZ for Android through Android system properties. +This may include enabling extra profiles or features inside HALs implementation +These properties are read on Bluetooth stack startup only and require stack +restart if changed. All customization properties names start with +"persist.sys.bluetooth." followed by specific HAL name e.g. +"persist.sys.bluetooth.handsfree". This section list available customization +options. + +Property Value Description +------------------------------------------- +mode bredr Enable BlueZ in BR/EDR mode + le Enable BlueZ in LE mode + Enable BlueZ in default mode - enable BR/EDR/LE + if available. +handsfree hfp Enable Handsfree Profile (HFP) with narrowband + speech only + hfp_wbs Enable Handsfree Profile (HFP) with narrowband + and wideband speech support + Don't enable Handsfree Profile (HFP) + + +Building and running on Linux +----------------------------- + +It is possible to build and test BlueZ for Android daemon on Linux (eg. PC). +Simply follow instructions available at README file in BlueZ top directory. +Android daemon binary is located at android/bluetoothd. See next section on +how to test Android daemon on Linux. + + +Testing tool +------------ + +BT HAL test tools located in android/haltest is provided for HAL level testing +of both Android daemon and HAL library. Start it with '-n' parameter and type +'bluetooth init' in prompt to initialize HAL library. Running without parameter +will make haltest try to initialize all services after start. On Android +required bluetoothd service will be started automatically. On Linux it is +required to start android/bluetoothd manually before init command timeout or +use provided android/system-emulator, which takes care of launching daemon +automatically on HAL library initialization. To deinitialize HAL library and +stop daemon type 'bluetooth cleanup'. Type 'help' for more information. Tab +completion is also supported. + + +Implementation status +===================== + +Summary of HALs implementation status. + +complete - implementation is feature complete and Android Framework is able + to use it normally +partial - implementation is in progress and not all required features are + present, Android Framework is able to use some of features +initial - only initial implementations is present, Android Framework is + able to initialize but most likely not able to use it +not started - no implementation, Android Framework is not able to initialize it + +Profile ID HAL header Status +--------------------------------------- +core bluetooth.h complete +a2dp bt_av.h complete +gatt bt_gatt.h complete + bt_gatt_client.h complete + bt_gatt_server.h complete +handsfree bt_hf.h complete +hidhost bt_hh.h complete +health bt_hl.h complete +pan bt_pan.h complete +avrcp bt_rc.h complete +socket bt_sock.h complete + + +Implementation shortcomings +=========================== + +It is possible that some of HAL functionality (although being marked as +complete) is missing implementation due to reasons like feature feasibility or +necessity for latest Android Framework. This sections provides list of such +deficiencies. Note that HAL library is always expected to fully implement HAL +API so missing implementation might happen only in daemon. + + +HAL Bluetooth +------------- + +methods: +dut_mode_send never called from Android Framework +le_test_mode never called from Android Framework + +callbacks: +dut_mode_recv_cb empty JNI implementation +le_test_mode_cb empty JNI implementation + +properties: +BT_PROPERTY_SERVICE_RECORD not supported for adapter, for device this + property is returned as a response to + get_remote_service_record call + +BT_PROPERTY_REMOTE_VERSION_INFO information required by this property (LMP + information) are not accessible from mgmt + interface, also marking this property as + settable is probably a typo in HAL header + +HAL Socket +---------- + +Support only for BTSOCK_RFCOMM socket type. + + +HAL AVRCP +--------- + +methods: +list_player_app_attr_rsp never called from Android Framework +list_player_app_value_rsp never called from Android Framework +get_player_app_value_rsp never called from Android Framework +get_player_app_attr_text_rsp never called from Android Framework +get_player_app_value_text_rsp never called from Android Framework +set_player_app_value_rsp never called from Android Framework + +callbacks: +list_player_app_attr_cb NULL JNI implementation +list_player_app_values_cb NULL JNI implementation +get_player_app_value_cb NULL JNI implementation +get_player_app_attrs_text_cb NULL JNI implementation +get_player_app_values_text_cb NULL JNI implementation +set_player_app_value_cb NULL JNI implementation + + +HAL GATT +-------- + +methods: +client->set_adv_data missing kernel support for vendor data +client->connect is_direct parameter is ignored + +Audio SCO HAL +============= + +When Bluetooth chip's audio is not wired directly to device audio, Audio SCO +HAL is used to enable SCO support. It needs to be loaded by AudioFlinger +following audio_policy.conf configuration. Example of configuration is shown +below: + +... + sco { + outputs { + sco { + sampling_rates 8000|44100 + channel_masks AUDIO_CHANNEL_OUT_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_OUT_ALL_SCO + } + } + inputs { + sco { + sampling_rates 8000|44100 + channel_masks AUDIO_CHANNEL_IN_MONO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET + } + } + } +... + +Known Android issues +==================== + +It is possible that BlueZ is triggering bugs on Android Framework that could +affect qualification or user experience. This section provides list of +recommended Android fixes that are not part of latest AOSP release supported by +BlueZ. + +https://android-review.googlesource.com/82757 +https://android-review.googlesource.com/87670 +https://android-review.googlesource.com/88384 +https://android-review.googlesource.com/99761 +https://android-review.googlesource.com/99850 +https://android-review.googlesource.com/100297 +https://android-review.googlesource.com/102882 + +Unimplemented Bluetooth features +================================ + +Some Bluetooth functionality require support from outside of BT stack +eg. telephony stack. This sections describes profiles optional features not +implemented due to lack of support in other Android subsystems or missing API +in respective BT HALs. + +Profile Feature Comments +-------------------------------------------------------- +HFP Attach a phone number to AT+BINP=1 + a voice tag +HFP Enhanced Call Control AT+CHLD={1x,2x} +HFP Explicit Call Transfer AT+CHLD=4 +HFP Response and Hold AT+BTRH, +BTRH +HFP In-band Ring Tone +BSIR +AVRCP Player Settings HAL API present but not used +AVRCP Browsing No HAL API +GATT Read multiple characteristics No HAL API + + +Reporting Bugs +============== + +Bugs should be reported at https://01.org/jira/browse/BA. When reporting +a bug please attach logs from logcat (logcat -v time) and HCI trace. Daemon +debug logs should be enabled. When reporting daemon crash please run it under +valgrind if possible. For details on how to enabled debug logs and valgrind see +"Enabling BlueZ debugs" section. diff -Nru bluez-4.101/android/sco-msg.h bluez-5.23/android/sco-msg.h --- bluez-4.101/android/sco-msg.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/sco-msg.h 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +static const char BLUEZ_SCO_SK_PATH[] = "\0bluez_sco_socket"; + +#define SCO_SERVICE_ID 0 + +#define SCO_STATUS_SUCCESS IPC_STATUS_SUCCESS +#define SCO_STATUS_FAILED 0x01 + +#define SCO_OP_STATUS IPC_OP_STATUS + +#define SCO_OP_GET_FD 0x01 +struct sco_rsp_get_fd { + uint16_t mtu; +} __attribute__((packed)); diff -Nru bluez-4.101/android/scpp.c bluez-5.23/android/scpp.c --- bluez-4.101/android/scpp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/scpp.c 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,303 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Nordic Semiconductor Inc. + * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "src/log.h" + +#include "lib/uuid.h" +#include "src/shared/util.h" + +#include "attrib/att.h" +#include "attrib/gattrib.h" +#include "attrib/gatt.h" + +#include "android/scpp.h" + +#define SCAN_INTERVAL_WIN_UUID 0x2A4F +#define SCAN_REFRESH_UUID 0x2A31 + +#define SCAN_INTERVAL 0x0060 +#define SCAN_WINDOW 0x0030 +#define SERVER_REQUIRES_REFRESH 0x00 + +struct bt_scpp { + int ref_count; + GAttrib *attrib; + struct gatt_primary *primary; + uint16_t interval; + uint16_t window; + uint16_t iwhandle; + uint16_t refresh_handle; + guint refresh_cb_id; +}; + +static void scpp_free(struct bt_scpp *scan) +{ + bt_scpp_detach(scan); + + g_free(scan->primary); + g_free(scan); +} + +struct bt_scpp *bt_scpp_new(void *primary) +{ + struct bt_scpp *scan; + + scan = g_try_new0(struct bt_scpp, 1); + if (!scan) + return NULL; + + scan->interval = SCAN_INTERVAL; + scan->window = SCAN_WINDOW; + + if (primary) + scan->primary = g_memdup(primary, sizeof(*scan->primary)); + + return bt_scpp_ref(scan); +} + +struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan) +{ + if (!scan) + return NULL; + + __sync_fetch_and_add(&scan->ref_count, 1); + + return scan; +} + +void bt_scpp_unref(struct bt_scpp *scan) +{ + if (!scan) + return; + + if (__sync_sub_and_fetch(&scan->ref_count, 1)) + return; + + scpp_free(scan); +} + +static void write_scan_params(GAttrib *attrib, uint16_t handle, + uint16_t interval, uint16_t window) +{ + uint8_t value[4]; + + put_le16(interval, &value[0]); + put_le16(window, &value[2]); + + gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL); +} + +static void refresh_value_cb(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + struct bt_scpp *scan = user_data; + + DBG("Server requires refresh: %d", pdu[3]); + + if (pdu[3] == SERVER_REQUIRES_REFRESH) + write_scan_params(scan->attrib, scan->iwhandle, scan->interval, + scan->window); +} + +static void ccc_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct bt_scpp *scan = user_data; + + if (status != 0) { + error("Write Scan Refresh CCC failed: %s", + att_ecode2str(status)); + return; + } + + DBG("Scan Refresh: notification enabled"); + + scan->refresh_cb_id = g_attrib_register(scan->attrib, + ATT_OP_HANDLE_NOTIFY, scan->refresh_handle, + refresh_value_cb, scan, NULL); +} + +static void write_ccc(GAttrib *attrib, uint16_t handle, void *user_data) +{ + uint8_t value[2]; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + + gatt_write_char(attrib, handle, value, sizeof(value), ccc_written_cb, + user_data); +} + +static void discover_descriptor_cb(uint8_t status, GSList *descs, + void *user_data) +{ + struct bt_scpp *scan = user_data; + struct gatt_desc *desc; + + + if (status != 0) { + error("Discover descriptors failed: %s", att_ecode2str(status)); + return; + } + + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; + + write_ccc(scan->attrib, desc->handle, scan); +} + +static void refresh_discovered_cb(uint8_t status, GSList *chars, + void *user_data) +{ + struct bt_scpp *scan = user_data; + struct gatt_char *chr; + uint16_t start, end; + bt_uuid_t uuid; + + if (status) { + error("Scan Refresh %s", att_ecode2str(status)); + return; + } + + if (!chars) { + DBG("Scan Refresh not supported"); + return; + } + + chr = chars->data; + + DBG("Scan Refresh handle: 0x%04x", chr->value_handle); + + start = chr->value_handle + 1; + end = scan->primary->range.end; + + if (start > end) + return; + + scan->refresh_handle = chr->value_handle; + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + gatt_discover_desc(scan->attrib, start, end, &uuid, + discover_descriptor_cb, user_data); +} + +static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct bt_scpp *scan = user_data; + struct gatt_char *chr; + + if (status) { + error("Discover Scan Interval Window: %s", + att_ecode2str(status)); + return; + } + + chr = chars->data; + scan->iwhandle = chr->value_handle; + + DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle); + + write_scan_params(scan->attrib, scan->iwhandle, scan->interval, + scan->window); +} + +bool bt_scpp_attach(struct bt_scpp *scan, void *attrib) +{ + bt_uuid_t iwin_uuid, refresh_uuid; + + if (!scan || scan->attrib || !scan->primary) + return false; + + scan->attrib = g_attrib_ref(attrib); + + if (scan->iwhandle) + write_scan_params(scan->attrib, scan->iwhandle, scan->interval, + scan->window); + else { + bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID); + gatt_discover_char(scan->attrib, scan->primary->range.start, + scan->primary->range.end, &iwin_uuid, + iwin_discovered_cb, scan); + } + + if (scan->refresh_handle) + scan->refresh_cb_id = g_attrib_register(scan->attrib, + ATT_OP_HANDLE_NOTIFY, scan->refresh_handle, + refresh_value_cb, scan, NULL); + else { + bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID); + gatt_discover_char(scan->attrib, scan->primary->range.start, + scan->primary->range.end, &refresh_uuid, + refresh_discovered_cb, scan); + } + + return true; +} + +void bt_scpp_detach(struct bt_scpp *scan) +{ + if (!scan || !scan->attrib) + return; + + if (scan->refresh_cb_id > 0) { + g_attrib_unregister(scan->attrib, scan->refresh_cb_id); + scan->refresh_cb_id = 0; + } + + g_attrib_unref(scan->attrib); + scan->attrib = NULL; +} + +bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value) +{ + if (!scan) + return false; + + /* TODO: Check valid range */ + + scan->interval = value; + + return true; +} + +bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value) +{ + if (!scan) + return false; + + /* TODO: Check valid range */ + + scan->window = value; + + return true; +} diff -Nru bluez-4.101/android/scpp.h bluez-5.23/android/scpp.h --- bluez-4.101/android/scpp.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/scpp.h 2014-07-04 18:13:40.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can rescpptribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is scpptributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct bt_scpp; + +struct bt_scpp *bt_scpp_new(void *primary); + +struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan); +void bt_scpp_unref(struct bt_scpp *scan); + +bool bt_scpp_attach(struct bt_scpp *scan, void *gatt); +void bt_scpp_detach(struct bt_scpp *scan); + +bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value); +bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value); diff -Nru bluez-4.101/android/socket.c bluez-5.23/android/socket.c --- bluez-4.101/android/socket.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/socket.c 2014-06-20 18:33:13.000000000 +0000 @@ -0,0 +1,1181 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "btio/btio.h" +#include "lib/sdp.h" +#include "lib/sdp_lib.h" +#include "src/sdp-client.h" +#include "src/sdpd.h" +#include "src/log.h" + +#include "hal-msg.h" +#include "ipc-common.h" +#include "ipc.h" +#include "utils.h" +#include "bluetooth.h" +#include "socket.h" + +#define RFCOMM_CHANNEL_MAX 30 + +#define OPP_DEFAULT_CHANNEL 9 +#define HSP_AG_DEFAULT_CHANNEL 12 +#define HFP_AG_DEFAULT_CHANNEL 13 +#define PBAP_DEFAULT_CHANNEL 15 +#define MAP_MAS_DEFAULT_CHANNEL 16 + +#define SVC_HINT_OBEX 0x10 + +/* Hardcoded MAP stuff needed for MAS SMS Instance.*/ +#define DEFAULT_MAS_INSTANCE 0x00 + +#define MAP_MSG_TYPE_SMS_GSM 0x02 +#define MAP_MSG_TYPE_SMS_CDMA 0x04 +#define DEFAULT_MAS_MSG_TYPE (MAP_MSG_TYPE_SMS_GSM | MAP_MSG_TYPE_SMS_CDMA) + +static struct ipc *hal_ipc = NULL; +struct rfcomm_sock { + int channel; /* RFCOMM channel */ + BtIOSecLevel sec_level; + + /* for socket to BT */ + int bt_sock; + guint bt_watch; + + /* for socket to HAL */ + int jv_sock; + guint jv_watch; + + bdaddr_t dst; + uint32_t service_handle; + + uint8_t *buf; + int buf_size; +}; + +struct rfcomm_channel { + bool reserved; + struct rfcomm_sock *rfsock; +}; + +static bdaddr_t adapter_addr; + +static const uint8_t zero_uuid[16] = { 0 }; + +/* Simple list of RFCOMM connected sockets */ +static GList *connections = NULL; + +static struct rfcomm_channel servers[RFCOMM_CHANNEL_MAX + 1]; + +static int rfsock_set_buffer(struct rfcomm_sock *rfsock) +{ + socklen_t len = sizeof(int); + int rcv, snd, size, err; + + err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_RCVBUF, &rcv, &len); + if (err < 0) { + int err = -errno; + error("getsockopt(SO_RCVBUF): %s", strerror(-err)); + return err; + } + + err = getsockopt(rfsock->bt_sock, SOL_SOCKET, SO_SNDBUF, &snd, &len); + if (err < 0) { + int err = -errno; + error("getsockopt(SO_SNDBUF): %s", strerror(-err)); + return err; + } + + size = MAX(rcv, snd); + + DBG("Set buffer size %d", size); + + rfsock->buf = g_malloc(size); + rfsock->buf_size = size; + + return 0; +} + +static void cleanup_rfsock(gpointer data) +{ + struct rfcomm_sock *rfsock = data; + + DBG("rfsock %p bt_sock %d jv_sock %d", rfsock, rfsock->bt_sock, + rfsock->jv_sock); + + if (rfsock->jv_sock >= 0) + if (close(rfsock->jv_sock) < 0) + error("close() fd %d failed: %s", rfsock->jv_sock, + strerror(errno)); + + if (rfsock->bt_sock >= 0) + if (close(rfsock->bt_sock) < 0) + error("close() fd %d: failed: %s", rfsock->bt_sock, + strerror(errno)); + + if (rfsock->bt_watch > 0) + if (!g_source_remove(rfsock->bt_watch)) + error("bt_watch source was not found"); + + if (rfsock->jv_watch > 0) + if (!g_source_remove(rfsock->jv_watch)) + error("stack_watch source was not found"); + + if (rfsock->service_handle) + bt_adapter_remove_record(rfsock->service_handle); + + if (rfsock->buf) + g_free(rfsock->buf); + + g_free(rfsock); +} + +static struct rfcomm_sock *create_rfsock(int bt_sock, int *hal_sock) +{ + int fds[2] = {-1, -1}; + struct rfcomm_sock *rfsock; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) { + error("socketpair(): %s", strerror(errno)); + *hal_sock = -1; + return NULL; + } + + rfsock = g_new0(struct rfcomm_sock, 1); + rfsock->jv_sock = fds[0]; + *hal_sock = fds[1]; + rfsock->bt_sock = bt_sock; + + DBG("rfsock %p", rfsock); + + if (bt_sock < 0) + return rfsock; + + if (rfsock_set_buffer(rfsock) < 0) { + cleanup_rfsock(rfsock); + return NULL; + } + + return rfsock; +} + +static sdp_record_t *create_rfcomm_record(uint8_t chan, uuid_t *uuid, + const char *svc_name, + bool has_obex) +{ + sdp_list_t *svclass_id; + sdp_list_t *seq, *proto_seq, *pbg_seq; + sdp_list_t *proto[3]; + uuid_t l2cap_uuid, rfcomm_uuid, obex_uuid, pbg_uuid; + sdp_data_t *channel; + sdp_record_t *record; + + record = sdp_record_alloc(); + if (!record) + return NULL; + + record->handle = sdp_next_handle(); + + svclass_id = sdp_list_append(NULL, uuid); + sdp_set_service_classes(record, svclass_id); + + sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); + proto[0] = sdp_list_append(NULL, &l2cap_uuid); + seq = sdp_list_append(NULL, proto[0]); + + sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); + proto[1] = sdp_list_append(NULL, &rfcomm_uuid); + channel = sdp_data_alloc(SDP_UINT8, &chan); + proto[1] = sdp_list_append(proto[1], channel); + seq = sdp_list_append(seq, proto[1]); + + if (has_obex) { + sdp_uuid16_create(&obex_uuid, OBEX_UUID); + proto[2] = sdp_list_append(NULL, &obex_uuid); + seq = sdp_list_append(seq, proto[2]); + } + + proto_seq = sdp_list_append(NULL, seq); + sdp_set_access_protos(record, proto_seq); + + sdp_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP); + pbg_seq = sdp_list_append(NULL, &pbg_uuid); + sdp_set_browse_groups(record, pbg_seq); + + if (svc_name) + sdp_set_info_attr(record, svc_name, NULL, NULL); + + sdp_data_free(channel); + sdp_list_free(proto[0], NULL); + sdp_list_free(proto[1], NULL); + if (has_obex) + sdp_list_free(proto[2], NULL); + sdp_list_free(seq, NULL); + sdp_list_free(proto_seq, NULL); + sdp_list_free(pbg_seq, NULL); + sdp_list_free(svclass_id, NULL); + + return record; +} + +static sdp_record_t *create_opp_record(uint8_t chan, const char *svc_name) +{ + uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xff }; + uint8_t dtd = SDP_UINT8; + uuid_t uuid; + sdp_list_t *seq; + sdp_profile_desc_t profile[1]; + void *dtds[sizeof(formats)], *values[sizeof(formats)]; + sdp_data_t *formats_list; + sdp_record_t *record; + size_t i; + + sdp_uuid16_create(&uuid, OBEX_OBJPUSH_SVCLASS_ID); + + record = create_rfcomm_record(chan, &uuid, svc_name, true); + if (!record) + return NULL; + + sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID); + profile[0].version = 0x0100; + seq = sdp_list_append(NULL, profile); + sdp_set_profile_descs(record, seq); + + for (i = 0; i < sizeof(formats); i++) { + dtds[i] = &dtd; + values[i] = &formats[i]; + } + formats_list = sdp_seq_alloc(dtds, values, sizeof(formats)); + sdp_attr_add(record, SDP_ATTR_SUPPORTED_FORMATS_LIST, formats_list); + + sdp_list_free(seq, NULL); + + return record; +} + +static sdp_record_t *create_pbap_record(uint8_t chan, const char *svc_name) +{ + sdp_list_t *seq; + sdp_profile_desc_t profile[1]; + uint8_t formats = 0x01; + sdp_record_t *record; + uuid_t uuid; + + sdp_uuid16_create(&uuid, PBAP_PSE_SVCLASS_ID); + + record = create_rfcomm_record(chan, &uuid, svc_name, true); + if (!record) + return NULL; + + sdp_uuid16_create(&profile[0].uuid, PBAP_PROFILE_ID); + profile[0].version = 0x0101; + seq = sdp_list_append(NULL, profile); + sdp_set_profile_descs(record, seq); + + sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_REPOSITORIES, SDP_UINT8, + &formats); + + sdp_list_free(seq, NULL); + + return record; +} + +static sdp_record_t *create_mas_record(uint8_t chan, const char *svc_name) +{ + sdp_list_t *seq; + sdp_profile_desc_t profile[1]; + uint8_t minst = DEFAULT_MAS_INSTANCE; + uint8_t mtype = DEFAULT_MAS_MSG_TYPE; + sdp_record_t *record; + uuid_t uuid; + + sdp_uuid16_create(&uuid, MAP_MSE_SVCLASS_ID); + + record = create_rfcomm_record(chan, &uuid, svc_name, true); + if (!record) + return NULL; + + sdp_uuid16_create(&profile[0].uuid, MAP_PROFILE_ID); + profile[0].version = 0x0101; + seq = sdp_list_append(NULL, profile); + sdp_set_profile_descs(record, seq); + + sdp_attr_add_new(record, SDP_ATTR_MAS_INSTANCE_ID, SDP_UINT8, &minst); + sdp_attr_add_new(record, SDP_ATTR_SUPPORTED_MESSAGE_TYPES, SDP_UINT8, + &mtype); + + sdp_list_free(seq, NULL); + + return record; +} + +static sdp_record_t *create_spp_record(uint8_t chan, const char *svc_name) +{ + sdp_record_t *record; + uuid_t uuid; + + sdp_uuid16_create(&uuid, SERIAL_PORT_SVCLASS_ID); + + record = create_rfcomm_record(chan, &uuid, svc_name, false); + if (!record) + return NULL; + + return record; +} + +static sdp_record_t *create_app_record(uint8_t chan, + const uint8_t *app_uuid, + const char *svc_name) +{ + sdp_record_t *record; + uuid_t uuid; + + sdp_uuid128_create(&uuid, app_uuid); + sdp_uuid128_to_uuid(&uuid); + + record = create_rfcomm_record(chan, &uuid, svc_name, false); + if (!record) + return NULL; + + return record; +} + +static const struct profile_info { + uint8_t uuid[16]; + uint8_t channel; + uint8_t svc_hint; + BtIOSecLevel sec_level; + sdp_record_t * (*create_record)(uint8_t chan, const char *svc_name); +} profiles[] = { + { + .uuid = { + 0x00, 0x00, 0x11, 0x08, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = HSP_AG_DEFAULT_CHANNEL, + .svc_hint = 0, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = NULL + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x1F, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = HFP_AG_DEFAULT_CHANNEL, + .svc_hint = 0, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = NULL + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x2F, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = PBAP_DEFAULT_CHANNEL, + .svc_hint = SVC_HINT_OBEX, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = create_pbap_record + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = OPP_DEFAULT_CHANNEL, + .svc_hint = SVC_HINT_OBEX, + .sec_level = BT_IO_SEC_LOW, + .create_record = create_opp_record + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x32, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = MAP_MAS_DEFAULT_CHANNEL, + .svc_hint = SVC_HINT_OBEX, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = create_mas_record + }, { + .uuid = { + 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x10, 0x00, + 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB + }, + .channel = 0, + .svc_hint = 0, + .sec_level = BT_IO_SEC_MEDIUM, + .create_record = create_spp_record + }, +}; + +static uint32_t sdp_service_register(uint8_t channel, const uint8_t *uuid, + const struct profile_info *profile, + const void *svc_name) +{ + sdp_record_t *record = NULL; + uint8_t svc_hint = 0; + + if (profile && profile->create_record) { + record = profile->create_record(channel, svc_name); + svc_hint = profile->svc_hint; + } else if (uuid) { + record = create_app_record(channel, uuid, svc_name); + } + + if (!record) + return 0; + + if (bt_adapter_add_record(record, svc_hint) < 0) { + error("Failed to register on SDP record"); + sdp_record_free(record); + return 0; + } + + return record->handle; +} + +static int bt_sock_send_fd(int sock_fd, const void *buf, int len, int send_fd) +{ + ssize_t ret; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iv; + char cmsgbuf[CMSG_SPACE(sizeof(int))]; + + DBG("len %d sock_fd %d send_fd %d", len, sock_fd, send_fd); + + if (sock_fd == -1 || send_fd == -1) + return -1; + + memset(&msg, 0, sizeof(msg)); + memset(cmsgbuf, 0, sizeof(cmsgbuf)); + + msg.msg_control = cmsgbuf; + msg.msg_controllen = sizeof(cmsgbuf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd)); + + memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(send_fd)); + + iv.iov_base = (unsigned char *) buf; + iv.iov_len = len; + + msg.msg_iov = &iv; + msg.msg_iovlen = 1; + + ret = sendmsg(sock_fd, &msg, MSG_NOSIGNAL); + if (ret < 0) { + error("sendmsg(): sock_fd %d send_fd %d: %s", + sock_fd, send_fd, strerror(errno)); + return ret; + } + + return ret; +} + +static const struct profile_info *get_profile_by_uuid(const uint8_t *uuid) +{ + unsigned int i; + + for (i = 0; i < G_N_ELEMENTS(profiles); i++) { + if (!memcmp(profiles[i].uuid, uuid, 16)) + return &profiles[i]; + } + + return NULL; +} + +static int try_write_all(int fd, unsigned char *buf, int len) +{ + int sent = 0; + + while (len > 0) { + int written; + + written = write(fd, buf, len); + if (written < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + return -1; + } + + if (!written) + return 0; + + len -= written; buf += written; sent += written; + } + + return sent; +} + +static gboolean jv_sock_client_event_cb(GIOChannel *io, GIOCondition cond, + gpointer data) +{ + struct rfcomm_sock *rfsock = data; + int len, sent; + + if (cond & G_IO_HUP) { + DBG("Socket %d hang up", g_io_channel_unix_get_fd(io)); + goto fail; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + error("Socket %d error", g_io_channel_unix_get_fd(io)); + goto fail; + } + + len = read(rfsock->jv_sock, rfsock->buf, rfsock->buf_size); + if (len <= 0) { + error("read(): %s", strerror(errno)); + /* Read again */ + return TRUE; + } + + sent = try_write_all(rfsock->bt_sock, rfsock->buf, len); + if (sent < 0) { + error("write(): %s", strerror(errno)); + goto fail; + } + + return TRUE; +fail: + DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond); + + connections = g_list_remove(connections, rfsock); + cleanup_rfsock(rfsock); + + return FALSE; +} + +static gboolean bt_sock_event_cb(GIOChannel *io, GIOCondition cond, + gpointer data) +{ + struct rfcomm_sock *rfsock = data; + int len, sent; + + if (cond & G_IO_HUP) { + DBG("Socket %d hang up", g_io_channel_unix_get_fd(io)); + goto fail; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + error("Socket %d error", g_io_channel_unix_get_fd(io)); + goto fail; + } + + len = read(rfsock->bt_sock, rfsock->buf, rfsock->buf_size); + if (len <= 0) { + error("read(): %s", strerror(errno)); + /* Read again */ + return TRUE; + } + + sent = try_write_all(rfsock->jv_sock, rfsock->buf, len); + if (sent < 0) { + error("write(): %s", strerror(errno)); + goto fail; + } + + return TRUE; +fail: + DBG("rfsock %p bt_sock %d cond %d", rfsock, rfsock->bt_sock, cond); + + connections = g_list_remove(connections, rfsock); + cleanup_rfsock(rfsock); + + return FALSE; +} + +static bool sock_send_accept(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr, + int fd_accepted) +{ + struct hal_sock_connect_signal cmd; + int len; + + DBG(""); + + cmd.size = sizeof(cmd); + bdaddr2android(bdaddr, cmd.bdaddr); + cmd.channel = rfsock->channel; + cmd.status = 0; + + len = bt_sock_send_fd(rfsock->jv_sock, &cmd, sizeof(cmd), fd_accepted); + if (len != sizeof(cmd)) { + error("Error sending accept signal"); + return false; + } + + return true; +} + +static gboolean jv_sock_server_event_cb(GIOChannel *io, GIOCondition cond, + gpointer data) +{ + struct rfcomm_sock *rfsock = data; + + DBG("rfsock %p jv_sock %d cond %d", rfsock, rfsock->jv_sock, cond); + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_ERR | G_IO_HUP)) { + servers[rfsock->channel].rfsock = NULL; + cleanup_rfsock(rfsock); + } + + return FALSE; +} + +static void accept_cb(GIOChannel *io, GError *err, gpointer user_data) +{ + struct rfcomm_sock *rfsock = user_data; + struct rfcomm_sock *new_rfsock; + GIOChannel *jv_io; + GError *gerr = NULL; + bdaddr_t dst; + char address[18]; + int new_sock; + int hal_sock; + guint id; + GIOCondition cond; + + if (err) { + error("%s", err->message); + return; + } + + bt_io_get(io, &gerr, + BT_IO_OPT_DEST_BDADDR, &dst, + BT_IO_OPT_INVALID); + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + g_io_channel_shutdown(io, TRUE, NULL); + return; + } + + ba2str(&dst, address); + DBG("Incoming connection from %s on channel %d (rfsock %p)", address, + rfsock->channel, rfsock); + + new_sock = g_io_channel_unix_get_fd(io); + new_rfsock = create_rfsock(new_sock, &hal_sock); + if (!new_rfsock) { + g_io_channel_shutdown(io, TRUE, NULL); + return; + } + + DBG("new rfsock %p bt_sock %d jv_sock %d hal_sock %d", new_rfsock, + new_rfsock->bt_sock, new_rfsock->jv_sock, hal_sock); + + if (!sock_send_accept(rfsock, &dst, hal_sock)) { + cleanup_rfsock(new_rfsock); + return; + } + + connections = g_list_append(connections, new_rfsock); + + /* Handle events from Android */ + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + jv_io = g_io_channel_unix_new(new_rfsock->jv_sock); + id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, new_rfsock); + g_io_channel_unref(jv_io); + + new_rfsock->jv_watch = id; + + /* Handle rfcomm events */ + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + id = g_io_add_watch(io, cond, bt_sock_event_cb, new_rfsock); + g_io_channel_set_close_on_unref(io, FALSE); + + new_rfsock->bt_watch = id; +} + +static int find_free_channel(void) +{ + int ch; + + /* channel 0 is reserver so we don't use it */ + for (ch = 1; ch <= RFCOMM_CHANNEL_MAX; ch++) { + struct rfcomm_channel *srv = &servers[ch]; + + if (!srv->reserved && srv->rfsock == NULL) + return ch; + } + + return 0; +} + +static BtIOSecLevel get_sec_level(uint8_t flags) +{ + /* + * HAL_SOCK_FLAG_AUTH should require MITM but in our case setting + * security to BT_IO_SEC_HIGH would also require 16-digits PIN code + * for pre-2.1 devices which is not what Android expects. For this + * reason we ignore this flag to not break apps which use "secure" + * sockets (have both auth and encrypt flags set, there is no public + * API in Android which should provide proper high security socket). + */ + return flags & HAL_SOCK_FLAG_ENCRYPT ? BT_IO_SEC_MEDIUM : + BT_IO_SEC_LOW; +} + +static uint8_t rfcomm_listen(int chan, const uint8_t *name, const uint8_t *uuid, + uint8_t flags, int *hal_sock) +{ + const struct profile_info *profile; + struct rfcomm_sock *rfsock = NULL; + BtIOSecLevel sec_level; + GIOChannel *io, *jv_io; + GIOCondition cond; + GError *err = NULL; + guint id; + uuid_t uu; + char uuid_str[32]; + + sdp_uuid128_create(&uu, uuid); + sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str)); + + DBG("chan %d flags 0x%02x uuid %s name %s", chan, flags, uuid_str, + name); + + if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) || + (chan > RFCOMM_CHANNEL_MAX)) { + error("Invalid rfcomm listen params"); + return HAL_STATUS_INVALID; + } + + profile = get_profile_by_uuid(uuid); + if (!profile) { + sec_level = get_sec_level(flags); + } else { + if (!profile->create_record) + return HAL_STATUS_INVALID; + + chan = profile->channel; + sec_level = profile->sec_level; + } + + if (chan <= 0) + chan = find_free_channel(); + + if (!chan) { + error("No free channels"); + return HAL_STATUS_BUSY; + } + + if (servers[chan].rfsock != NULL) { + error("Channel already registered (%d)", chan); + return HAL_STATUS_BUSY; + } + + DBG("chan %d sec_level %d", chan, sec_level); + + rfsock = create_rfsock(-1, hal_sock); + if (!rfsock) + return HAL_STATUS_FAILED; + + rfsock->channel = chan; + + io = bt_io_listen(accept_cb, NULL, rfsock, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_CHANNEL, chan, + BT_IO_OPT_SEC_LEVEL, sec_level, + BT_IO_OPT_INVALID); + if (!io) { + error("Failed listen: %s", err->message); + g_error_free(err); + goto failed; + } + + rfsock->bt_sock = g_io_channel_unix_get_fd(io); + + g_io_channel_set_close_on_unref(io, FALSE); + g_io_channel_unref(io); + + /* Handle events from Android */ + cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; + jv_io = g_io_channel_unix_new(rfsock->jv_sock); + id = g_io_add_watch_full(jv_io, G_PRIORITY_HIGH, cond, + jv_sock_server_event_cb, rfsock, + NULL); + g_io_channel_unref(jv_io); + + rfsock->jv_watch = id; + + DBG("rfsock %p bt_sock %d jv_sock %d hal_sock %d", rfsock, + rfsock->bt_sock, + rfsock->jv_sock, + *hal_sock); + + if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) { + error("Error sending RFCOMM channel"); + goto failed; + } + + rfsock->service_handle = sdp_service_register(chan, uuid, profile, + name); + + servers[chan].rfsock = rfsock; + + return HAL_STATUS_SUCCESS; + +failed: + + cleanup_rfsock(rfsock); + close(*hal_sock); + return HAL_STATUS_FAILED; +} + +static void handle_listen(const void *buf, uint16_t len) +{ + const struct hal_cmd_socket_listen *cmd = buf; + uint8_t status; + int hal_sock; + + switch (cmd->type) { + case HAL_SOCK_RFCOMM: + status = rfcomm_listen(cmd->channel, cmd->name, cmd->uuid, + cmd->flags, &hal_sock); + break; + case HAL_SOCK_SCO: + case HAL_SOCK_L2CAP: + status = HAL_STATUS_UNSUPPORTED; + break; + default: + status = HAL_STATUS_INVALID; + break; + } + + if (status != HAL_STATUS_SUCCESS) + goto failed; + + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, + 0, NULL, hal_sock); + close(hal_sock); + return; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_LISTEN, + status); +} + +static bool sock_send_connect(struct rfcomm_sock *rfsock, bdaddr_t *bdaddr) +{ + struct hal_sock_connect_signal cmd; + int len; + + DBG(""); + + memset(&cmd, 0, sizeof(cmd)); + cmd.size = sizeof(cmd); + bdaddr2android(bdaddr, cmd.bdaddr); + cmd.channel = rfsock->channel; + cmd.status = 0; + + len = write(rfsock->jv_sock, &cmd, sizeof(cmd)); + if (len < 0) { + error("%s", strerror(errno)); + return false; + } + + if (len != sizeof(cmd)) { + error("Error sending connect signal"); + return false; + } + + return true; +} + +static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) +{ + struct rfcomm_sock *rfsock = user_data; + bdaddr_t *dst = &rfsock->dst; + GIOChannel *jv_io; + char address[18]; + guint id; + GIOCondition cond; + + if (err) { + error("%s", err->message); + goto fail; + } + + ba2str(dst, address); + DBG("Connected to %s on channel %d (rfsock %p)", address, + rfsock->channel, rfsock); + + if (!sock_send_connect(rfsock, dst)) + goto fail; + + /* Handle events from Android */ + cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + jv_io = g_io_channel_unix_new(rfsock->jv_sock); + id = g_io_add_watch(jv_io, cond, jv_sock_client_event_cb, rfsock); + g_io_channel_unref(jv_io); + + rfsock->jv_watch = id; + + /* Handle rfcomm events */ + cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + id = g_io_add_watch(io, cond, bt_sock_event_cb, rfsock); + g_io_channel_set_close_on_unref(io, FALSE); + + rfsock->bt_watch = id; + + return; +fail: + connections = g_list_remove(connections, rfsock); + cleanup_rfsock(rfsock); +} + +static bool do_rfcomm_connect(struct rfcomm_sock *rfsock, int chan) +{ + GIOChannel *io; + GError *gerr = NULL; + + DBG("rfsock %p sec_level %d chan %d", rfsock, rfsock->sec_level, chan); + + io = bt_io_connect(connect_cb, rfsock, NULL, &gerr, + BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, + BT_IO_OPT_DEST_BDADDR, &rfsock->dst, + BT_IO_OPT_CHANNEL, chan, + BT_IO_OPT_SEC_LEVEL, rfsock->sec_level, + BT_IO_OPT_INVALID); + if (!io) { + error("Failed connect: %s", gerr->message); + g_error_free(gerr); + return false; + } + + g_io_channel_set_close_on_unref(io, FALSE); + g_io_channel_unref(io); + + if (write(rfsock->jv_sock, &chan, sizeof(chan)) != sizeof(chan)) { + error("Error sending RFCOMM channel"); + return false; + } + + rfsock->bt_sock = g_io_channel_unix_get_fd(io); + rfsock_set_buffer(rfsock); + rfsock->channel = chan; + connections = g_list_append(connections, rfsock); + + return true; +} + +static void sdp_search_cb(sdp_list_t *recs, int err, gpointer data) +{ + struct rfcomm_sock *rfsock = data; + sdp_list_t *list; + int chan; + + DBG(""); + + if (err < 0) { + error("Unable to get SDP record: %s", strerror(-err)); + goto fail; + } + + if (!recs || !recs->data) { + error("No SDP records found"); + goto fail; + } + + for (list = recs; list != NULL; list = list->next) { + sdp_record_t *rec = list->data; + sdp_list_t *protos; + + if (sdp_get_access_protos(rec, &protos) < 0) { + error("Unable to get proto list"); + goto fail; + } + + chan = sdp_get_proto_port(protos, RFCOMM_UUID); + + sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, + NULL); + sdp_list_free(protos, NULL); + + if (chan) + break; + } + + if (chan <= 0) { + error("Could not get RFCOMM channel %d", chan); + goto fail; + } + + DBG("Got RFCOMM channel %d", chan); + + if (do_rfcomm_connect(rfsock, chan)) + return; +fail: + cleanup_rfsock(rfsock); +} + +static uint8_t connect_rfcomm(const bdaddr_t *addr, int chan, + const uint8_t *uuid, uint8_t flags, + int *hal_sock) +{ + struct rfcomm_sock *rfsock; + char address[18]; + uuid_t uu; + char uuid_str[32]; + + sdp_uuid128_create(&uu, uuid); + sdp_uuid2strn(&uu, uuid_str, sizeof(uuid_str)); + ba2str(addr, address); + + DBG("addr %s chan %d flags 0x%02x uuid %s", address, chan, flags, + uuid_str); + + if ((!memcmp(uuid, zero_uuid, sizeof(zero_uuid)) && chan <= 0) || + !bacmp(addr, BDADDR_ANY)) { + error("Invalid rfcomm connect params"); + return HAL_STATUS_INVALID; + } + + rfsock = create_rfsock(-1, hal_sock); + if (!rfsock) + return HAL_STATUS_FAILED; + + DBG("rfsock %p jv_sock %d hal_sock %d", rfsock, rfsock->jv_sock, + *hal_sock); + + rfsock->sec_level = get_sec_level(flags); + + bacpy(&rfsock->dst, addr); + + if (!memcmp(uuid, zero_uuid, sizeof(zero_uuid))) { + if (!do_rfcomm_connect(rfsock, chan)) + goto failed; + } else { + + if (bt_search_service(&adapter_addr, &rfsock->dst, &uu, + sdp_search_cb, rfsock, NULL, 0) < 0) { + error("Failed to search SDP records"); + goto failed; + } + } + + return HAL_STATUS_SUCCESS; + +failed: + cleanup_rfsock(rfsock); + close(*hal_sock); + return HAL_STATUS_FAILED; +} + +static void handle_connect(const void *buf, uint16_t len) +{ + const struct hal_cmd_socket_connect *cmd = buf; + bdaddr_t bdaddr; + uint8_t status; + int hal_sock; + + DBG(""); + + android2bdaddr(cmd->bdaddr, &bdaddr); + + switch (cmd->type) { + case HAL_SOCK_RFCOMM: + status = connect_rfcomm(&bdaddr, cmd->channel, cmd->uuid, + cmd->flags, &hal_sock); + break; + case HAL_SOCK_SCO: + case HAL_SOCK_L2CAP: + status = HAL_STATUS_UNSUPPORTED; + break; + default: + status = HAL_STATUS_INVALID; + break; + } + + if (status != HAL_STATUS_SUCCESS) + goto failed; + + ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, + 0, NULL, hal_sock); + close(hal_sock); + return; + +failed: + ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_SOCKET, HAL_OP_SOCKET_CONNECT, + status); + +} + +static const struct ipc_handler cmd_handlers[] = { + /* HAL_OP_SOCKET_LISTEN */ + { handle_listen, false, sizeof(struct hal_cmd_socket_listen) }, + /* HAL_OP_SOCKET_CONNECT */ + { handle_connect, false, sizeof(struct hal_cmd_socket_connect) }, +}; + +void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode) +{ + size_t i; + + DBG(""); + + /* + * make sure channels assigned for profiles are reserved and not used + * for app services + */ + for (i = 0; i < G_N_ELEMENTS(profiles); i++) + if (profiles[i].channel) + servers[profiles[i].channel].reserved = true; + + bacpy(&adapter_addr, addr); + + hal_ipc = ipc; + ipc_register(hal_ipc, HAL_SERVICE_ID_SOCKET, cmd_handlers, + G_N_ELEMENTS(cmd_handlers)); +} + +void bt_socket_unregister(void) +{ + int ch; + + DBG(""); + + g_list_free_full(connections, cleanup_rfsock); + + for (ch = 0; ch <= RFCOMM_CHANNEL_MAX; ch++) + if (servers[ch].rfsock) + cleanup_rfsock(servers[ch].rfsock); + + memset(servers, 0, sizeof(servers)); + + ipc_unregister(hal_ipc, HAL_SERVICE_ID_SOCKET); + hal_ipc = NULL; +} diff -Nru bluez-4.101/android/socket.h bluez-5.23/android/socket.h --- bluez-4.101/android/socket.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/socket.h 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct hal_sock_connect_signal { + short size; + uint8_t bdaddr[6]; + int channel; + int status; +} __attribute__((packed)); + +void bt_socket_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode); +void bt_socket_unregister(void); diff -Nru bluez-4.101/android/system/audio.h bluez-5.23/android/system/audio.h --- bluez-4.101/android/system/audio.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/system/audio.h 2013-12-27 17:16:56.000000000 +0000 @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef ANDROID_AUDIO_CORE_H +#define ANDROID_AUDIO_CORE_H + +#include +#include +#include +#include + +#define popcount __builtin_popcount + +__BEGIN_DECLS + +/* The enums were moved here mostly from + * frameworks/base/include/media/AudioSystem.h + */ + +/* device address used to refer to the standard remote submix */ +#define AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS "0" + +typedef int audio_io_handle_t; + +/* Audio stream types */ +typedef enum { + AUDIO_STREAM_DEFAULT = -1, + AUDIO_STREAM_VOICE_CALL = 0, + AUDIO_STREAM_SYSTEM = 1, + AUDIO_STREAM_RING = 2, + AUDIO_STREAM_MUSIC = 3, + AUDIO_STREAM_ALARM = 4, + AUDIO_STREAM_NOTIFICATION = 5, + AUDIO_STREAM_BLUETOOTH_SCO = 6, + AUDIO_STREAM_ENFORCED_AUDIBLE = 7, /* Sounds that cannot be muted by user and must be routed to speaker */ + AUDIO_STREAM_DTMF = 8, + AUDIO_STREAM_TTS = 9, + + AUDIO_STREAM_CNT, + AUDIO_STREAM_MAX = AUDIO_STREAM_CNT - 1, +} audio_stream_type_t; + +/* Do not change these values without updating their counterparts + * in media/java/android/media/MediaRecorder.java! + */ +typedef enum { + AUDIO_SOURCE_DEFAULT = 0, + AUDIO_SOURCE_MIC = 1, + AUDIO_SOURCE_VOICE_UPLINK = 2, + AUDIO_SOURCE_VOICE_DOWNLINK = 3, + AUDIO_SOURCE_VOICE_CALL = 4, + AUDIO_SOURCE_CAMCORDER = 5, + AUDIO_SOURCE_VOICE_RECOGNITION = 6, + AUDIO_SOURCE_VOICE_COMMUNICATION = 7, + AUDIO_SOURCE_REMOTE_SUBMIX = 8, /* Source for the mix to be presented remotely. */ + /* An example of remote presentation is Wifi Display */ + /* where a dongle attached to a TV can be used to */ + /* play the mix captured by this audio source. */ + AUDIO_SOURCE_CNT, + AUDIO_SOURCE_MAX = AUDIO_SOURCE_CNT - 1, + AUDIO_SOURCE_HOTWORD = 1999, /* A low-priority, preemptible audio source for + for background software hotword detection. + Same tuning as AUDIO_SOURCE_VOICE_RECOGNITION. + Used only internally to the framework. Not exposed + at the audio HAL. */ +} audio_source_t; + +/* special audio session values + * (XXX: should this be living in the audio effects land?) + */ +typedef enum { + /* session for effects attached to a particular output stream + * (value must be less than 0) + */ + AUDIO_SESSION_OUTPUT_STAGE = -1, + + /* session for effects applied to output mix. These effects can + * be moved by audio policy manager to another output stream + * (value must be 0) + */ + AUDIO_SESSION_OUTPUT_MIX = 0, +} audio_session_t; + +/* Audio sub formats (see enum audio_format). */ + +/* PCM sub formats */ +typedef enum { + AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, /* DO NOT CHANGE - PCM signed 16 bits */ + AUDIO_FORMAT_PCM_SUB_8_BIT = 0x2, /* DO NOT CHANGE - PCM unsigned 8 bits */ + AUDIO_FORMAT_PCM_SUB_32_BIT = 0x3, /* PCM signed .31 fixed point */ + AUDIO_FORMAT_PCM_SUB_8_24_BIT = 0x4, /* PCM signed 7.24 fixed point */ +} audio_format_pcm_sub_fmt_t; + +/* MP3 sub format field definition : can use 11 LSBs in the same way as MP3 + * frame header to specify bit rate, stereo mode, version... + */ +typedef enum { + AUDIO_FORMAT_MP3_SUB_NONE = 0x0, +} audio_format_mp3_sub_fmt_t; + +/* AMR NB/WB sub format field definition: specify frame block interleaving, + * bandwidth efficient or octet aligned, encoding mode for recording... + */ +typedef enum { + AUDIO_FORMAT_AMR_SUB_NONE = 0x0, +} audio_format_amr_sub_fmt_t; + +/* AAC sub format field definition: specify profile or bitrate for recording... */ +typedef enum { + AUDIO_FORMAT_AAC_SUB_NONE = 0x0, +} audio_format_aac_sub_fmt_t; + +/* VORBIS sub format field definition: specify quality for recording... */ +typedef enum { + AUDIO_FORMAT_VORBIS_SUB_NONE = 0x0, +} audio_format_vorbis_sub_fmt_t; + +/* Audio format consists in a main format field (upper 8 bits) and a sub format + * field (lower 24 bits). + * + * The main format indicates the main codec type. The sub format field + * indicates options and parameters for each format. The sub format is mainly + * used for record to indicate for instance the requested bitrate or profile. + * It can also be used for certain formats to give informations not present in + * the encoded audio stream (e.g. octet alignement for AMR). + */ +typedef enum { + AUDIO_FORMAT_INVALID = 0xFFFFFFFFUL, + AUDIO_FORMAT_DEFAULT = 0, + AUDIO_FORMAT_PCM = 0x00000000UL, /* DO NOT CHANGE */ + AUDIO_FORMAT_MP3 = 0x01000000UL, + AUDIO_FORMAT_AMR_NB = 0x02000000UL, + AUDIO_FORMAT_AMR_WB = 0x03000000UL, + AUDIO_FORMAT_AAC = 0x04000000UL, + AUDIO_FORMAT_HE_AAC_V1 = 0x05000000UL, + AUDIO_FORMAT_HE_AAC_V2 = 0x06000000UL, + AUDIO_FORMAT_VORBIS = 0x07000000UL, + AUDIO_FORMAT_MAIN_MASK = 0xFF000000UL, + AUDIO_FORMAT_SUB_MASK = 0x00FFFFFFUL, + + /* Aliases */ + AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_16_BIT), + AUDIO_FORMAT_PCM_8_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_8_BIT), + AUDIO_FORMAT_PCM_32_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_32_BIT), + AUDIO_FORMAT_PCM_8_24_BIT = (AUDIO_FORMAT_PCM | + AUDIO_FORMAT_PCM_SUB_8_24_BIT), +} audio_format_t; + +enum { + /* output channels */ + AUDIO_CHANNEL_OUT_FRONT_LEFT = 0x1, + AUDIO_CHANNEL_OUT_FRONT_RIGHT = 0x2, + AUDIO_CHANNEL_OUT_FRONT_CENTER = 0x4, + AUDIO_CHANNEL_OUT_LOW_FREQUENCY = 0x8, + AUDIO_CHANNEL_OUT_BACK_LEFT = 0x10, + AUDIO_CHANNEL_OUT_BACK_RIGHT = 0x20, + AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x40, + AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80, + AUDIO_CHANNEL_OUT_BACK_CENTER = 0x100, + AUDIO_CHANNEL_OUT_SIDE_LEFT = 0x200, + AUDIO_CHANNEL_OUT_SIDE_RIGHT = 0x400, + AUDIO_CHANNEL_OUT_TOP_CENTER = 0x800, + AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT = 0x1000, + AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER = 0x2000, + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT = 0x4000, + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT = 0x8000, + AUDIO_CHANNEL_OUT_TOP_BACK_CENTER = 0x10000, + AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT = 0x20000, + + AUDIO_CHANNEL_OUT_MONO = AUDIO_CHANNEL_OUT_FRONT_LEFT, + AUDIO_CHANNEL_OUT_STEREO = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT), + AUDIO_CHANNEL_OUT_QUAD = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT), + AUDIO_CHANNEL_OUT_SURROUND = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_BACK_CENTER), + AUDIO_CHANNEL_OUT_5POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT), + // matches the correct AudioFormat.CHANNEL_OUT_7POINT1_SURROUND definition for 7.1 + AUDIO_CHANNEL_OUT_7POINT1 = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT | + AUDIO_CHANNEL_OUT_SIDE_LEFT | + AUDIO_CHANNEL_OUT_SIDE_RIGHT), + AUDIO_CHANNEL_OUT_ALL = (AUDIO_CHANNEL_OUT_FRONT_LEFT | + AUDIO_CHANNEL_OUT_FRONT_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_CENTER | + AUDIO_CHANNEL_OUT_LOW_FREQUENCY | + AUDIO_CHANNEL_OUT_BACK_LEFT | + AUDIO_CHANNEL_OUT_BACK_RIGHT | + AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER | + AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | + AUDIO_CHANNEL_OUT_BACK_CENTER| + AUDIO_CHANNEL_OUT_SIDE_LEFT| + AUDIO_CHANNEL_OUT_SIDE_RIGHT| + AUDIO_CHANNEL_OUT_TOP_CENTER| + AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT| + AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER| + AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT| + AUDIO_CHANNEL_OUT_TOP_BACK_LEFT| + AUDIO_CHANNEL_OUT_TOP_BACK_CENTER| + AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT), + + /* input channels */ + AUDIO_CHANNEL_IN_LEFT = 0x4, + AUDIO_CHANNEL_IN_RIGHT = 0x8, + AUDIO_CHANNEL_IN_FRONT = 0x10, + AUDIO_CHANNEL_IN_BACK = 0x20, + AUDIO_CHANNEL_IN_LEFT_PROCESSED = 0x40, + AUDIO_CHANNEL_IN_RIGHT_PROCESSED = 0x80, + AUDIO_CHANNEL_IN_FRONT_PROCESSED = 0x100, + AUDIO_CHANNEL_IN_BACK_PROCESSED = 0x200, + AUDIO_CHANNEL_IN_PRESSURE = 0x400, + AUDIO_CHANNEL_IN_X_AXIS = 0x800, + AUDIO_CHANNEL_IN_Y_AXIS = 0x1000, + AUDIO_CHANNEL_IN_Z_AXIS = 0x2000, + AUDIO_CHANNEL_IN_VOICE_UPLINK = 0x4000, + AUDIO_CHANNEL_IN_VOICE_DNLINK = 0x8000, + + AUDIO_CHANNEL_IN_MONO = AUDIO_CHANNEL_IN_FRONT, + AUDIO_CHANNEL_IN_STEREO = (AUDIO_CHANNEL_IN_LEFT | AUDIO_CHANNEL_IN_RIGHT), + AUDIO_CHANNEL_IN_FRONT_BACK = (AUDIO_CHANNEL_IN_FRONT | AUDIO_CHANNEL_IN_BACK), + AUDIO_CHANNEL_IN_ALL = (AUDIO_CHANNEL_IN_LEFT | + AUDIO_CHANNEL_IN_RIGHT | + AUDIO_CHANNEL_IN_FRONT | + AUDIO_CHANNEL_IN_BACK| + AUDIO_CHANNEL_IN_LEFT_PROCESSED | + AUDIO_CHANNEL_IN_RIGHT_PROCESSED | + AUDIO_CHANNEL_IN_FRONT_PROCESSED | + AUDIO_CHANNEL_IN_BACK_PROCESSED| + AUDIO_CHANNEL_IN_PRESSURE | + AUDIO_CHANNEL_IN_X_AXIS | + AUDIO_CHANNEL_IN_Y_AXIS | + AUDIO_CHANNEL_IN_Z_AXIS | + AUDIO_CHANNEL_IN_VOICE_UPLINK | + AUDIO_CHANNEL_IN_VOICE_DNLINK), +}; + +typedef uint32_t audio_channel_mask_t; + +typedef enum { + AUDIO_MODE_INVALID = -2, + AUDIO_MODE_CURRENT = -1, + AUDIO_MODE_NORMAL = 0, + AUDIO_MODE_RINGTONE = 1, + AUDIO_MODE_IN_CALL = 2, + AUDIO_MODE_IN_COMMUNICATION = 3, + + AUDIO_MODE_CNT, + AUDIO_MODE_MAX = AUDIO_MODE_CNT - 1, +} audio_mode_t; + +typedef enum { + AUDIO_IN_ACOUSTICS_AGC_ENABLE = 0x0001, + AUDIO_IN_ACOUSTICS_AGC_DISABLE = 0, + AUDIO_IN_ACOUSTICS_NS_ENABLE = 0x0002, + AUDIO_IN_ACOUSTICS_NS_DISABLE = 0, + AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE = 0x0004, + AUDIO_IN_ACOUSTICS_TX_DISABLE = 0, +} audio_in_acoustics_t; + +enum { + AUDIO_DEVICE_NONE = 0x0, + /* reserved bits */ + AUDIO_DEVICE_BIT_IN = 0x80000000, + AUDIO_DEVICE_BIT_DEFAULT = 0x40000000, + /* output devices */ + AUDIO_DEVICE_OUT_EARPIECE = 0x1, + AUDIO_DEVICE_OUT_SPEAKER = 0x2, + AUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, + AUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO = 0x10, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20, + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP = 0x80, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100, + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200, + AUDIO_DEVICE_OUT_AUX_DIGITAL = 0x400, + AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET = 0x800, + AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000, + AUDIO_DEVICE_OUT_USB_ACCESSORY = 0x2000, + AUDIO_DEVICE_OUT_USB_DEVICE = 0x4000, + AUDIO_DEVICE_OUT_REMOTE_SUBMIX = 0x8000, + AUDIO_DEVICE_OUT_DEFAULT = AUDIO_DEVICE_BIT_DEFAULT, + AUDIO_DEVICE_OUT_ALL = (AUDIO_DEVICE_OUT_EARPIECE | + AUDIO_DEVICE_OUT_SPEAKER | + AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | + AUDIO_DEVICE_OUT_AUX_DIGITAL | + AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET | + AUDIO_DEVICE_OUT_USB_ACCESSORY | + AUDIO_DEVICE_OUT_USB_DEVICE | + AUDIO_DEVICE_OUT_REMOTE_SUBMIX | + AUDIO_DEVICE_OUT_DEFAULT), + AUDIO_DEVICE_OUT_ALL_A2DP = (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), + AUDIO_DEVICE_OUT_ALL_SCO = (AUDIO_DEVICE_OUT_BLUETOOTH_SCO | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), + AUDIO_DEVICE_OUT_ALL_USB = (AUDIO_DEVICE_OUT_USB_ACCESSORY | + AUDIO_DEVICE_OUT_USB_DEVICE), + + /* input devices */ + AUDIO_DEVICE_IN_COMMUNICATION = AUDIO_DEVICE_BIT_IN | 0x1, + AUDIO_DEVICE_IN_AMBIENT = AUDIO_DEVICE_BIT_IN | 0x2, + AUDIO_DEVICE_IN_BUILTIN_MIC = AUDIO_DEVICE_BIT_IN | 0x4, + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET = AUDIO_DEVICE_BIT_IN | 0x8, + AUDIO_DEVICE_IN_WIRED_HEADSET = AUDIO_DEVICE_BIT_IN | 0x10, + AUDIO_DEVICE_IN_AUX_DIGITAL = AUDIO_DEVICE_BIT_IN | 0x20, + AUDIO_DEVICE_IN_VOICE_CALL = AUDIO_DEVICE_BIT_IN | 0x40, + AUDIO_DEVICE_IN_BACK_MIC = AUDIO_DEVICE_BIT_IN | 0x80, + AUDIO_DEVICE_IN_REMOTE_SUBMIX = AUDIO_DEVICE_BIT_IN | 0x100, + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x200, + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET = AUDIO_DEVICE_BIT_IN | 0x400, + AUDIO_DEVICE_IN_USB_ACCESSORY = AUDIO_DEVICE_BIT_IN | 0x800, + AUDIO_DEVICE_IN_USB_DEVICE = AUDIO_DEVICE_BIT_IN | 0x1000, + AUDIO_DEVICE_IN_DEFAULT = AUDIO_DEVICE_BIT_IN | AUDIO_DEVICE_BIT_DEFAULT, + + AUDIO_DEVICE_IN_ALL = (AUDIO_DEVICE_IN_COMMUNICATION | + AUDIO_DEVICE_IN_AMBIENT | + AUDIO_DEVICE_IN_BUILTIN_MIC | + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET | + AUDIO_DEVICE_IN_WIRED_HEADSET | + AUDIO_DEVICE_IN_AUX_DIGITAL | + AUDIO_DEVICE_IN_VOICE_CALL | + AUDIO_DEVICE_IN_BACK_MIC | + AUDIO_DEVICE_IN_REMOTE_SUBMIX | + AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET | + AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET | + AUDIO_DEVICE_IN_USB_ACCESSORY | + AUDIO_DEVICE_IN_USB_DEVICE | + AUDIO_DEVICE_IN_DEFAULT), + AUDIO_DEVICE_IN_ALL_SCO = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET, +}; + +typedef uint32_t audio_devices_t; + +/* the audio output flags serve two purposes: + * - when an AudioTrack is created they indicate a "wish" to be connected to an + * output stream with attributes corresponding to the specified flags + * - when present in an output profile descriptor listed for a particular audio + * hardware module, they indicate that an output stream can be opened that + * supports the attributes indicated by the flags. + * the audio policy manager will try to match the flags in the request + * (when getOuput() is called) to an available output stream. + */ +typedef enum { + AUDIO_OUTPUT_FLAG_NONE = 0x0, // no attributes + AUDIO_OUTPUT_FLAG_DIRECT = 0x1, // this output directly connects a track + // to one output stream: no software mixer + AUDIO_OUTPUT_FLAG_PRIMARY = 0x2, // this output is the primary output of + // the device. It is unique and must be + // present. It is opened by default and + // receives routing, audio mode and volume + // controls related to voice calls. + AUDIO_OUTPUT_FLAG_FAST = 0x4, // output supports "fast tracks", + // defined elsewhere + AUDIO_OUTPUT_FLAG_DEEP_BUFFER = 0x8, // use deep audio buffers + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD = 0x10, // offload playback of compressed + // streams to hardware codec + AUDIO_OUTPUT_FLAG_NON_BLOCKING = 0x20 // use non-blocking write +} audio_output_flags_t; + +/* The audio input flags are analogous to audio output flags. + * Currently they are used only when an AudioRecord is created, + * to indicate a preference to be connected to an input stream with + * attributes corresponding to the specified flags. + */ +typedef enum { + AUDIO_INPUT_FLAG_NONE = 0x0, // no attributes + AUDIO_INPUT_FLAG_FAST = 0x1, // prefer an input that supports "fast tracks" +} audio_input_flags_t; + +/* Additional information about compressed streams offloaded to + * hardware playback + * The version and size fields must be initialized by the caller by using + * one of the constants defined here. + */ +typedef struct { + uint16_t version; // version of the info structure + uint16_t size; // total size of the structure including version and size + uint32_t sample_rate; // sample rate in Hz + audio_channel_mask_t channel_mask; // channel mask + audio_format_t format; // audio format + audio_stream_type_t stream_type; // stream type + uint32_t bit_rate; // bit rate in bits per second + int64_t duration_us; // duration in microseconds, -1 if unknown + bool has_video; // true if stream is tied to a video stream + bool is_streaming; // true if streaming, false if local playback +} audio_offload_info_t; + +#define AUDIO_MAKE_OFFLOAD_INFO_VERSION(maj,min) \ + ((((maj) & 0xff) << 8) | ((min) & 0xff)) + +#define AUDIO_OFFLOAD_INFO_VERSION_0_1 AUDIO_MAKE_OFFLOAD_INFO_VERSION(0, 1) +#define AUDIO_OFFLOAD_INFO_VERSION_CURRENT AUDIO_OFFLOAD_INFO_VERSION_0_1 + +static const audio_offload_info_t AUDIO_INFO_INITIALIZER = { + version: AUDIO_OFFLOAD_INFO_VERSION_CURRENT, + size: sizeof(audio_offload_info_t), +}; + +static inline bool audio_is_output_device(audio_devices_t device) +{ + if (((device & AUDIO_DEVICE_BIT_IN) == 0) && + (popcount(device) == 1) && ((device & ~AUDIO_DEVICE_OUT_ALL) == 0)) + return true; + else + return false; +} + +static inline bool audio_is_input_device(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~AUDIO_DEVICE_IN_ALL) == 0)) + return true; + } + return false; +} + +static inline bool audio_is_output_devices(audio_devices_t device) +{ + return (device & AUDIO_DEVICE_BIT_IN) == 0; +} + + +static inline bool audio_is_a2dp_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_A2DP)) + return true; + else + return false; +} + +static inline bool audio_is_bluetooth_sco_device(audio_devices_t device) +{ + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && (device & (AUDIO_DEVICE_OUT_ALL_SCO | + AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET))) + return true; + else + return false; +} + +static inline bool audio_is_usb_device(audio_devices_t device) +{ + if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_ALL_USB)) + return true; + else + return false; +} + +static inline bool audio_is_remote_submix_device(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_OUT_REMOTE_SUBMIX) == AUDIO_DEVICE_OUT_REMOTE_SUBMIX + || (device & AUDIO_DEVICE_IN_REMOTE_SUBMIX) == AUDIO_DEVICE_IN_REMOTE_SUBMIX) + return true; + else + return false; +} + +static inline bool audio_is_input_channel(audio_channel_mask_t channel) +{ + if ((channel & ~AUDIO_CHANNEL_IN_ALL) == 0) + return channel != 0; + else + return false; +} + +static inline bool audio_is_output_channel(audio_channel_mask_t channel) +{ + if ((channel & ~AUDIO_CHANNEL_OUT_ALL) == 0) + return channel != 0; + else + return false; +} + +/* Derive an output channel mask from a channel count. + * This is to be used when the content channel mask is unknown. The 1, 2, 4, 5, 6, 7 and 8 channel + * cases are mapped to the standard game/home-theater layouts, but note that 4 is mapped to quad, + * and not stereo + FC + mono surround. A channel count of 3 is arbitrarily mapped to stereo + FC + * for continuity with stereo. + * Returns the matching channel mask, or 0 if the number of channels exceeds that of the + * configurations for which a default channel mask is defined. + */ +static inline audio_channel_mask_t audio_channel_out_mask_from_count(uint32_t channel_count) +{ + switch(channel_count) { + case 1: + return AUDIO_CHANNEL_OUT_MONO; + case 2: + return AUDIO_CHANNEL_OUT_STEREO; + case 3: + return (AUDIO_CHANNEL_OUT_STEREO | AUDIO_CHANNEL_OUT_FRONT_CENTER); + case 4: // 4.0 + return AUDIO_CHANNEL_OUT_QUAD; + case 5: // 5.0 + return (AUDIO_CHANNEL_OUT_QUAD | AUDIO_CHANNEL_OUT_FRONT_CENTER); + case 6: // 5.1 + return AUDIO_CHANNEL_OUT_5POINT1; + case 7: // 6.1 + return (AUDIO_CHANNEL_OUT_5POINT1 | AUDIO_CHANNEL_OUT_BACK_CENTER); + case 8: + return AUDIO_CHANNEL_OUT_7POINT1; + default: + return 0; + } +} + +/* Similar to above, but for input. Currently handles only mono and stereo. */ +static inline audio_channel_mask_t audio_channel_in_mask_from_count(uint32_t channel_count) +{ + switch (channel_count) { + case 1: + return AUDIO_CHANNEL_IN_MONO; + case 2: + return AUDIO_CHANNEL_IN_STEREO; + default: + return 0; + } +} + +static inline bool audio_is_valid_format(audio_format_t format) +{ + switch (format & AUDIO_FORMAT_MAIN_MASK) { + case AUDIO_FORMAT_PCM: + if (format != AUDIO_FORMAT_PCM_16_BIT && + format != AUDIO_FORMAT_PCM_8_BIT) { + return false; + } + case AUDIO_FORMAT_MP3: + case AUDIO_FORMAT_AMR_NB: + case AUDIO_FORMAT_AMR_WB: + case AUDIO_FORMAT_AAC: + case AUDIO_FORMAT_HE_AAC_V1: + case AUDIO_FORMAT_HE_AAC_V2: + case AUDIO_FORMAT_VORBIS: + return true; + default: + return false; + } +} + +static inline bool audio_is_linear_pcm(audio_format_t format) +{ + return ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM); +} + +static inline size_t audio_bytes_per_sample(audio_format_t format) +{ + size_t size = 0; + + switch (format) { + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_8_24_BIT: + size = sizeof(int32_t); + break; + case AUDIO_FORMAT_PCM_16_BIT: + size = sizeof(int16_t); + break; + case AUDIO_FORMAT_PCM_8_BIT: + size = sizeof(uint8_t); + break; + default: + break; + } + return size; +} + +__END_DECLS + +#endif // ANDROID_AUDIO_CORE_H diff -Nru bluez-4.101/android/system-emulator.c bluez-5.23/android/system-emulator.c --- bluez-4.101/android/system-emulator.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/system-emulator.c 2014-08-06 17:25:36.000000000 +0000 @@ -0,0 +1,254 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "monitor/mainloop.h" + +static char exec_dir[PATH_MAX]; + +static pid_t daemon_pid = -1; +static pid_t snoop_pid = -1; + +static void run_valgrind(char *prg_name) +{ + char *prg_argv[6]; + char *prg_envp[3]; + + prg_argv[0] = "/usr/bin/valgrind"; + prg_argv[1] = "--leak-check=full"; + prg_argv[2] = "--track-origins=yes"; + prg_argv[3] = prg_name; + prg_argv[4] = "-d"; + prg_argv[5] = NULL; + + prg_envp[0] = "G_SLICE=always-malloc"; + prg_envp[1] = "G_DEBUG=gc-friendly"; + prg_envp[2] = NULL; + + execve(prg_argv[0], prg_argv, prg_envp); +} + +static void run_bluetoothd(char *prg_name) +{ + char *prg_argv[3]; + char *prg_envp[1]; + + prg_argv[0] = prg_name; + prg_argv[1] = "-d"; + prg_argv[2] = NULL; + + prg_envp[0] = NULL; + + execve(prg_argv[0], prg_argv, prg_envp); +} + +static void ctl_start(void) +{ + char prg_name[PATH_MAX]; + pid_t pid; + + snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); + + printf("Starting %s\n", prg_name); + + pid = fork(); + if (pid < 0) { + perror("Failed to fork new process"); + return; + } + + if (pid == 0) { + run_valgrind(prg_name); + + /* Fallback to no valgrind if running with valgind failed */ + run_bluetoothd(prg_name); + exit(0); + } + + printf("New process %d created\n", pid); + + daemon_pid = pid; +} + +static void snoop_start(void) +{ + char prg_name[PATH_MAX]; + char *prg_argv[3]; + char *prg_envp[1]; + pid_t pid; + + snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, + "bluetoothd-snoop"); + + prg_argv[0] = prg_name; + prg_argv[1] = "/tmp/btsnoop_hci.log"; + prg_argv[2] = NULL; + + prg_envp[0] = NULL; + + printf("Starting %s\n", prg_name); + + pid = fork(); + if (pid < 0) { + perror("Failed to fork new process"); + return; + } + + if (pid == 0) { + execve(prg_argv[0], prg_argv, prg_envp); + exit(0); + } + + printf("New process %d created\n", pid); + + snoop_pid = pid; +} + +static void snoop_stop(void) +{ + printf("Stoping %s/%s\n", exec_dir, "bluetoothd-snoop"); + + kill(snoop_pid, SIGTERM); +} + +static void system_socket_callback(int fd, uint32_t events, void *user_data) +{ + char buf[4096]; + ssize_t len; + + if (events & (EPOLLERR | EPOLLHUP)) { + mainloop_remove_fd(fd); + return; + } + + len = read(fd, buf, sizeof(buf)); + if (len < 0) + return; + + printf("Received %s\n", buf); + + if (!strcmp(buf, "bluetooth.start=daemon")) { + if (daemon_pid > 0) + return; + + ctl_start(); + } else if (!strcmp(buf, "bluetooth.start=snoop")) { + if (snoop_pid > 0) + return; + + snoop_start(); + } else if (!strcmp(buf, "bluetooth.stop=snoop")) { + if (snoop_pid > 0) + snoop_stop(); + } +} + +static void signal_callback(int signum, void *user_data) +{ + switch (signum) { + case SIGINT: + case SIGTERM: + mainloop_quit(); + break; + case SIGCHLD: + while (1) { + pid_t pid; + int status; + + pid = waitpid(WAIT_ANY, &status, WNOHANG); + if (pid < 0 || pid == 0) + break; + + printf("Process %d terminated with status=%d\n", + pid, status); + + if (pid == daemon_pid) + daemon_pid = -1; + else if (pid == snoop_pid) + snoop_pid = -1; + } + break; + } +} + +int main(int argc, char *argv[]) +{ + const char SYSTEM_SOCKET_PATH[] = "\0android_system"; + sigset_t mask; + struct sockaddr_un addr; + int fd; + + mainloop_init(); + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGCHLD); + + mainloop_set_signal(&mask, signal_callback, NULL, NULL); + + printf("Android system emulator ver %s\n", VERSION); + + snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); + + fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + perror("Failed to create system socket"); + return EXIT_FAILURE; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to bind system socket"); + close(fd); + return EXIT_FAILURE; + } + + mainloop_add_fd(fd, EPOLLIN, system_socket_callback, NULL, NULL); + + /* Make sure bluetoothd creates files with proper permissions */ + umask(0177); + + return mainloop_run(); +} diff -Nru bluez-4.101/android/tester-a2dp.c bluez-5.23/android/tester-a2dp.c --- bluez-4.101/android/tester-a2dp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-a2dp.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "emulator/bthost.h" +#include "src/shared/util.h" + +#include "tester-main.h" +#include "android/utils.h" + +static struct queue *list; + +struct emu_cid_data { + uint16_t handle; + uint16_t cid; +}; + +static struct emu_cid_data cid_data; + +static const uint8_t req_dsc[] = { 0x00, 0x01 }; +static const uint8_t rsp_dsc[] = { 0x02, 0x01, 0x04, 0x08 }; +static const uint8_t req_get[] = { 0x10, 0x02, 0x04 }; +static const uint8_t rsp_get[] = { 0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, + 0x00, 0xff, 0xff, 0x02, 0x40 }; +static const uint8_t req_cfg[] = { 0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, + 0x06, 0x00, 0x00, 0x21, 0x15, 0x02, + 0x40 }; +static const uint8_t rsp_cfg[] = { 0x22, 0x03 }; +static const uint8_t req_open[] = { 0x30, 0x06, 0x04 }; +static const uint8_t rsp_open[] = { 0x32, 0x06 }; +static const uint8_t req_close[] = { 0x40, 0x08, 0x04 }; +static const uint8_t rsp_close[] = { 0x42, 0x08 }; +static const uint8_t req_start[] = { 0x40, 0x07, 0x04 }; +static const uint8_t rsp_start[] = { 0x42, 0x07 }; +static const uint8_t req_suspend[] = { 0x50, 0x09, 0x04 }; +static const uint8_t rsp_suspend[] = { 0x52, 0x09 }; + +const struct pdu { + const uint8_t *req; + size_t req_len; + const uint8_t *rsp; + size_t rsp_len; +} pdus[] = { + { req_dsc, sizeof(req_dsc), rsp_dsc, sizeof(rsp_dsc) }, + { req_get, sizeof(req_get), rsp_get, sizeof(rsp_get) }, + { req_cfg, sizeof(req_cfg), rsp_cfg, sizeof(rsp_cfg) }, + { req_open, sizeof(req_open), rsp_open, sizeof(rsp_open) }, + { req_close, sizeof(req_close), rsp_close, sizeof(rsp_close) }, + { req_start, sizeof(req_start), rsp_start, sizeof(rsp_start) }, + { req_suspend, sizeof(req_suspend), rsp_suspend, sizeof(rsp_start) }, + { }, +}; + +static void print_data(const char *str, void *user_data) +{ + tester_debug("a2dp: %s", str); +} + +static void a2dp_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + struct emu_cid_data *cid_data = user_data; + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + int i; + + util_hexdump('>', data, len, print_data, NULL); + + for (i = 0; pdus[i].req; i++) { + if (pdus[i].req_len != len) + continue; + + if (memcmp(pdus[i].req, data, len)) + continue; + + util_hexdump('<', pdus[i].rsp, pdus[i].rsp_len, print_data, + NULL); + + bthost_send_cid(bthost, cid_data->handle, cid_data->cid, + pdus[i].rsp, pdus[i].rsp_len); + } +} + +static void a2dp_connect_request_cb(uint16_t handle, uint16_t cid, + void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + if (cid_data.handle) + return; + + cid_data.handle = handle; + cid_data.cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, a2dp_cid_hook_cb, &cid_data); +} + +static struct emu_set_l2cap_data l2cap_setup_data = { + .psm = 25, + .func = a2dp_connect_request_cb, + .user_data = NULL, +}; + +static void a2dp_connect_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + cid_data.handle = 0; + cid_data.cid = 0; + + bdaddr2android((const bdaddr_t *) addr, &bdaddr); + + step->action_status = data->if_a2dp->connect(&bdaddr); + + schedule_action_verification(step); +} + +static void a2dp_disconnect_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) addr, &bdaddr); + + step->action_status = data->if_a2dp->disconnect(&bdaddr); + + schedule_action_verification(step); +} + +static void audio_resume_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + int err; + + err = data->audio->open_output_stream(data->audio, + 0, + AUDIO_DEVICE_OUT_ALL_A2DP, + AUDIO_OUTPUT_FLAG_NONE, + NULL, + &data->if_stream); + if (err < 0) { + step->action_status = BT_STATUS_FAIL; + goto done; + } + + /* Write something to force resume */ + data->if_stream->write(data->if_stream, &err, sizeof(err)); + +done: + schedule_action_verification(step); +} + +static void audio_suspend_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + data->if_stream->common.standby(&data->if_stream->common); + + schedule_action_verification(step); +} + +static struct test_case test_cases[] = { + TEST_CASE_BREDRLE("A2DP Init", + ACTION_SUCCESS(dummy_action, NULL), + ), + TEST_CASE_BREDRLE("A2DP Connect - Success", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(a2dp_connect_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTING), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTED), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_DISCONNECTED), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("A2DP Disconnect - Success", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(a2dp_connect_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTING), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTED), + ACTION_SUCCESS(a2dp_disconnect_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_DISCONNECTING), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_DISCONNECTED), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("A2DP Resume - Success", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(a2dp_connect_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTING), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTED), + ACTION_SUCCESS(audio_resume_action, NULL), + CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE, + BTAV_AUDIO_STATE_STARTED), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_DISCONNECTED), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("A2DP Suspend - Success", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(a2dp_connect_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTING), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTED), + ACTION_SUCCESS(audio_resume_action, NULL), + CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE, + BTAV_AUDIO_STATE_STARTED), + ACTION_SUCCESS(audio_suspend_action, NULL), + CALLBACK_AV_AUDIO_STATE(CB_A2DP_AUDIO_STATE, + BTAV_AUDIO_STATE_STOPPED), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_DISCONNECTED), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), +}; + +struct queue *get_a2dp_tests(void) +{ + uint16_t i = 0; + + list = queue_new(); + + for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) + if (!queue_push_tail(list, &test_cases[i])) + return NULL; + + return list; +} + +void remove_a2dp_tests(void) +{ + queue_destroy(list, NULL); +} diff -Nru bluez-4.101/android/tester-avrcp.c bluez-5.23/android/tester-avrcp.c --- bluez-4.101/android/tester-avrcp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-avrcp.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "emulator/bthost.h" +#include "src/shared/util.h" + +#include "tester-main.h" +#include "android/utils.h" + +static struct queue *list; + +struct emu_cid_data { + uint16_t handle; + uint16_t cid; +}; + +static struct emu_cid_data sdp_data; +static struct emu_cid_data a2dp_data; +static struct emu_cid_data avrcp_data; + +static const uint8_t sdp_rsp_pdu[] = { 0x07, /* PDU id */ + 0x00, 0x00, /* Transaction id */ + 0x00, 0x7f, /* Response length */ + 0x00, 0x7c, /* Attributes length */ + 0x36, 0x00, 0x79, 0x36, 0x00, 0x3b, 0x09, 0x00, 0x00, + 0x0a, 0x00, 0x01, 0x00, 0x04, 0x09, 0x00, 0x01, 0x35, + 0x06, 0x19, 0x11, 0x0e, 0x19, 0x11, 0x0f, 0x09, 0x00, + 0x04, 0x35, 0x10, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, + 0x00, 0x17, 0x35, 0x06, 0x19, 0x00, 0x17, 0x09, 0x01, + 0x03, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, + 0x11, 0x0e, 0x09, 0x01, 0x00, 0x09, 0x03, 0x11, 0x09, + 0x00, 0x01, 0x36, 0x00, 0x38, 0x09, 0x00, 0x00, 0x0a, + 0x00, 0x01, 0x00, 0x05, 0x09, 0x00, 0x01, 0x35, 0x03, + 0x19, 0x11, 0x0c, 0x09, 0x00, 0x04, 0x35, 0x10, 0x35, + 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x17, 0x35, 0x06, + 0x19, 0x00, 0x17, 0x09, 0x01, 0x03, 0x09, 0x00, 0x09, + 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x0e, 0x09, 0x01, + 0x04, 0x09, 0x03, 0x11, 0x09, 0x00, 0x02, + 0x00}; /* no continuation */ +static const uint8_t req_dsc[] = { 0x00, 0x01 }; +static const uint8_t rsp_dsc[] = { 0x02, 0x01, 0x04, 0x08 }; +static const uint8_t req_get[] = { 0x10, 0x02, 0x04 }; +static const uint8_t rsp_get[] = { 0x12, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, + 0x00, 0xff, 0xff, 0x02, 0x40 }; +static const uint8_t req_cfg[] = { 0x20, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, + 0x06, 0x00, 0x00, 0x21, 0x15, 0x02, + 0x40 }; +static const uint8_t rsp_cfg[] = { 0x22, 0x03 }; +static const uint8_t req_open[] = { 0x30, 0x06, 0x04 }; +static const uint8_t rsp_open[] = { 0x32, 0x06 }; +static const uint8_t req_close[] = { 0x40, 0x08, 0x04 }; +static const uint8_t rsp_close[] = { 0x42, 0x08 }; +static const uint8_t req_start[] = { 0x40, 0x07, 0x04 }; +static const uint8_t rsp_start[] = { 0x42, 0x07 }; +static const uint8_t req_suspend[] = { 0x50, 0x09, 0x04 }; +static const uint8_t rsp_suspend[] = { 0x52, 0x09 }; + +static const struct pdu { + const uint8_t *req; + size_t req_len; + const uint8_t *rsp; + size_t rsp_len; +} pdus[] = { + { req_dsc, sizeof(req_dsc), rsp_dsc, sizeof(rsp_dsc) }, + { req_get, sizeof(req_get), rsp_get, sizeof(rsp_get) }, + { req_cfg, sizeof(req_cfg), rsp_cfg, sizeof(rsp_cfg) }, + { req_open, sizeof(req_open), rsp_open, sizeof(rsp_open) }, + { req_close, sizeof(req_close), rsp_close, sizeof(rsp_close) }, + { req_start, sizeof(req_start), rsp_start, sizeof(rsp_start) }, + { req_suspend, sizeof(req_suspend), rsp_suspend, sizeof(rsp_start) }, + { }, +}; + +static void print_avrcp(const char *str, void *user_data) +{ + tester_debug("avrcp: %s", str); +} + +static void avrcp_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + util_hexdump('>', data, len, print_avrcp, NULL); +} + +static void avrcp_connect_request_cb(uint16_t handle, uint16_t cid, + void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + struct emu_cid_data *cid_data = user_data; + + cid_data->handle = handle; + cid_data->cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, avrcp_cid_hook_cb, cid_data); +} + +static struct emu_set_l2cap_data avrcp_setup_data = { + .psm = 23, + .func = avrcp_connect_request_cb, + .user_data = &avrcp_data, +}; + +static void print_a2dp(const char *str, void *user_data) +{ + tester_debug("a2dp: %s", str); +} + +static void a2dp_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + struct emu_cid_data *cid_data = user_data; + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + int i; + + util_hexdump('>', data, len, print_a2dp, NULL); + + for (i = 0; pdus[i].req; i++) { + if (pdus[i].req_len != len) + continue; + + if (memcmp(pdus[i].req, data, len)) + continue; + + util_hexdump('<', pdus[i].rsp, pdus[i].rsp_len, print_a2dp, + NULL); + + bthost_send_cid(bthost, cid_data->handle, cid_data->cid, + pdus[i].rsp, pdus[i].rsp_len); + } +} + +static void a2dp_connect_request_cb(uint16_t handle, uint16_t cid, + void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + struct emu_cid_data *cid_data = user_data; + + if (cid_data->handle) + return; + + cid_data->handle = handle; + cid_data->cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, a2dp_cid_hook_cb, cid_data); +} + +static struct emu_set_l2cap_data a2dp_setup_data = { + .psm = 25, + .func = a2dp_connect_request_cb, + .user_data = &a2dp_data, +}; + +static void sdp_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + struct emu_cid_data *cid_data = user_data; + + bthost_send_cid(bthost, cid_data->handle, cid_data->cid, + sdp_rsp_pdu, sizeof(sdp_rsp_pdu)); +} +static void sdp_connect_request_cb(uint16_t handle, uint16_t cid, + void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + struct emu_cid_data *cid_data = user_data; + + cid_data->handle = handle; + cid_data->cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, sdp_cid_hook_cb, cid_data); +} + +static struct emu_set_l2cap_data sdp_setup_data = { + .psm = 1, + .func = sdp_connect_request_cb, + .user_data = &sdp_data, +}; + +static void avrcp_connect_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + sdp_data.handle = 0; + sdp_data.cid = 0; + + a2dp_data.handle = 0; + a2dp_data.cid = 0; + + avrcp_data.handle = 0; + avrcp_data.cid = 0; + + bdaddr2android((const bdaddr_t *) addr, &bdaddr); + + step->action_status = data->if_a2dp->connect(&bdaddr); + + schedule_action_verification(step); +} + +static void avrcp_disconnect_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) addr, &bdaddr); + + step->action_status = data->if_a2dp->disconnect(&bdaddr); + + schedule_action_verification(step); +} + +static struct test_case test_cases[] = { + TEST_CASE_BREDRLE("AVRCP Init", + ACTION_SUCCESS(dummy_action, NULL), + ), + TEST_CASE_BREDRLE("AVRCP Connect - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), + ACTION_SUCCESS(avrcp_connect_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTING), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTED), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("AVRCP Disconnect - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &sdp_setup_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, &a2dp_setup_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, &avrcp_setup_data), + ACTION_SUCCESS(avrcp_connect_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTING), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_CONNECTED), + ACTION_SUCCESS(avrcp_disconnect_action, NULL), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_DISCONNECTING), + CALLBACK_AV_CONN_STATE(CB_A2DP_CONN_STATE, + BTAV_CONNECTION_STATE_DISCONNECTED), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), +}; + +struct queue *get_avrcp_tests(void) +{ + uint16_t i = 0; + + list = queue_new(); + + for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) + if (!queue_push_tail(list, &test_cases[i])) + return NULL; + + return list; +} + +void remove_avrcp_tests(void) +{ + queue_destroy(list, NULL); +} diff -Nru bluez-4.101/android/tester-bluetooth.c bluez-5.23/android/tester-bluetooth.c --- bluez-4.101/android/tester-bluetooth.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-bluetooth.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,1227 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include "emulator/bthost.h" +#include "tester-main.h" + +static struct queue *list; /* List of bluetooth test cases */ + +static bt_bdaddr_t emu_bdaddr_val = { + .address = { 0x00, 0xaa, 0x01, 0x00, 0x00, 0x00 }, +}; +static bt_property_t prop_emu_bdaddr = { + .type = BT_PROPERTY_BDADDR, + .val = &emu_bdaddr_val, + .len = sizeof(emu_bdaddr_val), +}; + +static const char emu_bdname_val[] = "BlueZ for Android"; +static bt_property_t prop_emu_bdname = { + .type = BT_PROPERTY_BDNAME, + .val = &emu_bdname_val, + .len = sizeof(emu_bdname_val) - 1, +}; + +static const char emu_uuids_val[] = { + /* Multi profile UUID */ + 0x00, 0x00, 0x11, 0x3b, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB, + /* Device identification profile UUID */ + 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, + 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB, +}; +static bt_property_t prop_emu_uuids = { + .type = BT_PROPERTY_UUIDS, + .val = &emu_uuids_val, + .len = sizeof(emu_uuids_val), +}; + +static uint32_t emu_cod_val = 0x00020c; +static bt_property_t prop_emu_cod = { + .type = BT_PROPERTY_CLASS_OF_DEVICE, + .val = &emu_cod_val, + .len = sizeof(emu_cod_val), +}; + +static bt_device_type_t emu_tod_dual_val = BT_DEVICE_DEVTYPE_DUAL; +static bt_property_t prop_emu_dual_tod = { + .type = BT_PROPERTY_TYPE_OF_DEVICE, + .val = &emu_tod_dual_val, + .len = sizeof(emu_tod_dual_val), +}; + +static bt_scan_mode_t emu_scan_mode_val = BT_SCAN_MODE_NONE; +static bt_property_t prop_emu_scan_mode = { + .type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .val = &emu_scan_mode_val, + .len = sizeof(emu_scan_mode_val), +}; + +static uint32_t emu_disc_timeout_val = 120; +static bt_property_t prop_emu_disc_timeout = { + .type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + .val = &emu_disc_timeout_val, + .len = sizeof(emu_disc_timeout_val), +}; + +static bt_property_t prop_emu_bonded_devs = { + .type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, + .val = NULL, + .len = 0, +}; + +static bt_bdaddr_t emu_remote_bdaddr_val = { + .address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 }, +}; +static bt_property_t prop_emu_remote_bdadr = { + .type = BT_PROPERTY_BDADDR, + .val = &emu_remote_bdaddr_val, + .len = sizeof(emu_remote_bdaddr_val), +}; +static struct bt_action_data prop_emu_remote_ble_bdaddr_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_BDADDR, +}; + +static uint32_t emu_remote_type_val = BT_DEVICE_DEVTYPE_BREDR; + +static uint32_t emu_remote_tod_ble_val = BT_DEVICE_DEVTYPE_BLE; +static bt_property_t prop_emu_remote_ble_tod_prop = { + .type = BT_PROPERTY_TYPE_OF_DEVICE, + .val = &emu_remote_tod_ble_val, + .len = sizeof(emu_remote_tod_ble_val), +}; +static struct bt_action_data prop_emu_remote_ble_tod_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_TYPE_OF_DEVICE, +}; + +static int32_t emu_remote_rssi_val = -60; + +static int32_t emu_remote_ble_rssi_val = 127; +static bt_property_t prop_emu_remote_ble_rssi_prop = { + .type = BT_PROPERTY_REMOTE_RSSI, + .val = &emu_remote_ble_rssi_val, + .len = sizeof(emu_remote_ble_rssi_val), +}; +static struct bt_action_data prop_emu_remote_ble_rssi_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_REMOTE_RSSI, +}; + +static const char emu_remote_bdname_val[] = "00:AA:01:01:00:00"; +static bt_property_t prop_emu_remote_ble_bdname_prop = { + .type = BT_PROPERTY_BDNAME, + .val = &emu_remote_bdname_val, + .len = sizeof(emu_remote_bdname_val) - 1, +}; +static struct bt_action_data prop_emu_remote_ble_bdname_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_BDNAME, +}; + +static uint32_t emu_remote_cod_val = 0; +static bt_property_t prop_emu_remote_ble_cod_prop = { + .type = BT_PROPERTY_CLASS_OF_DEVICE, + .val = &emu_remote_cod_val, + .len = sizeof(emu_remote_cod_val), +}; +static struct bt_action_data prop_emu_remote_ble_cod_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_CLASS_OF_DEVICE, +}; + +static bt_property_t prop_emu_remote_ble_uuids_prop = { + .type = BT_PROPERTY_UUIDS, + .val = NULL, + .len = 0, +}; +static struct bt_action_data prop_emu_remote_ble_uuids_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_UUIDS, +}; + +static bt_property_t prop_emu_remote_ble_timestamp_prop = { + .type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, + .val = NULL, + .len = 4, +}; +static struct bt_action_data prop_emu_remote_ble_timestamp_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, +}; + +static struct bt_action_data prop_emu_remote_ble_scan_mode_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_ADAPTER_SCAN_MODE, +}; + +static struct bt_action_data prop_emu_remote_ble_bondeddev_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, +}; + +static struct bt_action_data prop_emu_remote_ble_disctimeout_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, +}; + +static struct bt_action_data prop_emu_remote_ble_verinfo_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_REMOTE_VERSION_INFO, +}; + +static const char prop_test_fname_val[] = "FriendlyTestName"; +static bt_property_t prop_emu_remote_ble_fname_prop = { + .type = BT_PROPERTY_REMOTE_FRIENDLY_NAME, + .val = &prop_test_fname_val, + .len = sizeof(prop_test_fname_val) - 1, +}; +static struct bt_action_data prop_emu_remote_ble_fname_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_REMOTE_FRIENDLY_NAME, + .prop = &prop_emu_remote_ble_fname_prop, +}; + +static bt_pin_code_t emu_pin_value = { + .pin = { 0x30, 0x30, 0x30, 0x30 }, +}; +static bt_pin_code_t emu_pin_invalid_value = { + .pin = { 0x30, 0x10, 0x30, 0x30 }, +}; +static struct bt_action_data emu_pin_set_req = { + .addr = &emu_remote_bdaddr_val, + .pin = &emu_pin_value, + .pin_len = 4, +}; +static struct bt_action_data emu_pin_set_invalid_req = { + .addr = &emu_remote_bdaddr_val, + .pin = &emu_pin_invalid_value, + .pin_len = 4, +}; + +static bt_property_t prop_emu_default_set[] = { + { BT_PROPERTY_BDADDR, sizeof(emu_bdaddr_val), NULL }, + { BT_PROPERTY_BDNAME, sizeof(emu_bdname_val) - 1, &emu_bdname_val }, + { BT_PROPERTY_CLASS_OF_DEVICE, sizeof(uint32_t), NULL }, + { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_tod_dual_val), + &emu_tod_dual_val }, + { BT_PROPERTY_ADAPTER_SCAN_MODE, sizeof(emu_scan_mode_val), + &emu_scan_mode_val }, + { BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, sizeof(emu_disc_timeout_val), + &emu_disc_timeout_val}, + { BT_PROPERTY_ADAPTER_BONDED_DEVICES, 0, NULL }, + { BT_PROPERTY_UUIDS, sizeof(emu_uuids_val), &emu_uuids_val }, +}; + +static bt_property_t prop_emu_remote_bles_default_set[] = { + { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), + &emu_remote_bdaddr_val }, + { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_tod_ble_val), + &emu_remote_tod_ble_val }, + { BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_ble_rssi_val), + &emu_remote_ble_rssi_val }, +}; + +static bt_property_t prop_emu_remotes_default_set[] = { + { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), + &emu_remote_bdaddr_val }, + { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_type_val), + &emu_remote_type_val }, + { BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_rssi_val), + &emu_remote_rssi_val }, +}; + +static bt_property_t prop_emu_remote_bles_query_set[] = { + { BT_PROPERTY_TYPE_OF_DEVICE, sizeof(emu_remote_tod_ble_val), + &emu_remote_tod_ble_val }, + { BT_PROPERTY_CLASS_OF_DEVICE, sizeof(emu_remote_cod_val), + &emu_remote_cod_val }, + { BT_PROPERTY_REMOTE_RSSI, sizeof(emu_remote_ble_rssi_val), + &emu_remote_ble_rssi_val }, + { BT_PROPERTY_BDNAME, sizeof(emu_remote_bdname_val) - 1, + &emu_remote_bdname_val }, + { BT_PROPERTY_UUIDS, 0, NULL }, + { BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, 4, NULL }, +}; + +static bt_property_t prop_emu_remotes_pin_req_set[] = { + { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), + &emu_remote_bdaddr_val }, + { BT_PROPERTY_CLASS_OF_DEVICE, sizeof(emu_remote_cod_val), + &emu_remote_cod_val }, + { BT_PROPERTY_BDNAME, sizeof(emu_remote_bdname_val) - 1, + &emu_remote_bdname_val }, +}; + +static char test_bdname[] = "test_bdname"; +static bt_property_t prop_test_bdname = { + .type = BT_PROPERTY_BDNAME, + .val = test_bdname, + .len = sizeof(test_bdname) - 1, +}; +static struct bt_action_data prop_test_remote_ble_bdname_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_BDNAME, + .prop = &prop_test_bdname, +}; + +static bt_scan_mode_t test_scan_mode_connectable_discoverable = + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE; +static bt_property_t prop_test_scanmode_conn_discov = { + .type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .val = &test_scan_mode_connectable_discoverable, + .len = sizeof(bt_scan_mode_t), +}; + +static uint32_t test_disctimeout_val = 600; +static bt_property_t prop_test_disctimeout = { + .type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + .val = &test_disctimeout_val, + .len = sizeof(test_disctimeout_val), +}; +static struct bt_action_data prop_test_remote_ble_disc_timeout_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_ADAPTER_DISCOVERY_TIMEOUT, + .prop = &prop_test_disctimeout, +}; + +static unsigned char test_uuids_val[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, + 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }; +static bt_property_t prop_test_uuid = { + .type = BT_PROPERTY_UUIDS, + .val = &test_uuids_val, + .len = sizeof(test_uuids_val), +}; +static struct bt_action_data prop_test_remote_ble_uuids_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_UUIDS, + .prop = &prop_test_uuid, +}; + +static uint32_t test_cod_val = 0; +static bt_property_t prop_test_cod = { + .type = BT_PROPERTY_CLASS_OF_DEVICE, + .val = &test_cod_val, + .len = sizeof(test_cod_val), +}; +static struct bt_action_data prop_test_remote_ble_cod_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_CLASS_OF_DEVICE, + .prop = &prop_test_cod, +}; + +static uint32_t test_tod_val = BT_DEVICE_DEVTYPE_BLE; +static bt_property_t prop_test_tod = { + .type = BT_PROPERTY_TYPE_OF_DEVICE, + .val = &test_tod_val, + .len = sizeof(test_tod_val), +}; +static struct bt_action_data prop_test_remote_ble_tod_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_TYPE_OF_DEVICE, + .prop = &prop_test_tod, +}; + +static int32_t test_remote_rssi_val = -9; +static bt_property_t prop_test_remote_rssi = { + .type = BT_PROPERTY_REMOTE_RSSI, + .val = &test_remote_rssi_val, + .len = sizeof(test_remote_rssi_val), +}; +static struct bt_action_data prop_test_remote_ble_rssi_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_REMOTE_RSSI, + .prop = &prop_test_remote_rssi, +}; + +static bt_service_record_t test_srvc_record_val = { + .uuid = { {0x00} }, + .channel = 12, + .name = "bt_name", +}; +static bt_property_t prop_test_srvc_record = { + .type = BT_PROPERTY_SERVICE_RECORD, + .val = &test_srvc_record_val, + .len = sizeof(test_srvc_record_val), +}; +static struct bt_action_data prop_test_remote_ble_srvc_record_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_SERVICE_RECORD, + .prop = &prop_test_srvc_record, +}; + +static bt_bdaddr_t test_bdaddr_val = { + .address = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +}; +static bt_property_t prop_test_bdaddr = { + .type = BT_PROPERTY_BDADDR, + .val = &test_bdaddr_val, + .len = sizeof(test_bdaddr_val), +}; +static struct bt_action_data prop_test_remote_ble_bdaddr_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_BDADDR, + .prop = &prop_test_bdaddr, +}; +static struct bt_action_data prop_test_bdaddr_req = { + .addr = &test_bdaddr_val, + .prop_type = BT_PROPERTY_BDADDR, + .prop = &prop_test_bdaddr, +}; + +static bt_scan_mode_t setprop_scan_mode_conn_val = BT_SCAN_MODE_CONNECTABLE; + +static bt_property_t prop_test_scan_mode_conn = { + .type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .val = &setprop_scan_mode_conn_val, + .len = sizeof(setprop_scan_mode_conn_val), +}; + +static bt_scan_mode_t test_scan_mode_none_val = BT_SCAN_MODE_NONE; +static bt_property_t prop_test_scan_mode_none = { + .type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .val = &test_scan_mode_none_val, + .len = sizeof(test_scan_mode_none_val), +}; +static struct bt_action_data prop_test_remote_ble_scanmode_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .prop = &prop_test_scan_mode_none, +}; + +static bt_bdaddr_t test_bonded_dev_addr_val = { + .address = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }, +}; +static bt_property_t prop_test_bonded_dev_addr = { + .type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, + .val = &test_bonded_dev_addr_val, + .len = sizeof(test_bonded_dev_addr_val), +}; +static struct bt_action_data prop_test_ble_bonded_dev_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_ADAPTER_BONDED_DEVICES, + .prop = &prop_test_bonded_dev_addr, +}; + +static uint32_t test_remote_timestamp_val = 42; +static bt_property_t prop_test_remote_ble_timestamp_prop = { + .type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, + .val = &test_remote_timestamp_val, + .len = sizeof(test_remote_timestamp_val), +}; +static struct bt_action_data prop_test_remote_ble_timestamp_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP, + .prop = &prop_test_remote_ble_timestamp_prop, +}; + +static struct bt_action_data ssp_confirm_accept_reply = { + .addr = &emu_remote_bdaddr_val, + .ssp_variant = BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + .accept = TRUE, +}; + +static struct bt_action_data ssp_confirm_reject_reply = { + .addr = &emu_remote_bdaddr_val, + .ssp_variant = BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + .accept = FALSE, +}; + +static struct bt_action_data no_input_no_output_io_cap = { + .io_cap = 0x03, +}; + +static uint16_t test_conn_handle = 0; + +static void conn_cb(uint16_t handle, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + tester_print("New connection with handle 0x%04x", handle); + + test_conn_handle = handle; + + bthost_request_auth(bthost, handle); +} + +static struct test_case test_cases[] = { + TEST_CASE_BREDRLE("Bluetooth Init", + ACTION_SUCCESS(dummy_action, NULL), + ), + TEST_CASE_BREDRLE("Bluetooth Enable - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_ADAPTER_PROPS(prop_emu_default_set, 8), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ), + TEST_CASE_BREDRLE("Bluetooth Enable - Success 2", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_ADAPTER_PROPS(prop_emu_default_set, 8), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + ), + TEST_CASE_BREDRLE("Bluetooth Disable - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Bluetooth Set BDNAME - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_set_property_action, &prop_test_bdname), + CALLBACK_ADAPTER_PROPS(&prop_test_bdname, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_set_property_action, + &prop_test_scanmode_conn_discov), + CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Set DISCOVERY_TIMEOUT - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_set_property_action, &prop_test_disctimeout), + CALLBACK_ADAPTER_PROPS(&prop_test_disctimeout, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Get BDADDR - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_get_property_action, &prop_emu_bdaddr), + CALLBACK_ADAPTER_PROPS(&prop_emu_bdaddr, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Get BDNAME - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_get_property_action, &prop_emu_bdname), + CALLBACK_ADAPTER_PROPS(&prop_emu_bdname, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Set UUID - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_FAIL(bt_set_property_action, &prop_test_uuid), + ), + TEST_CASE_BREDRLE("Bluetooth Set CLASS_OF_DEVICE - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_FAIL(bt_set_property_action, &prop_test_cod), + ), + TEST_CASE_BREDRLE("Bluetooth Set TYPE_OF_DEVICE - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_FAIL(bt_set_property_action, &prop_test_tod), + ), + TEST_CASE_BREDRLE("Bluetooth Set REMOTE_RSSI - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_FAIL(bt_set_property_action, &prop_test_remote_rssi), + ), + TEST_CASE_BREDRLE("Bluetooth Set SERVICE_RECORD - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_FAIL(bt_set_property_action, &prop_test_srvc_record), + ), + TEST_CASE_BREDRLE("Bluetooth Set BDADDR - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_FAIL(bt_set_property_action, &prop_test_bdaddr), + ), + TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE_CONNECTABLE - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_set_property_action, + &prop_test_scan_mode_conn), + CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Set BONDED_DEVICES - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_FAIL(bt_set_property_action, &prop_test_bonded_dev_addr), + ), + TEST_CASE_BREDRLE("Bluetooth Get CLASS_OF_DEVICE - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_get_property_action, &prop_emu_cod), + CALLBACK_ADAPTER_PROPS(&prop_emu_cod, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Get TYPE_OF_DEVICE - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_get_property_action, &prop_emu_dual_tod), + CALLBACK_ADAPTER_PROPS(&prop_emu_dual_tod, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Get SCAN_MODE - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_get_property_action, &prop_emu_scan_mode), + CALLBACK_ADAPTER_PROPS(&prop_emu_scan_mode, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Get DISCOVERY_TIMEOUT - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_get_property_action, &prop_emu_disc_timeout), + CALLBACK_ADAPTER_PROPS(&prop_emu_disc_timeout, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Get UUIDS - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_get_property_action, &prop_emu_uuids), + CALLBACK_ADAPTER_PROPS(&prop_emu_uuids, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Get BONDED_DEVICES - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_get_property_action, &prop_emu_bonded_devs), + CALLBACK_ADAPTER_PROPS(&prop_emu_bonded_devs, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Set SCAN_MODE - Success 2", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_set_property_action, + &prop_test_scan_mode_none), + CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_none, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Discovery Start - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ), + TEST_CASE_BREDRLE("Bluetooth Discovery Start - Done", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + ), + TEST_CASE_BREDRLE("Bluetooth Discovery Stop - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ), + TEST_CASE_BREDRLE("Bluetooth Discovery Stop - Done", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + ), + TEST_CASE_BREDRLE("Bluetooth Discovery Device Found", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + CALLBACK_DEVICE_FOUND(prop_emu_remote_bles_default_set, 3), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get Props - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_get_device_props_action, + &emu_remote_bdaddr_val), + CALLBACK_DEVICE_PROPS(prop_emu_remote_bles_query_set, 6), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get BDNAME - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_get_device_prop_action, + &prop_emu_remote_ble_bdname_req), + CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_bdname_prop, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get UUIDS - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_get_device_prop_action, + &prop_emu_remote_ble_uuids_req), + CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_uuids_prop, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get COD - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_get_device_prop_action, + &prop_emu_remote_ble_cod_req), + CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_cod_prop, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get TOD - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_get_device_prop_action, + &prop_emu_remote_ble_tod_req), + CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_tod_prop, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get RSSI - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_get_device_prop_action, + &prop_emu_remote_ble_rssi_req), + CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_rssi_prop, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get TIMESTAMP - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_get_device_prop_action, + &prop_emu_remote_ble_timestamp_req), + CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_timestamp_prop, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get BDADDR - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_get_device_prop_action, + &prop_emu_remote_ble_bdaddr_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get SCAN_MODE - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_get_device_prop_action, + &prop_emu_remote_ble_scan_mode_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get BONDED_DEVICES - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_get_device_prop_action, + &prop_emu_remote_ble_bondeddev_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get DISCOVERY_TIMEOUT - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_get_device_prop_action, + &prop_emu_remote_ble_disctimeout_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get VERSION_INFO - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_get_device_prop_action, + &prop_emu_remote_ble_verinfo_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Get FRIENDLY_NAME - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_get_device_prop_action, + &prop_emu_remote_ble_fname_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set FRIENDLY_NAME - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_set_device_prop_action, + &prop_emu_remote_ble_fname_req), + ACTION_SUCCESS(bt_get_device_prop_action, + &prop_emu_remote_ble_fname_req), + CALLBACK_DEVICE_PROPS(&prop_emu_remote_ble_fname_prop, 1), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set BDNAME - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_bdname_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set UUIDS - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_uuids_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set COD - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_cod_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set TOD - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_tod_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set RSSI - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_rssi_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set TIMESTAMP - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_timestamp_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set BDADDR - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_bdaddr_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set SERVICE_RECORD - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_srvc_record_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set SCAN_MODE - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_scanmode_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set BONDED_DEVICES - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_ble_bonded_dev_req), + ), + TEST_CASE_BREDRLE("Bluetooth Device Set DISCOVERY_TIMEOUT - Fail", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_FAIL(bt_set_device_prop_action, + &prop_test_remote_ble_disc_timeout_req), + ), + TEST_CASE_BREDR("Bluetooth Create Bond PIN - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_pin_code_action, &emu_pin_set_req), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_create_bond_action, + &prop_test_remote_ble_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_PROPS(CB_BT_PIN_REQUEST, prop_emu_remotes_pin_req_set, + 2), + ACTION_SUCCESS(bt_pin_reply_accept_action, + &emu_pin_set_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, + &prop_emu_remote_bdadr, 1), + ), + TEST_CASE_BREDR("Bluetooth Create Bond PIN - Bad PIN", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_pin_code_action, &emu_pin_set_req), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_create_bond_action, + &prop_test_remote_ble_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_PROPS(CB_BT_PIN_REQUEST, prop_emu_remotes_pin_req_set, + 2), + ACTION_SUCCESS(bt_pin_reply_accept_action, + &emu_pin_set_invalid_req), + CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE, + &prop_emu_remote_bdadr, 1, + BT_STATUS_AUTH_FAILURE), + ), + TEST_CASE_BREDR("Bluetooth Create Bond SSP -Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_create_bond_action, + &prop_test_remote_ble_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + prop_emu_remotes_pin_req_set, 2), + ACTION_SUCCESS(bt_ssp_reply_accept_action, + &ssp_confirm_accept_reply), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, + &prop_emu_remote_bdadr, 1), + ), + TEST_CASE_BREDR("Bluetooth Create Bond SSP - Negative reply", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_create_bond_action, + &prop_test_remote_ble_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + prop_emu_remotes_pin_req_set, 2), + ACTION_SUCCESS(bt_ssp_reply_accept_action, + &ssp_confirm_reject_reply), + CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE, + &prop_emu_remote_bdadr, 1, + BT_STATUS_AUTH_FAILURE), + ), + TEST_CASE_BREDR("Bluetooth Create Bond - No Discovery", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(bt_create_bond_action, + &prop_test_remote_ble_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + prop_emu_remotes_pin_req_set, 2), + ACTION_SUCCESS(bt_ssp_reply_accept_action, + &ssp_confirm_accept_reply), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, + &prop_emu_remote_bdadr, 1), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDR("Bluetooth Create Bond - Bad Address", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(bt_create_bond_action, &prop_test_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_test_bdaddr, 1), + CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE, + &prop_test_bdaddr, 1, + BT_STATUS_FAIL), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDR("Bluetooth Cancel Bonding - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_create_bond_action, + &prop_test_remote_ble_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + prop_emu_remotes_pin_req_set, 2), + ACTION_SUCCESS(bt_cancel_bond_action, &emu_remote_bdaddr_val), + CALLBACK_BOND_STATE_FAILED(BT_BOND_STATE_NONE, + &prop_emu_remote_bdadr, 1, + BT_STATUS_FAIL), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDR("Bluetooth Remove Bond - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(bt_start_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STARTED), + CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 3), + ACTION_SUCCESS(bt_cancel_discovery_action, NULL), + CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED, + BT_DISCOVERY_STOPPED), + ACTION_SUCCESS(bt_create_bond_action, + &prop_test_remote_ble_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_SSP_REQ(BT_SSP_VARIANT_PASSKEY_CONFIRMATION, + prop_emu_remotes_pin_req_set, 2), + ACTION_SUCCESS(bt_ssp_reply_accept_action, + &ssp_confirm_accept_reply), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, + &prop_emu_remote_bdadr, 1), + ACTION_SUCCESS(bt_remove_bond_action, &emu_remote_bdaddr_val), + CALLBACK_BOND_STATE(BT_BOND_STATE_NONE, + &prop_emu_remote_bdadr, 1), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDR("Bluetooth Accept Bond - Just Works - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_set_property_action, + &prop_test_scanmode_conn_discov), + CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_io_cap, &no_input_no_output_io_cap), + ACTION_SUCCESS(emu_set_connect_cb_action, conn_cb), + ACTION_SUCCESS(emu_remote_connect_hci_action, NULL), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, + &prop_emu_remote_bdadr, 1), + ACTION_SUCCESS(bt_remove_bond_action, &emu_remote_bdaddr_val), + CALLBACK_BOND_STATE(BT_BOND_STATE_NONE, + &prop_emu_remote_bdadr, 1), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDR("Bluetooth Accept Bond - No Bond - Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(bt_set_property_action, + &prop_test_scanmode_conn_discov), + CALLBACK_ADAPTER_PROPS(&prop_test_scanmode_conn_discov, 1), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_io_cap, &no_input_no_output_io_cap), + ACTION_SUCCESS(emu_set_connect_cb_action, conn_cb), + ACTION_SUCCESS(emu_remote_connect_hci_action, NULL), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, + &prop_emu_remote_bdadr, 1), + ACTION_SUCCESS(emu_remote_disconnect_hci_action, + &test_conn_handle), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_BOND_STATE(BT_BOND_STATE_NONE, + &prop_emu_remote_bdadr, 1), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), +}; + +struct queue *get_bluetooth_tests(void) +{ + uint16_t i = 0; + + list = queue_new(); + + for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) + if (!queue_push_tail(list, &test_cases[i])) + return NULL; + + return list; +} + +void remove_bluetooth_tests(void) +{ + queue_destroy(list, NULL); +} diff -Nru bluez-4.101/android/tester-gatt.c bluez-5.23/android/tester-gatt.c --- bluez-4.101/android/tester-gatt.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-gatt.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,738 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "emulator/bthost.h" +#include "tester-main.h" +#include "src/shared/util.h" + +#define L2CAP_ATT_EXCHANGE_MTU_REQ 0x02 +#define L2CAP_ATT_EXCHANGE_MTU_RSP 0x03 + +#define GATT_STATUS_SUCCESS 0x00000000 +#define GATT_STATUS_FAILURE 0x00000101 + +#define CLIENT1_ID 1 +#define CLIENT2_ID 2 + +#define CONN1_ID 1 +#define CONN2_ID 2 + +#define data(args...) ((const unsigned char[]) { args }) + +#define raw_pdu(args...) \ + { \ + .data = data(args), \ + .size = sizeof(data(args)), \ + } + +#define end_pdu { .data = NULL } + +static struct queue *list; /* List of gatt test cases */ + +struct pdu { + const uint8_t *data; + uint16_t size; +}; + +static bt_uuid_t client_app_uuid = { + .uu = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, +}; + +struct emu_cid_data { + const int pdu_len; + const void *pdu; + + uint16_t handle; + uint16_t cid; +}; + +struct gatt_connect_data { + const int client_id; + const int conn_id; +}; + +struct gatt_search_service_data { + const int conn_id; + bt_uuid_t *filter_uuid; +}; + +struct get_char_data { + const int conn_id; + btgatt_srvc_id_t *service; +}; + +static bt_uuid_t client2_app_uuid = { + .uu = { 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 }, +}; + +static bt_bdaddr_t emu_remote_bdaddr_val = { + .address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 }, +}; +static bt_property_t prop_emu_remotes_default_set[] = { + { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), + &emu_remote_bdaddr_val }, +}; + +static bt_scan_mode_t setprop_scan_mode_conn_val = + BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE; + +static bt_property_t prop_test_scan_mode_conn = { + .type = BT_PROPERTY_ADAPTER_SCAN_MODE, + .val = &setprop_scan_mode_conn_val, + .len = sizeof(setprop_scan_mode_conn_val), +}; + +static struct emu_cid_data cid_data; + +static struct gatt_connect_data client1_conn_req = { + .client_id = CLIENT1_ID, + .conn_id = CONN1_ID, +}; + +static struct gatt_connect_data client1_conn2_req = { + .client_id = CLIENT1_ID, + .conn_id = CONN2_ID, +}; + +static struct gatt_connect_data client2_conn_req = { + .client_id = CLIENT2_ID, + .conn_id = CONN2_ID, +}; + +static struct gatt_search_service_data search_services_1 = { + .conn_id = CONN1_ID, + .filter_uuid = NULL, +}; + +static const uint8_t exchange_mtu_req_pdu[] = { 0x02, 0xa0, 0x02 }; +static const uint8_t exchange_mtu_resp_pdu[] = { 0x03, 0xa0, 0x02 }; + +static struct bt_action_data bearer_type = { + .bearer_type = BDADDR_LE_PUBLIC, +}; + +static btgatt_srvc_id_t service_1 = { + .is_primary = true, + .id = { + .inst_id = 0, + .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00} + } +}; + +static btgatt_srvc_id_t service_2 = { + .is_primary = true, + .id = { + .inst_id = 1, + .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x01, 0x18, 0x00, 0x00}, + } +}; + +static btgatt_gatt_id_t characteristic_1 = { + .inst_id = 0, + .uuid.uu = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00} +}; + +static struct get_char_data get_char_data_1 = { + .conn_id = CONN1_ID, + .service = &service_1 +}; + +static struct pdu search_service[] = { + raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), + raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18), + raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x00, 0x28), + raw_pdu(0x01, 0x10, 0x11, 0x00, 0x0a), + end_pdu +}; + +static struct pdu search_service_2[] = { + raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), + raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18), + raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x00, 0x28), + raw_pdu(0x11, 0x06, 0x11, 0x00, 0x20, 0x00, 0x01, 0x18), + raw_pdu(0x10, 0x21, 0x00, 0xff, 0xff, 0x00, 0x28), + raw_pdu(0x01, 0x10, 0x21, 0x00, 0x0a), + end_pdu +}; + +static struct pdu search_service_3[] = { + raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), + raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), + end_pdu +}; + +static struct pdu get_characteristic_1[] = { + raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), + raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18), + raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x00, 0x28), + raw_pdu(0x01, 0x11, 0x11, 0x00, 0x0a), + raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), + raw_pdu(0x09, 0x07, 0x02, 0x00, 0x04, 0x00, 0x00, 0x19, 0x00), + raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), + raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), + end_pdu +}; + +static void gatt_client_register_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + bt_uuid_t *app_uuid = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + if (!app_uuid) { + tester_warn("No app uuid provided for register action."); + return; + } + + step->action_status = data->if_gatt->client->register_client(app_uuid); + + schedule_action_verification(step); +} + +static void gatt_client_unregister_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + int32_t cl_id = PTR_TO_INT(current_data_step->set_data); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_gatt->client->unregister_client(cl_id); + + schedule_action_verification(step); +} + +static void gatt_client_start_scan_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + int32_t cl_id = PTR_TO_INT(current_data_step->set_data); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_gatt->client->scan(cl_id, TRUE); + + schedule_action_verification(step); +} + +static void gatt_client_stop_scan_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + int32_t cl_id = PTR_TO_INT(current_data_step->set_data); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_gatt->client->scan(cl_id, FALSE); + + schedule_action_verification(step); +} + +static void gatt_client_connect_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct gatt_connect_data *conn_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_gatt->client->connect( + conn_data->client_id, + &emu_remote_bdaddr_val, + 0); + + schedule_action_verification(step); +} + +static void gatt_client_disconnect_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct gatt_connect_data *conn_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_gatt->client->disconnect( + conn_data->client_id, + &emu_remote_bdaddr_val, + conn_data->conn_id); + + schedule_action_verification(step); +} + +static void gatt_client_do_listen_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct gatt_connect_data *conn_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_gatt->client->listen( + conn_data->client_id, + 1); + + schedule_action_verification(step); +} + +static void gatt_client_stop_listen_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct gatt_connect_data *conn_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_gatt->client->listen( + conn_data->client_id, + 0); + + schedule_action_verification(step); +} + +static void gatt_client_get_characteristic_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct get_char_data *get_char = current_data_step->set_data; + const btgatt_client_interface_t *client = data->if_gatt->client; + struct step *step = g_new0(struct step, 1); + int status; + + status = client->get_characteristic(get_char->conn_id, + get_char->service, NULL); + step->action_status = status; + + schedule_action_verification(step); +} + +static void gatt_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + struct emu_cid_data *cid_data = user_data; + const uint8_t *pdu = data; + struct pdu *gatt_pdu = queue_peek_head(t_data->pdus); + + switch (pdu[0]) { + case L2CAP_ATT_EXCHANGE_MTU_REQ: + tester_print("Exchange MTU request received."); + + if (!memcmp(exchange_mtu_req_pdu, pdu, len)) + bthost_send_cid(bthost, cid_data->handle, cid_data->cid, + exchange_mtu_resp_pdu, + sizeof(exchange_mtu_resp_pdu)); + + break; + case L2CAP_ATT_EXCHANGE_MTU_RSP: + tester_print("Exchange MTU response received."); + + break; + default: + if (!gatt_pdu || !gatt_pdu->data) { + tester_print("Unknown ATT packet."); + break; + } + + if (gatt_pdu->size != len) { + tester_print("Size of incoming frame is not valid"); + tester_print("Expected size = %d incoming size = %d", + gatt_pdu->size, len); + break; + } + + if (memcmp(gatt_pdu->data, data, len)) { + tester_print("Incoming data mismatch"); + break; + } + queue_pop_head(t_data->pdus); + gatt_pdu = queue_pop_head(t_data->pdus); + if (!gatt_pdu->data) + break; + + bthost_send_cid(bthost, cid_data->handle, cid_data->cid, + gatt_pdu->data, gatt_pdu->size); + + break; + } +} + +static void gatt_conn_cb(uint16_t handle, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + tester_print("New connection with handle 0x%04x", handle); + + if (data->hciemu_type == HCIEMU_TYPE_BREDR) { + tester_warn("Not handled device type."); + return; + } + + cid_data.cid = 0x0004; + cid_data.handle = handle; + + bthost_add_cid_hook(bthost, handle, cid_data.cid, gatt_cid_hook_cb, + &cid_data); +} + +static void gatt_client_search_services(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct step *step = g_new0(struct step, 1); + struct gatt_search_service_data *search_data; + int status; + + search_data = current_data_step->set_data; + + status = data->if_gatt->client->search_service(search_data->conn_id, + search_data->filter_uuid); + step->action_status = status; + + schedule_action_verification(step); +} + +static void init_pdus(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct step *step = g_new0(struct step, 1); + struct pdu *pdu = current_data_step->set_data; + + while (pdu->data) { + queue_push_tail(data->pdus, pdu); + pdu++; + } + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +static struct test_case test_cases[] = { + TEST_CASE_BREDRLE("Gatt Init", + ACTION_SUCCESS(dummy_action, NULL), + ), + TEST_CASE_BREDRLE("Gatt Client - Register", + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ), + TEST_CASE_BREDRLE("Gatt Client - Unregister", + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_unregister_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ), + TEST_CASE_BREDRLE("Gatt Client - Scan", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_start_scan_action, + INT_TO_PTR(CLIENT1_ID)), + CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), + ACTION_SUCCESS(gatt_client_stop_scan_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Connect", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_start_scan_action, + INT_TO_PTR(CLIENT1_ID)), + CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), + ACTION_SUCCESS(gatt_client_stop_scan_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(gatt_client_connect_action, + &client1_conn_req), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Disconnect", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_start_scan_action, + INT_TO_PTR(CLIENT1_ID)), + CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), + ACTION_SUCCESS(gatt_client_stop_scan_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(gatt_client_connect_action, + &client1_conn_req), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_disconnect_action, + &client1_conn_req), + CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Multiple Client Conn./Disc.", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_register_action, &client2_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_start_scan_action, + INT_TO_PTR(CLIENT1_ID)), + CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), + ACTION_SUCCESS(gatt_client_stop_scan_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(gatt_client_connect_action, &client1_conn_req), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_connect_action, &client2_conn_req), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN2_ID, CLIENT2_ID), + ACTION_SUCCESS(gatt_client_disconnect_action, + &client2_conn_req), + CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN2_ID, CLIENT2_ID), + ACTION_SUCCESS(gatt_client_disconnect_action, + &client1_conn_req), + CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Listen and Disconnect", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(bt_set_property_action, + &prop_test_scan_mode_conn), + CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_do_listen_action, &client1_conn_req), + CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), + ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_stop_listen_action, + &client1_conn_req), + CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_disconnect_action, + &client1_conn_req), + CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Double Listen", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(bt_set_property_action, + &prop_test_scan_mode_conn), + CALLBACK_ADAPTER_PROPS(&prop_test_scan_mode_conn, 1), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_do_listen_action, &client1_conn_req), + CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), + ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_stop_listen_action, + &client1_conn_req), + CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_disconnect_action, + &client1_conn_req), + CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + /* Close ACL on emulated remotes side so it can reconnect */ + ACTION_SUCCESS(emu_remote_disconnect_hci_action, + &cid_data.handle), + CALLBACK_STATE(CB_BT_ACL_STATE_CHANGED, + BT_ACL_STATE_DISCONNECTED), + ACTION_SUCCESS(gatt_client_do_listen_action, &client1_conn_req), + CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), + ACTION_SUCCESS(emu_remote_connect_hci_action, &bearer_type), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN2_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_disconnect_action, + &client1_conn2_req), + CALLBACK_GATTC_DISCONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN2_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_stop_listen_action, + &client1_conn_req), + CALLBACK_STATUS(CB_GATTC_LISTEN, GATT_STATUS_SUCCESS), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Search Service single", + ACTION_SUCCESS(init_pdus, search_service), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_start_scan_action, + INT_TO_PTR(CLIENT1_ID)), + CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), + ACTION_SUCCESS(gatt_client_stop_scan_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(gatt_client_connect_action, + &client1_conn_req), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_search_services, &search_services_1), + CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_1), + CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Search Service multiple", + ACTION_SUCCESS(init_pdus, search_service_2), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_start_scan_action, + INT_TO_PTR(CLIENT1_ID)), + CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), + ACTION_SUCCESS(gatt_client_stop_scan_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(gatt_client_connect_action, + &client1_conn_req), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_search_services, &search_services_1), + CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_1), + CALLBACK_GATTC_SEARCH_RESULT(CONN1_ID, &service_2), + CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Search Service none", + ACTION_SUCCESS(init_pdus, search_service_3), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_start_scan_action, + INT_TO_PTR(CLIENT1_ID)), + CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), + ACTION_SUCCESS(gatt_client_stop_scan_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(gatt_client_connect_action, + &client1_conn_req), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_search_services, &search_services_1), + CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Gatt Client - Get Characteristic 1", + ACTION_SUCCESS(init_pdus, get_characteristic_1), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb), + ACTION_SUCCESS(gatt_client_register_action, &client_app_uuid), + CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS), + ACTION_SUCCESS(gatt_client_start_scan_action, + INT_TO_PTR(CLIENT1_ID)), + CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE), + ACTION_SUCCESS(gatt_client_stop_scan_action, + INT_TO_PTR(CLIENT1_ID)), + ACTION_SUCCESS(gatt_client_connect_action, + &client1_conn_req), + CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS, + prop_emu_remotes_default_set, + CONN1_ID, CLIENT1_ID), + ACTION_SUCCESS(gatt_client_search_services, &search_services_1), + CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID), + ACTION_SUCCESS(gatt_client_get_characteristic_action, + &get_char_data_1), + CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS, + CONN1_ID, &service_1, &characteristic_1, 4), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), +}; + +struct queue *get_gatt_tests(void) +{ + uint16_t i = 0; + + list = queue_new(); + + for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) + if (!queue_push_tail(list, &test_cases[i])) + return NULL; + + return list; +} + +void remove_gatt_tests(void) +{ + queue_destroy(list, NULL); +} diff -Nru bluez-4.101/android/tester-hdp.c bluez-5.23/android/tester-hdp.c --- bluez-4.101/android/tester-hdp.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-hdp.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "emulator/bthost.h" +#include "tester-main.h" +#include "android/utils.h" + +typedef enum { + HDP_APP_SINK_RELIABLE, + HDP_APP_SINK_STREAM, + HDP_APP_SOURCE_RELIABLE, + HDP_APP_SOURCE_STREAM, +} hdp_app_reg_type; + +struct emu_cid_data { + uint16_t sdp_handle; + uint16_t sdp_cid; + uint16_t ctrl_handle; + uint16_t ctrl_cid; + uint16_t data_handle; + uint16_t data_cid; +}; + +struct emu_cid_data cid_data; + +static struct queue *list; /* List of hdp test cases */ + +static bthl_reg_param_t *create_app(hdp_app_reg_type type) +{ + bthl_reg_param_t *reg; + bthl_mdep_cfg_t mdep1, mdep2; + + reg = malloc(sizeof(bthl_reg_param_t)); + reg->application_name = "bluez-android"; + reg->provider_name = "Bluez"; + reg->srv_name = "bluez-hdp"; + reg->srv_desp = "health-device-profile"; + + mdep1.data_type = 4100; + mdep1.mdep_description = "pulse-oximeter"; + + mdep2.data_type = 4100; + mdep2.mdep_description = "pulse-oximeter"; + + switch (type) { + case HDP_APP_SINK_RELIABLE: + reg->number_of_mdeps = 1; + mdep1.mdep_role = BTHL_MDEP_ROLE_SINK; + mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE; + reg->mdep_cfg = malloc(reg->number_of_mdeps * + sizeof(bthl_mdep_cfg_t)); + reg->mdep_cfg[0] = mdep1; + break; + + case HDP_APP_SINK_STREAM: + reg->number_of_mdeps = 2; + + mdep1.mdep_role = BTHL_MDEP_ROLE_SINK; + mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE; + + mdep2.mdep_role = BTHL_MDEP_ROLE_SINK; + mdep2.channel_type = BTHL_CHANNEL_TYPE_STREAMING; + + reg->mdep_cfg = malloc(reg->number_of_mdeps * + sizeof(bthl_mdep_cfg_t)); + reg->mdep_cfg[0] = mdep1; + reg->mdep_cfg[1] = mdep2; + break; + + case HDP_APP_SOURCE_RELIABLE: + reg->number_of_mdeps = 1; + + mdep1.mdep_role = BTHL_MDEP_ROLE_SOURCE; + mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE; + + reg->mdep_cfg = malloc(reg->number_of_mdeps * + sizeof(bthl_mdep_cfg_t)); + reg->mdep_cfg[0] = mdep1; + break; + + case HDP_APP_SOURCE_STREAM: + reg->number_of_mdeps = 2; + + mdep1.mdep_role = BTHL_MDEP_ROLE_SOURCE; + mdep1.channel_type = BTHL_CHANNEL_TYPE_RELIABLE; + + mdep2.mdep_role = BTHL_MDEP_ROLE_SOURCE; + mdep2.channel_type = BTHL_CHANNEL_TYPE_STREAMING; + + reg->mdep_cfg = malloc(reg->number_of_mdeps * + sizeof(bthl_mdep_cfg_t)); + reg->mdep_cfg[0] = mdep1; + reg->mdep_cfg[1] = mdep2; + break; + } + + + return reg; +} + +static void hdp_register_sink_reliable_app_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + int app_id = 0; + bthl_reg_param_t *reg; + + reg = create_app(HDP_APP_SINK_RELIABLE); + step->action_status = data->if_hdp->register_application(reg, &app_id); + + schedule_action_verification(step); + free(reg->mdep_cfg); + free(reg); +} + +static void hdp_register_sink_stream_app_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + int app_id = 0; + bthl_reg_param_t *reg; + + reg = create_app(HDP_APP_SINK_STREAM); + step->action_status = data->if_hdp->register_application(reg, &app_id); + + schedule_action_verification(step); + free(reg->mdep_cfg); + free(reg); +} + +static void hdp_register_source_reliable_app_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + int app_id = 0; + bthl_reg_param_t *reg; + + reg = create_app(HDP_APP_SOURCE_RELIABLE); + step->action_status = data->if_hdp->register_application(reg, &app_id); + + schedule_action_verification(step); + free(reg->mdep_cfg); + free(reg); +} + +static void hdp_register_source_stream_app_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + int app_id = 0; + bthl_reg_param_t *reg; + + reg = create_app(HDP_APP_SOURCE_STREAM); + step->action_status = data->if_hdp->register_application(reg, &app_id); + + schedule_action_verification(step); + free(reg->mdep_cfg); + free(reg); +} + +static void hdp_unregister_app_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_hdp->unregister_application(1); + + schedule_action_verification(step); +} + +static uint8_t hdp_rsp_pdu[] = { 0x07, /* PDU id */ + 0x00, 0x00, /* Transaction id */ + 0x01, 0xc8, /* Response length */ + 0x01, 0xc5, /* Attributes length */ + 0x36, 0x01, 0xc2, 0x36, 0x01, 0xbf, 0x09, 0x00, 0x00, + 0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, + 0x03, 0x19, 0x14, 0x01, 0x09, 0x00, 0x04, 0x35, 0x10, + 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x10, 0x01, 0x35, + 0x06, 0x19, 0x00, 0x1e, 0x09, 0x01, 0x00, 0x09, 0x00, + 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x14, 0x00, 0x09, + 0x01, 0x01, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35, 0x0d, + 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x10, 0x03, 0x35, + 0x03, 0x19, 0x00, 0x1f, 0x09, 0x01, 0x00, 0x25, 0x03, + 0x48, 0x44, 0x50, 0x09, 0x01, 0x01, 0x25, 0x28, 0x43, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x2c, 0x20, 0x61, + 0x6e, 0x64, 0x20, 0x72, 0x65, 0x8b, 0x6c, 0x61, 0x79, + 0x20, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x20, 0x64, + 0x61, 0x74, 0x61, 0x09, 0x01, 0x02, 0x25, 0x0d, 0x42, + 0x4c, 0x55, 0x45, 0x54, 0x4f, 0x4f, 0x54, 0x48, 0x20, + 0x53, 0x49, 0x47, 0x09, 0x02, 0x00, 0x36, 0x01, 0x22, + 0x35, 0x18, 0x08, 0x01, 0x09, 0x10, 0x04, 0x08, 0x00, + 0x25, 0x0f, 0x50, 0x75, 0x6c, 0x73, 0x65, 0x20, 0x4f, + 0x78, 0x69, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, + 0x20, 0x08, 0x02, 0x09, 0x10, 0x07, 0x08, 0x00, 0x25, + 0x17, 0x42, 0x6c, 0x6f, 0x6f, 0x64, 0x20, 0x50, 0x72, + 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x20, 0x4d, 0x6f, + 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x0d, 0x35, 0x1a, 0x08, + 0x03, 0x09, 0x10, 0x08, 0x08, 0x00, 0x25, 0x11, 0x42, + 0x6f, 0x64, 0x79, 0x20, 0x54, 0x68, 0x65, 0x72, 0x6d, + 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, 0x1e, + 0x08, 0x04, 0x09, 0x10, 0x0f, 0x08, 0x00, 0x25, 0x15, + 0x42, 0x6f, 0x64, 0x79, 0x20, 0x57, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x09, + 0x09, 0x09, 0x0d, 0x35, 0x17, 0x08, 0x05, 0x09, 0x10, + 0x11, 0x08, 0x00, 0x25, 0x0e, 0x47, 0x6c, 0x75, 0x63, + 0x6f, 0x73, 0x65, 0x20, 0x4d, 0x65, 0x74, 0x65, 0x72, + 0x0d, 0x35, 0x18, 0x08, 0x06, 0x09, 0x10, 0x04, 0x08, + 0x01, 0x25, 0x0f, 0x50, 0x75, 0x6c, 0x73, 0x65, 0x20, + 0x4f, 0x78, 0x69, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, + 0x35, 0x20, 0x08, 0x07, 0x09, 0x10, 0x07, 0x08, 0x01, + 0x25, 0x17, 0x42, 0x6c, 0x6f, 0x6f, 0x64, 0x20, 0x50, + 0x72, 0x65, 0x73, 0x73, 0x75, 0x72, 0x65, 0x20, 0x4d, + 0x6f, 0x6e, 0x69, 0x74, 0x6f, 0x72, 0x0d, 0x35, 0x1a, + 0x08, 0x08, 0x09, 0x10, 0x08, 0x08, 0x01, 0x25, 0x11, + 0x42, 0x6f, 0x64, 0x79, 0x20, 0x54, 0x68, 0x65, 0x72, + 0x6d, 0x6f, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x0d, 0x35, + 0x1e, 0x08, 0x09, 0x09, 0x10, 0x0f, 0x08, 0x01, 0x25, + 0x15, 0x42, 0x6f, 0x64, 0x79, 0x20, 0x57, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x20, 0x53, 0x63, 0x61, 0x6c, 0x65, + 0x09, 0x09, 0x09, 0x0d, 0x35, 0x17, 0x08, 0x0a, 0x09, + 0x10, 0x11, 0x08, 0x01, 0x25, 0x0e, 0x47, 0x6c, 0x75, + 0x63, 0x6f, 0x73, 0x65, 0x20, 0x4d, 0x65, 0x74, 0x65, + 0x72, 0x0d, 0x09, 0x03, 0x01, 0x08, 0x01, 0x09, 0x03, + 0x02, 0x08, 0x00, + 0x00 }; + +static void hdp_sdp_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + struct emu_cid_data *cid_data = user_data; + + hdp_rsp_pdu[1] = ((uint8_t *) data)[1]; + hdp_rsp_pdu[2] = ((uint8_t *) data)[2]; + + bthost_send_cid(bthost, cid_data->sdp_handle, cid_data->sdp_cid, + hdp_rsp_pdu, sizeof(hdp_rsp_pdu)); +} + +static void hdp_sdp_search_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + cid_data.sdp_handle = handle; + cid_data.sdp_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, hdp_sdp_cid_hook_cb, + &cid_data); +} + +static void mcap_ctrl_cid_hook_cb(const void *data, uint16_t len, + void *user_data) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + uint8_t crt_rsp[5], del_rsp[4], config; + uint8_t opcode = ((uint8_t *) data)[0]; + static bool reliable = false; + + switch (opcode) { + case 0x01: /* MD_CREATE_MDL_REQ */ + crt_rsp[0] = 0x02; /* MD_CREATE_MDL_RSP */ + crt_rsp[1] = 0x00; /* Response code - Success */ + crt_rsp[2] = ((uint8_t *) data)[1]; /* mdlid */ + crt_rsp[3] = ((uint8_t *) data)[2]; + config = ((uint8_t *) data)[4]; + + if (config == 0x00) { + if (!reliable) { + crt_rsp[4] = 0x01; + reliable = true; + } else { + crt_rsp[4] = 0x02; + reliable = false; + } + } else { + crt_rsp[4] = config; + } + + bthost_send_cid(bthost, cid_data.ctrl_handle, + cid_data.ctrl_cid, + crt_rsp, sizeof(crt_rsp)); + break; + case 0x03: /* MD_RECONNECT_MDL_REQ */ + case 0x05: /* MD_ABORT_MDL_REQ */ + break; + case 0x07: /* MD_DELETE_MDL_REQ */ + del_rsp[0] = 0x08; /* MD_DELETE_MDL_RSP */ + del_rsp[1] = 0x00; /* Response code - Success */ + del_rsp[2] = ((uint8_t *) data)[1]; /* mdlid */ + del_rsp[3] = ((uint8_t *) data)[2]; + bthost_send_cid(bthost, cid_data.ctrl_handle, + cid_data.ctrl_cid, + del_rsp, sizeof(del_rsp)); + break; + } +} + +static void mcap_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + cid_data.ctrl_handle = handle; + cid_data.ctrl_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, mcap_ctrl_cid_hook_cb, + &cid_data); +} + +static void mcap_data_cid_hook_cb(const void *data, uint16_t len, + void *user_data) +{ +} + +static void mcap_data_connect_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + cid_data.data_handle = handle; + cid_data.data_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, mcap_data_cid_hook_cb, + &cid_data); +} + +/* Emulate SDP (PSM = 1) */ +static struct emu_set_l2cap_data l2cap_setup_sdp_data = { + .psm = 1, + .func = hdp_sdp_search_cb, + .user_data = NULL, +}; + +/* Emulate Control Channel (PSM = 0x1001) */ +static struct emu_set_l2cap_data l2cap_setup_cc_data = { + .psm = 0x1001, + .func = mcap_ctrl_connect_cb, + .user_data = NULL, +}; + +/* Emulate Data Channel (PSM = 0x1003) */ +static struct emu_set_l2cap_data l2cap_setup_dc_data = { + .psm = 0x1003, + .func = mcap_data_connect_cb, + .user_data = NULL, +}; + +static void hdp_connect_source_reliable_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + int app_id, channel_id, mdep_cfg_index; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + app_id = 1; + mdep_cfg_index = 0; + channel_id = 0; + step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr, + mdep_cfg_index, &channel_id); + + schedule_action_verification(step); +} + +static void hdp_destroy_source_reliable_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_hdp->destroy_channel(1); + schedule_action_verification(step); +} + +static void hdp_connect_sink_reliable_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + int app_id, channel_id, mdep_cfg_index; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + app_id = 1; + mdep_cfg_index = 0; + channel_id = 0; + step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr, + mdep_cfg_index, &channel_id); + + schedule_action_verification(step); +} + +static void hdp_connect_sink_stream_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + int app_id, channel_id, mdep_cfg_index; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + app_id = 1; + mdep_cfg_index = 1; + channel_id = 0; + step->action_status = data->if_hdp->connect_channel(app_id, &bdaddr, + mdep_cfg_index, &channel_id); + + schedule_action_verification(step); +} + +static void hdp_destroy_sink_reliable_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_hdp->destroy_channel(1); + schedule_action_verification(step); +} + +static void hdp_destroy_sink_stream_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_hdp->destroy_channel(2); + schedule_action_verification(step); +} + +static struct test_case test_cases[] = { + TEST_CASE_BREDRLE("HDP Init", + ACTION_SUCCESS(dummy_action, NULL), + ), + TEST_CASE_BREDRLE("HDP Register Sink Reliable Application", + ACTION_SUCCESS(hdp_register_sink_reliable_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ), + TEST_CASE_BREDRLE("HDP Register Sink Stream Application", + ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ), + TEST_CASE_BREDRLE("HDP Register Source Reliable Application", + ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ), + TEST_CASE_BREDRLE("HDP Register Source Stream Application", + ACTION_SUCCESS(hdp_register_source_stream_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ), + TEST_CASE_BREDRLE("HDP Unegister Application", + ACTION_SUCCESS(hdp_register_source_stream_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ACTION_SUCCESS(hdp_unregister_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_DEREG_SUCCESS), + ), + TEST_CASE_BREDRLE("HDP Connect Source Reliable Channel", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_dc_data), + ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ACTION_SUCCESS(hdp_connect_source_reliable_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_CONNECTING), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_CONNECTED), + ), + TEST_CASE_BREDRLE("HDP Destroy Source Reliable Channel", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_dc_data), + ACTION_SUCCESS(hdp_register_source_reliable_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ACTION_SUCCESS(hdp_connect_source_reliable_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_CONNECTING), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hdp_destroy_source_reliable_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_DESTROYED), + ), + TEST_CASE_BREDRLE("HDP Connect Sink Streaming Channel", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_dc_data), + ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ACTION_SUCCESS(hdp_connect_sink_reliable_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_CONNECTING), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hdp_connect_sink_stream_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1, + BTHL_CONN_STATE_CONNECTED), + ), + TEST_CASE_BREDRLE("HDP Destroy Sink Streaming Channel", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_dc_data), + ACTION_SUCCESS(hdp_register_sink_stream_app_action, NULL), + CALLBACK_HDP_APP_REG_STATE(CB_HDP_APP_REG_STATE, 1, + BTHL_APP_REG_STATE_REG_SUCCESS), + ACTION_SUCCESS(hdp_connect_sink_reliable_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_CONNECTING), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hdp_connect_sink_stream_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1, + BTHL_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hdp_destroy_sink_reliable_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 1, 0, + BTHL_CONN_STATE_DESTROYED), + ACTION_SUCCESS(hdp_destroy_sink_stream_action, NULL), + CALLBACK_HDP_CHANNEL_STATE(CB_HDP_CHANNEL_STATE, 1, 2, 1, + BTHL_CONN_STATE_DESTROYED), + ), +}; + +struct queue *get_hdp_tests(void) +{ + uint16_t i = 0; + + list = queue_new(); + + for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) + if (!queue_push_tail(list, &test_cases[i])) + return NULL; + + return list; +} + +void remove_hdp_tests(void) +{ + queue_destroy(list, NULL); +} diff -Nru bluez-4.101/android/tester-hidhost.c bluez-5.23/android/tester-hidhost.c --- bluez-4.101/android/tester-hidhost.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-hidhost.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "emulator/bthost.h" +#include "tester-main.h" + +#include "android/utils.h" + +#define HID_GET_REPORT_PROTOCOL 0x60 +#define HID_GET_BOOT_PROTOCOL 0x61 +#define HID_SET_REPORT_PROTOCOL 0x70 +#define HID_SET_BOOT_PROTOCOL 0x71 + +#define HID_SET_INPUT_REPORT 0x51 +#define HID_SET_OUTPUT_REPORT 0x52 +#define HID_SET_FEATURE_REPORT 0x53 + +#define HID_SEND_DATA 0xa2 + +#define HID_GET_INPUT_REPORT 0x49 +#define HID_GET_OUTPUT_REPORT 0x4a +#define HID_GET_FEATURE_REPORT 0x4b + +#define HID_MODE_DEFAULT 0x00 +#define HID_MODE_BREDR 0x01 +#define HID_MODE_LE 0x02 + +#define HID_EXPECTED_REPORT_SIZE 0x02 + +static struct queue *list; /* List of hidhost test cases */ + +struct emu_cid_data { + const int pdu_len; + const void *pdu; + + uint16_t sdp_handle; + uint16_t sdp_cid; + uint16_t ctrl_handle; + uint16_t ctrl_cid; + uint16_t intr_handle; + uint16_t intr_cid; +}; + +static struct emu_cid_data cid_data; + +static const uint8_t did_req_pdu[] = { 0x06, /* PDU id */ + 0x00, 0x00, /* Transaction id */ + 0x00, 0x0f, /* Req length */ + 0x35, 0x03, /* Attributes length */ + 0x19, 0x12, 0x00, 0xff, 0xff, 0x35, 0x05, 0x0a, 0x00, + 0x00, 0xff, 0xff, 0x00 }; /* no continuation */ + +static const uint8_t did_rsp_pdu[] = { 0x07, /* PDU id */ + 0x00, 0x00, /* Transaction id */ + 0x00, 0x4f, /* Response length */ + 0x00, 0x4c, /* Attributes length */ + 0x35, 0x4a, 0x35, 0x48, 0x09, 0x00, 0x00, 0x0a, 0x00, + 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, + 0x12, 0x00, 0x09, 0x00, 0x05, 0x35, 0x03, 0x19, 0x10, + 0x02, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, + 0x12, 0x00, 0x09, 0x01, 0x03, 0x09, 0x02, 0x00, 0x09, + 0x01, 0x03, 0x09, 0x02, 0x01, 0x09, 0x1d, 0x6b, 0x09, + 0x02, 0x02, 0x09, 0x02, 0x46, 0x09, 0x02, 0x03, 0x09, + 0x05, 0x0e, 0x09, 0x02, 0x04, 0x28, 0x01, 0x09, 0x02, + 0x05, 0x09, 0x00, 0x02, + 0x00 }; /* no continuation */ + +static const uint8_t hid_rsp_pdu[] = { 0x07, /* PDU id */ + 0x00, 0x01, /* Transaction id */ + 0x01, 0x71, /* Response length */ + 0x01, 0x6E, /* Attributes length */ + 0x36, 0x01, 0x6b, 0x36, 0x01, 0x68, 0x09, 0x00, 0x00, + 0x0a, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, + 0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d, + 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35, + 0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x05, 0x35, 0x03, + 0x19, 0x10, 0x02, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, + 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09, + 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x24, + 0x09, 0x01, 0x00, 0x09, 0x00, 0x0d, 0x35, 0x0f, 0x35, + 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x13, + 0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, 0x25, + 0x1e, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, 0x68, + 0x20, 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, + 0x68, 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x20, 0x4d, + 0x35, 0x35, 0x35, 0x62, 0x09, 0x01, 0x01, 0x25, 0x0f, + 0x42, 0x6c, 0x75, 0x65, 0x74, 0x6f, 0x6f, 0x74, 0x68, + 0x20, 0x4d, 0x6f, 0x75, 0x73, 0x65, 0x09, 0x01, 0x02, + 0x25, 0x08, 0x4c, 0x6f, 0x67, 0x69, 0x74, 0x65, 0x63, + 0x68, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02, + 0x01, 0x09, 0x01, 0x11, 0x09, 0x02, 0x02, 0x08, 0x80, + 0x09, 0x02, 0x03, 0x08, 0x21, 0x09, 0x02, 0x04, 0x28, + 0x01, 0x09, 0x02, 0x05, 0x28, 0x01, 0x09, 0x02, 0x06, + 0x35, 0x74, 0x35, 0x72, 0x08, 0x22, 0x25, 0x6e, 0x05, + 0x01, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x02, 0x09, 0x01, + 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15, + 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x16, 0x01, 0xf8, + 0x26, 0xff, 0x07, 0x75, 0x0c, 0x95, 0x02, 0x81, 0x06, + 0x09, 0x38, 0x15, 0x81, 0x25, 0x7f, 0x75, 0x08, 0x95, + 0x01, 0x81, 0x06, 0x05, 0x0c, 0x0a, 0x38, 0x02, 0x81, + 0x06, 0x05, 0x09, 0x19, 0x09, 0x29, 0x10, 0x15, 0x00, + 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0xc0, + 0xc0, 0x06, 0x00, 0xff, 0x09, 0x01, 0xa1, 0x01, 0x85, + 0x10, 0x75, 0x08, 0x95, 0x06, 0x15, 0x00, 0x26, 0xff, + 0x00, 0x09, 0x01, 0x81, 0x00, 0x09, 0x01, 0x91, 0x00, + 0xc0, 0x09, 0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09, + 0x04, 0x09, 0x09, 0x01, 0x00, 0x09, 0x02, 0x08, 0x28, + 0x00, 0x09, 0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a, + 0x28, 0x01, 0x09, 0x02, 0x0b, 0x09, 0x01, 0x00, 0x09, + 0x02, 0x0c, 0x09, 0x0c, 0x80, 0x09, 0x02, 0x0d, 0x28, + 0x00, 0x09, 0x02, 0x0e, 0x28, 0x01, + 0x00 }; /* no continuation */ + +static void hid_sdp_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + struct emu_cid_data *cid_data = user_data; + + if (!memcmp(did_req_pdu, data, len)) { + bthost_send_cid(bthost, cid_data->sdp_handle, cid_data->sdp_cid, + did_rsp_pdu, sizeof(did_rsp_pdu)); + return; + } + + bthost_send_cid(bthost, cid_data->sdp_handle, cid_data->sdp_cid, + hid_rsp_pdu, sizeof(hid_rsp_pdu)); +} +static void hid_sdp_search_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + cid_data.sdp_handle = handle; + cid_data.sdp_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, hid_sdp_cid_hook_cb, + &cid_data); +} + +static void hid_prepare_reply_protocol_mode(struct emu_cid_data *cid_data) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + uint8_t pdu[2] = { 0, 0 }; + uint16_t pdu_len = 0; + + pdu_len = 2; + pdu[0] = 0xa0; + pdu[1] = 0x00; + + bthost_send_cid(bthost, cid_data->ctrl_handle, cid_data->ctrl_cid, + (void *)pdu, pdu_len); +} + +static void hid_prepare_reply_report(struct emu_cid_data *cid_data) +{ + struct test_data *t_data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + uint8_t pdu[3] = { 0, 0, 0 }; + uint16_t pdu_len = 0; + + pdu_len = 3; + pdu[0] = 0xa2; + pdu[1] = 0x01; + pdu[2] = 0x00; + + bthost_send_cid(bthost, cid_data->ctrl_handle, cid_data->ctrl_cid, + (void *)pdu, pdu_len); +} + +static void hid_ctrl_cid_hook_cb(const void *data, uint16_t len, + void *user_data) +{ + struct emu_cid_data *cid_data = user_data; + uint8_t header = ((uint8_t *) data)[0]; + struct step *step; + + switch (header) { + case HID_GET_REPORT_PROTOCOL: + case HID_GET_BOOT_PROTOCOL: + case HID_SET_REPORT_PROTOCOL: + case HID_SET_BOOT_PROTOCOL: + hid_prepare_reply_protocol_mode(cid_data); + break; + case HID_GET_INPUT_REPORT: + case HID_GET_OUTPUT_REPORT: + case HID_GET_FEATURE_REPORT: + hid_prepare_reply_report(cid_data); + break; + /* + * HID device doesnot reply for this commads, so reaching pdu's + * to hid device means assuming test passed + */ + case HID_SET_INPUT_REPORT: + case HID_SET_OUTPUT_REPORT: + case HID_SET_FEATURE_REPORT: + case HID_SEND_DATA: + /* Successfully verify sending data step */ + step = g_new0(struct step, 1); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); + break; + } +} +static void hid_ctrl_connect_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + cid_data.ctrl_handle = handle; + cid_data.ctrl_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, hid_ctrl_cid_hook_cb, + &cid_data); +} + +static void hid_intr_cid_hook_cb(const void *data, uint16_t len, + void *user_data) +{ + uint8_t header = ((uint8_t *) data)[0]; + struct step *step; + + switch (header) { + case HID_SEND_DATA: + /* Successfully verify sending data step */ + step = g_new0(struct step, 1); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); + break; + } +} +static void hid_intr_connect_cb(uint16_t handle, uint16_t cid, void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + cid_data.intr_handle = handle; + cid_data.intr_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, hid_intr_cid_hook_cb, + &cid_data); +} + +/* Emulate SDP (PSM = 1) */ +static struct emu_set_l2cap_data l2cap_setup_sdp_data = { + .psm = 1, + .func = hid_sdp_search_cb, + .user_data = NULL, +}; + +/* Emulate Control Channel (PSM = 17) */ +static struct emu_set_l2cap_data l2cap_setup_cc_data = { + .psm = 17, + .func = hid_ctrl_connect_cb, + .user_data = NULL, +}; + +/* Emulate Interrupt Channel (PSM = 19) */ +static struct emu_set_l2cap_data l2cap_setup_ic_data = { + .psm = 19, + .func = hid_intr_connect_cb, + .user_data = NULL, +}; + +static void hidhost_connect_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + + step->action_status = data->if_hid->connect(&bdaddr); + + schedule_action_verification(step); +} + +static void hidhost_disconnect_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + + step->action_status = data->if_hid->disconnect(&bdaddr); + + schedule_action_verification(step); +} + +static void hidhost_virtual_unplug_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + + step->action_status = data->if_hid->virtual_unplug(&bdaddr); + + schedule_action_verification(step); +} + +static void hidhost_get_protocol_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + + step->action_status = data->if_hid->get_protocol(&bdaddr, + BTHH_REPORT_MODE); + + schedule_action_verification(step); +} + +static void hidhost_set_protocol_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + + step->action_status = data->if_hid->set_protocol(&bdaddr, + BTHH_REPORT_MODE); + + schedule_action_verification(step); +} + +static void hidhost_get_report_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + + step->action_status = data->if_hid->get_report(&bdaddr, + BTHH_INPUT_REPORT, 1, + 20); + + schedule_action_verification(step); +} + +static void hidhost_set_report_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + char *buf = "fe0201"; + bt_bdaddr_t bdaddr; + int status; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + + /* Successfull result should be verified on the other end (hook) */ + status = data->if_hid->send_data(&bdaddr, buf); + if (status) { + struct step *step = g_new0(struct step, 1); + + step->action_status = status; + schedule_action_verification(step); + } +} + +static void hidhost_send_data_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *hid_addr = hciemu_get_client_bdaddr(data->hciemu); + char *buf = "010101"; + bt_bdaddr_t bdaddr; + int status; + + bdaddr2android((const bdaddr_t *) hid_addr, &bdaddr); + + /* Successfull result should be verified on the other end (hook) */ + status = data->if_hid->set_report(&bdaddr, BTHH_INPUT_REPORT, buf); + if (status) { + struct step *step = g_new0(struct step, 1); + + step->action_status = status; + schedule_action_verification(step); + } +} + +static struct test_case test_cases[] = { + TEST_CASE_BREDRLE("HidHost Init", + ACTION_SUCCESS(dummy_action, NULL), + ), + TEST_CASE_BREDRLE("HidHost Connect Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_ic_data), + ACTION_SUCCESS(hidhost_connect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTED), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_DISCONNECTED), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("HidHost Disconnect Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_ic_data), + ACTION_SUCCESS(hidhost_connect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hidhost_disconnect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_DISCONNECTED), + ), + TEST_CASE_BREDRLE("HidHost VirtualUnplug Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_ic_data), + ACTION_SUCCESS(hidhost_connect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hidhost_virtual_unplug_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_DISCONNECTED), + ), + TEST_CASE_BREDRLE("HidHost GetProtocol Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_ic_data), + ACTION_SUCCESS(hidhost_connect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hidhost_get_protocol_action, NULL), + CALLBACK_HH_MODE(CB_HH_PROTOCOL_MODE, BTHH_OK, HID_MODE_BREDR), + ), + TEST_CASE_BREDRLE("HidHost SetProtocol Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_ic_data), + ACTION_SUCCESS(hidhost_connect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hidhost_set_protocol_action, NULL), + CALLBACK_HH_MODE(CB_HH_PROTOCOL_MODE, BTHH_OK, HID_MODE_BREDR), + ), + TEST_CASE_BREDRLE("HidHost GetReport Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_ic_data), + ACTION_SUCCESS(hidhost_connect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hidhost_get_report_action, NULL), + CALLBACK_HHREPORT(CB_HH_GET_REPORT, BTHH_OK, + HID_EXPECTED_REPORT_SIZE), + ), + TEST_CASE_BREDRLE("HidHost SetReport Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_ic_data), + ACTION_SUCCESS(hidhost_connect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTING), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hidhost_set_report_action, NULL), + ), + TEST_CASE_BREDRLE("HidHost SendData Success", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_sdp_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_cc_data), + ACTION_SUCCESS(emu_add_l2cap_server_action, + &l2cap_setup_ic_data), + ACTION_SUCCESS(hidhost_connect_action, NULL), + CALLBACK_STATE(CB_HH_CONNECTION_STATE, + BTHH_CONN_STATE_CONNECTED), + ACTION_SUCCESS(hidhost_send_data_action, NULL), + ), +}; + +struct queue *get_hidhost_tests(void) +{ + uint16_t i = 0; + + list = queue_new(); + + for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) + if (!queue_push_tail(list, &test_cases[i])) + return NULL; + + return list; +} + +void remove_hidhost_tests(void) +{ + queue_destroy(list, NULL); +} diff -Nru bluez-4.101/android/tester-main.c bluez-5.23/android/tester-main.c --- bluez-4.101/android/tester-main.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-main.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,2279 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include + +#include "emulator/bthost.h" +#include "tester-main.h" + +#include "monitor/bt.h" + +static char exec_dir[PATH_MAX + 1]; + +static gint scheduled_cbacks_num; + +#define EMULATOR_SIGNAL_TIMEOUT 2 /* in seconds */ +#define EMULATOR_SIGNAL "emulator_started" + +static gboolean check_callbacks_called(gpointer user_data) +{ + /* + * Wait for all callbacks scheduled in current test context to execute + * in main loop. This will avoid late callback calls after test case has + * already failed or timed out. + */ + + if (g_atomic_int_get(&scheduled_cbacks_num) == 0) { + tester_teardown_complete(); + return FALSE; + } else if (scheduled_cbacks_num < 0) { + tester_warn("Unscheduled callback called!"); + return FALSE; + } + + return TRUE; +} + +static void check_daemon_term(void) +{ + int status; + pid_t pid; + struct test_data *data = tester_get_data(); + + if (!data) + return; + + pid = waitpid(data->bluetoothd_pid, &status, WNOHANG); + if (pid != data->bluetoothd_pid) + return; + + data->bluetoothd_pid = 0; + + if (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS)) { + g_idle_add(check_callbacks_called, NULL); + return; + } + + tester_warn("Unexpected Daemon shutdown with status %d", status); +} + +static gboolean signal_handler(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct signalfd_siginfo si; + ssize_t result; + int fd; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) + return FALSE; + + fd = g_io_channel_unix_get_fd(channel); + + result = read(fd, &si, sizeof(si)); + if (result != sizeof(si)) + return FALSE; + + switch (si.ssi_signo) { + case SIGCHLD: + check_daemon_term(); + break; + } + + return TRUE; +} + +static guint setup_signalfd(void) +{ + GIOChannel *channel; + guint source; + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) + return 0; + + fd = signalfd(-1, &mask, 0); + if (fd < 0) + return 0; + + channel = g_io_channel_unix_new(fd); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +static void test_post_teardown(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + hciemu_unref(data->hciemu); + data->hciemu = NULL; + + g_source_remove(data->signalfd); + data->signalfd = 0; +} + +static void bluetoothd_start(int hci_index) +{ + char prg_name[PATH_MAX + 1]; + char index[8]; + char *prg_argv[5]; + + snprintf(prg_name, sizeof(prg_name), "%s/%s", exec_dir, "bluetoothd"); + snprintf(index, sizeof(index), "%d", hci_index); + + prg_argv[0] = prg_name; + prg_argv[1] = "-i"; + prg_argv[2] = index; + prg_argv[3] = "-d"; + prg_argv[4] = NULL; + + if (!tester_use_debug()) + fclose(stderr); + + execve(prg_argv[0], prg_argv, NULL); +} + +static void emulator(int pipe, int hci_index) +{ + static const char SYSTEM_SOCKET_PATH[] = "\0android_system"; + char buf[1024]; + struct sockaddr_un addr; + struct timeval tv; + int fd; + ssize_t len; + + fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) + goto failed; + + tv.tv_sec = EMULATOR_SIGNAL_TIMEOUT; + tv.tv_usec = 0; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, SYSTEM_SOCKET_PATH, sizeof(SYSTEM_SOCKET_PATH)); + + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Failed to bind system socket"); + goto failed; + } + + len = write(pipe, EMULATOR_SIGNAL, sizeof(EMULATOR_SIGNAL)); + if (len != sizeof(EMULATOR_SIGNAL)) + goto failed; + + memset(buf, 0, sizeof(buf)); + + len = read(fd, buf, sizeof(buf)); + if (len <= 0 || strcmp(buf, "bluetooth.start=daemon")) + goto failed; + + close(pipe); + close(fd); + return bluetoothd_start(hci_index); + +failed: + close(pipe); + + if (fd >= 0) + close(fd); +} + +static void mgmt_debug(const char *str, void *user_data) +{ + const char *prefix = user_data; + + tester_print("%s%s", prefix, str); +} + +static void read_info_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + const struct mgmt_rp_read_info *rp = param; + char addr[18]; + uint16_t manufacturer; + uint32_t supported_settings, current_settings; + + tester_print("Read Info callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + ba2str(&rp->bdaddr, addr); + manufacturer = btohs(rp->manufacturer); + supported_settings = btohl(rp->supported_settings); + current_settings = btohl(rp->current_settings); + + tester_print(" Address: %s", addr); + tester_print(" Version: 0x%02x", rp->version); + tester_print(" Manufacturer: 0x%04x", manufacturer); + tester_print(" Supported settings: 0x%08x", supported_settings); + tester_print(" Current settings: 0x%08x", current_settings); + tester_print(" Class: 0x%02x%02x%02x", + rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]); + tester_print(" Name: %s", rp->name); + tester_print(" Short name: %s", rp->short_name); + + if (strcmp(hciemu_get_address(data->hciemu), addr)) { + tester_pre_setup_failed(); + return; + } + + tester_pre_setup_complete(); +} + +static void index_added_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Added callback"); + tester_print(" Index: 0x%04x", index); + + data->mgmt_index = index; + + mgmt_send(data->mgmt, MGMT_OP_READ_INFO, data->mgmt_index, 0, NULL, + read_info_callback, NULL, NULL); +} + +static void index_removed_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Index Removed callback"); + tester_print(" Index: 0x%04x", index); + + if (index != data->mgmt_index) + return; + + mgmt_unregister_index(data->mgmt, data->mgmt_index); + + mgmt_unref(data->mgmt); + data->mgmt = NULL; + + tester_post_teardown_complete(); +} + +static void read_index_list_callback(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct test_data *data = tester_get_data(); + + tester_print("Read Index List callback"); + tester_print(" Status: 0x%02x", status); + + if (status || !param) { + tester_pre_setup_failed(); + return; + } + + mgmt_register(data->mgmt, MGMT_EV_INDEX_ADDED, MGMT_INDEX_NONE, + index_added_callback, NULL, NULL); + + mgmt_register(data->mgmt, MGMT_EV_INDEX_REMOVED, MGMT_INDEX_NONE, + index_removed_callback, NULL, NULL); + + data->hciemu = hciemu_new(data->hciemu_type); + if (!data->hciemu) { + tester_warn("Failed to setup HCI emulation"); + tester_pre_setup_failed(); + return; + } + + tester_print("New hciemu instance created"); +} + +static void test_pre_setup(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + data->signalfd = setup_signalfd(); + if (!data->signalfd) { + tester_warn("Failed to setup signalfd"); + tester_pre_setup_failed(); + return; + } + + data->mgmt = mgmt_new_default(); + if (!data->mgmt) { + tester_warn("Failed to setup management interface"); + tester_pre_setup_failed(); + return; + } + + if (!tester_use_debug()) + fclose(stderr); + else + mgmt_set_debug(data->mgmt, mgmt_debug, "mgmt: ", NULL); + + mgmt_send(data->mgmt, MGMT_OP_READ_INDEX_LIST, MGMT_INDEX_NONE, 0, + NULL, read_index_list_callback, NULL, NULL); +} + +static bool match_property(bt_property_t *exp_prop, bt_property_t *rec_prop, + int prop_num) +{ + if (exp_prop->type && (exp_prop->type != rec_prop->type)) + return 0; + + if (exp_prop->len && (exp_prop->len != rec_prop->len)) { + tester_debug("Property [%d] len don't match! received=%d, " + "expected=%d", prop_num, rec_prop->len, + exp_prop->len); + return 0; + } + + if (exp_prop->val && memcmp(exp_prop->val, rec_prop->val, + exp_prop->len)) { + tester_debug("Property [%d] value don't match!", prop_num); + return 0; + } + + return 1; +} + +static int verify_property(bt_property_t *exp_props, int exp_num_props, + bt_property_t *rec_props, int rec_num_props) +{ + int i, j; + int exp_prop_to_find = exp_num_props; + + /* Get first exp prop to match and search for it */ + for (i = 0; i < exp_num_props; i++) { + for (j = 0; j < rec_num_props; j++) { + if (match_property(&exp_props[i], &rec_props[j], i)) { + exp_prop_to_find--; + break; + } + } + } + + if ((i == 0) && exp_props) { + tester_warn("No property was verified: %s", exp_num_props ? + "unknown error!" : + "wrong \'.callback_result.num_properties\'?"); + + return 1; + } + + return exp_prop_to_find; +} + +/* + * Check each test case step if test case expected + * data is set and match it with expected result. + */ + +static bool verify_gatt_ids(btgatt_gatt_id_t *a, btgatt_gatt_id_t *b) +{ + + if (memcmp(&a->uuid, &b->uuid, sizeof(bt_uuid_t))) + return false; + + return true; +} + +static bool verify_services(btgatt_srvc_id_t *a, btgatt_srvc_id_t *b) +{ + if (a->is_primary != b->is_primary) + return false; + + return verify_gatt_ids(&a->id, &b->id); +} + +static bool match_data(struct step *step) +{ + struct test_data *data = tester_get_data(); + const struct step *exp; + + exp = queue_peek_head(data->steps); + + if (!exp) { + /* Can occure while test passed already */ + tester_debug("Cannot get step to match"); + return false; + } + + if (exp->action_status != step->action_status) { + tester_debug("Action status don't match"); + return false; + } + + if (exp->callback || step->callback) { + if (exp->callback != step->callback) { + tester_debug("Callback type don't match"); + return false; + } + + if (exp->callback_result.state != + step->callback_result.state) { + tester_debug("Callback state don't match"); + return false; + } + + if (exp->callback_result.status != + step->callback_result.status) { + tester_debug("Callback status don't match"); + return false; + } + + if (exp->callback_result.mode != + step->callback_result.mode) { + tester_debug("Callback mode don't match"); + return false; + } + + if (exp->callback_result.report_size != + step->callback_result.report_size) { + tester_debug("Callback report size don't match"); + return false; + } + + if (exp->callback_result.ctrl_state != + step->callback_result.ctrl_state) { + tester_debug("Callback ctrl state don't match"); + return false; + } + + if (exp->callback_result.conn_state != + step->callback_result.conn_state) { + tester_debug("Callback connection state don't match"); + return false; + } + + if (exp->callback_result.local_role != + step->callback_result.local_role) { + tester_debug("Callback local_role don't match"); + return false; + } + + if (exp->callback_result.remote_role != + step->callback_result.remote_role) { + tester_debug("Callback remote_role don't match"); + return false; + } + + if (exp->callback_result.app_id != + step->callback_result.app_id) { + tester_debug("Callback app_id don't match"); + return false; + } + + if (exp->callback_result.channel_id != + step->callback_result.channel_id) { + tester_debug("Callback channel_id don't match"); + return false; + } + + if (exp->callback_result.mdep_cfg_index != + step->callback_result.mdep_cfg_index) { + tester_debug("Callback mdep_cfg_index don't match"); + return false; + } + + if (exp->callback_result.app_state != + step->callback_result.app_state) { + tester_debug("Callback app_state don't match"); + return false; + } + + if (exp->callback_result.channel_state != + step->callback_result.channel_state) { + tester_debug("Callback channel_state don't match"); + return false; + } + + if (exp->callback_result.pairing_variant != + step->callback_result.pairing_variant) { + tester_debug("Callback pairing result don't match"); + return false; + } + + if (exp->callback_result.adv_data != + step->callback_result.adv_data) { + tester_debug("Callback adv. data status don't match"); + return false; + } + + if (exp->callback_result.conn_id != + step->callback_result.conn_id) { + tester_debug("Callback conn_id don't match"); + return false; + } + + if (exp->callback_result.client_id != + step->callback_result.client_id) { + tester_debug("Callback client_id don't match"); + return false; + } + + if (exp->callback_result.properties && + verify_property(exp->callback_result.properties, + exp->callback_result.num_properties, + step->callback_result.properties, + step->callback_result.num_properties)) { + tester_debug("Gatt properties don't match"); + return false; + } + + if (exp->callback_result.service && + !verify_services(step->callback_result.service, + exp->callback_result.service)) { + tester_debug("Gatt service doesn't match"); + return false; + } + + if (exp->callback_result.characteristic) { + btgatt_gatt_id_t *a; + btgatt_gatt_id_t *b; + a = step->callback_result.characteristic; + b = exp->callback_result.characteristic; + + if (!verify_gatt_ids(a, b)) { + tester_debug("Gatt char doesn't match"); + return false; + } + } + + if (exp->callback_result.char_prop != + step->callback_result.char_prop) { + tester_debug("Gatt char prop doesn't match"); + return false; + } + } + + return true; +} + +static void init_test_steps(struct test_data *data) +{ + const struct test_case *test_steps = data->test_data; + int i = 0; + + for (i = 0; i < test_steps->step_num; i++) + queue_push_tail(data->steps, (void *) &(test_steps->step[i])); + + tester_print("tester: Number of test steps=%d", + queue_length(data->steps)); +} + +/* + * Each test case step should be verified, if match with + * expected result tester should go to next test step. + */ +static void verify_step(struct step *step, queue_destroy_func_t cleanup_cb) +{ + struct test_data *data = tester_get_data(); + const struct test_case *test_steps = data->test_data; + struct step *next_step; + + tester_debug("tester: STEP[%d] check", + test_steps->step_num-queue_length(data->steps) + 1); + + if (step && !match_data(step)) { + if (cleanup_cb) + cleanup_cb(step); + + return; + } + + queue_pop_head(data->steps); + + if (cleanup_cb) + cleanup_cb(step); + + tester_debug("tester: STEP[%d] pass", + test_steps->step_num-queue_length(data->steps)); + + if (queue_isempty(data->steps)) { + tester_print("tester: All steps done, passing"); + tester_test_passed(); + + return; + } + + /* goto next step action if declared in step */ + next_step = queue_peek_head(data->steps); + + if (next_step->action) + next_step->action(); +} + +/* + * NOTICE: + * Its mandatory for callback to set proper step.callback value so that + * step verification could pass and move to next test step + */ + +static void free_properties(struct step *step) +{ + bt_property_t *properties = step->callback_result.properties; + int num_properties = step->callback_result.num_properties; + int i; + + for (i = 0; i < num_properties; i++) + g_free(properties[i].val); + + g_free(properties); +} + +static void destroy_callback_step(void *data) +{ + struct step *step = data; + + if (step->callback_result.properties) + free_properties(step); + + if (step->callback_result.service) + free(step->callback_result.service); + + if (step->callback_result.characteristic) + free(step->callback_result.characteristic); + + g_free(step); + g_atomic_int_dec_and_test(&scheduled_cbacks_num); +} + +static gboolean verify_action(gpointer user_data) +{ + struct step *step = user_data; + + verify_step(step, g_free); + + return FALSE; +} + +static gboolean verify_callback(gpointer user_data) +{ + struct test_data *data = tester_get_data(); + struct step *step = user_data; + + /* Return if callback came when all steps are already verified */ + if (queue_isempty(data->steps)) { + destroy_callback_step(step); + return FALSE; + } + + /* + * TODO: This may call action from next step before callback data + * from previous step was freed. + */ + verify_step(step, destroy_callback_step); + + return FALSE; +} + +static void schedule_callback_call(struct step *step) +{ + g_atomic_int_inc(&scheduled_cbacks_num); + g_idle_add(verify_callback, step); +} + +void schedule_action_verification(struct step *step) +{ + g_idle_add_full(G_PRIORITY_HIGH_IDLE, verify_action, step, NULL); +} + +static void adapter_state_changed_cb(bt_state_t state) +{ + struct step *step = g_new0(struct step, 1); + + step->callback_result.state = state; + step->callback = CB_BT_ADAPTER_STATE_CHANGED; + + schedule_callback_call(step); +} + +static bt_property_t *copy_properties(int num_properties, + bt_property_t *properties) +{ + int i; + bt_property_t *props = g_new0(bt_property_t, num_properties); + + for (i = 0; i < num_properties; i++) { + props[i].type = properties[i].type; + props[i].len = properties[i].len; + props[i].val = g_memdup(properties[i].val, properties[i].len); + } + + return props; +} + +static bt_property_t *repack_properties(int num_properties, + bt_property_t **properties) +{ + int i; + bt_property_t *props = g_new0(bt_property_t, num_properties); + + for (i = 0; i < num_properties; i++) { + props[i].type = properties[i]->type; + props[i].len = properties[i]->len; + props[i].val = g_memdup(properties[i]->val, properties[i]->len); + } + + return props; +} + +static bt_property_t *create_property(bt_property_type_t type, void *val, + int len) +{ + bt_property_t *prop = g_new0(bt_property_t, 1); + + prop->type = type; + prop->len = len; + prop->val = g_memdup(val, len); + + return prop; +} + +static void adapter_properties_cb(bt_status_t status, int num_properties, + bt_property_t *properties) +{ + struct step *step = g_new0(struct step, 1); + + step->callback_result.status = status; + step->callback_result.num_properties = num_properties; + step->callback_result.properties = copy_properties(num_properties, + properties); + step->callback = CB_BT_ADAPTER_PROPERTIES; + + schedule_callback_call(step); +} + +static void discovery_state_changed_cb(bt_discovery_state_t state) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_BT_DISCOVERY_STATE_CHANGED; + step->callback_result.state = state; + + schedule_callback_call(step); +} + +static void device_found_cb(int num_properties, bt_property_t *properties) +{ + struct step *step = g_new0(struct step, 1); + + step->callback_result.num_properties = num_properties; + step->callback_result.properties = copy_properties(num_properties, + properties); + + step->callback = CB_BT_DEVICE_FOUND; + + schedule_callback_call(step); +} + +static void remote_device_properties_cb(bt_status_t status, + bt_bdaddr_t *bd_addr, int num_properties, + bt_property_t *properties) +{ + struct step *step = g_new0(struct step, 1); + + step->callback_result.num_properties = num_properties; + step->callback_result.properties = copy_properties(num_properties, + properties); + + step->callback = CB_BT_REMOTE_DEVICE_PROPERTIES; + + schedule_callback_call(step); +} + +static void bond_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, + bt_bond_state_t state) +{ + struct step *step = g_new0(struct step, 1); + + step->callback_result.status = status; + step->callback_result.state = state; + + /* Utilize property verification mechanism for bdaddr */ + step->callback_result.num_properties = 1; + step->callback_result.properties = create_property(BT_PROPERTY_BDADDR, + remote_bd_addr, + sizeof(*remote_bd_addr)); + + step->callback = CB_BT_BOND_STATE_CHANGED; + + schedule_callback_call(step); +} + +static void pin_request_cb(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod) +{ + struct step *step = g_new0(struct step, 1); + bt_property_t *props[3]; + + step->callback = CB_BT_PIN_REQUEST; + + /* Utilize property verification mechanism for those */ + props[0] = create_property(BT_PROPERTY_BDADDR, remote_bd_addr, + sizeof(*remote_bd_addr)); + props[1] = create_property(BT_PROPERTY_BDNAME, bd_name->name, + strlen((char *) bd_name->name)); + props[2] = create_property(BT_PROPERTY_CLASS_OF_DEVICE, &cod, + sizeof(cod)); + + step->callback_result.num_properties = 3; + step->callback_result.properties = repack_properties(3, props); + + g_free(props[0]->val); + g_free(props[0]); + g_free(props[1]->val); + g_free(props[1]); + g_free(props[2]->val); + g_free(props[2]); + + schedule_callback_call(step); +} + +static void ssp_request_cb(bt_bdaddr_t *remote_bd_addr, + bt_bdname_t *bd_name, uint32_t cod, + bt_ssp_variant_t pairing_variant, + uint32_t pass_key) +{ + struct step *step = g_new0(struct step, 1); + bt_property_t *props[3]; + + step->callback = CB_BT_SSP_REQUEST; + + /* Utilize property verification mechanism for those */ + props[0] = create_property(BT_PROPERTY_BDADDR, remote_bd_addr, + sizeof(*remote_bd_addr)); + props[1] = create_property(BT_PROPERTY_BDNAME, bd_name->name, + strlen((char *) bd_name->name)); + props[2] = create_property(BT_PROPERTY_CLASS_OF_DEVICE, &cod, + sizeof(cod)); + + step->callback_result.num_properties = 3; + step->callback_result.properties = repack_properties(3, props); + + g_free(props[0]->val); + g_free(props[0]); + g_free(props[1]->val); + g_free(props[1]); + g_free(props[2]->val); + g_free(props[2]); + + schedule_callback_call(step); +} + +static void acl_state_changed_cb(bt_status_t status, + bt_bdaddr_t *remote_bd_addr, + bt_acl_state_t state) { + struct step *step = g_new0(struct step, 1); + + step->callback = CB_BT_ACL_STATE_CHANGED; + + step->callback_result.status = status; + step->callback_result.state = state; + + schedule_callback_call(step); +} + +static bt_callbacks_t bt_callbacks = { + .size = sizeof(bt_callbacks), + .adapter_state_changed_cb = adapter_state_changed_cb, + .adapter_properties_cb = adapter_properties_cb, + .remote_device_properties_cb = remote_device_properties_cb, + .device_found_cb = device_found_cb, + .discovery_state_changed_cb = discovery_state_changed_cb, + .pin_request_cb = pin_request_cb, + .ssp_request_cb = ssp_request_cb, + .bond_state_changed_cb = bond_state_changed_cb, + .acl_state_changed_cb = acl_state_changed_cb, + .thread_evt_cb = NULL, + .dut_mode_recv_cb = NULL, + .le_test_mode_cb = NULL +}; + +static void hidhost_connection_state_cb(bt_bdaddr_t *bd_addr, + bthh_connection_state_t state) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_HH_CONNECTION_STATE; + step->callback_result.state = state; + + schedule_callback_call(step); +} + +static void hidhost_virual_unplug_cb(bt_bdaddr_t *bd_addr, bthh_status_t status) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_HH_VIRTUAL_UNPLUG; + step->callback_result.status = status; + + schedule_callback_call(step); +} + +static void hidhost_protocol_mode_cb(bt_bdaddr_t *bd_addr, + bthh_status_t status, + bthh_protocol_mode_t mode) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_HH_PROTOCOL_MODE; + step->callback_result.status = status; + step->callback_result.mode = mode; + + /* TODO: add bdaddr to verify? */ + + schedule_callback_call(step); +} + +static void hidhost_hid_info_cb(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_HH_HID_INFO; + + schedule_callback_call(step); +} + +static void hidhost_get_report_cb(bt_bdaddr_t *bd_addr, bthh_status_t status, + uint8_t *report, int size) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_HH_GET_REPORT; + + step->callback_result.status = status; + step->callback_result.report_size = size; + + schedule_callback_call(step); +} + +static bthh_callbacks_t bthh_callbacks = { + .size = sizeof(bthh_callbacks), + .connection_state_cb = hidhost_connection_state_cb, + .hid_info_cb = hidhost_hid_info_cb, + .protocol_mode_cb = hidhost_protocol_mode_cb, + .idle_time_cb = NULL, + .get_report_cb = hidhost_get_report_cb, + .virtual_unplug_cb = hidhost_virual_unplug_cb +}; + +static void gattc_register_client_cb(int status, int client_if, + bt_uuid_t *app_uuid) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_GATTC_REGISTER_CLIENT; + + step->callback_result.status = status; + + schedule_callback_call(step); +} + +static void gattc_scan_result_cb(bt_bdaddr_t *bda, int rssi, uint8_t *adv_data) +{ + struct step *step = g_new0(struct step, 1); + bt_property_t *props[2]; + + step->callback = CB_GATTC_SCAN_RESULT; + step->callback_result.adv_data = adv_data ? TRUE : FALSE; + + /* Utilize property verification mechanism for those */ + props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); + props[1] = create_property(BT_PROPERTY_REMOTE_RSSI, &rssi, + sizeof(rssi)); + + step->callback_result.num_properties = 2; + step->callback_result.properties = repack_properties(2, props); + + g_free(props[0]->val); + g_free(props[0]); + g_free(props[1]->val); + g_free(props[1]); + + schedule_callback_call(step); +} + +static void gattc_connect_cb(int conn_id, int status, int client_if, + bt_bdaddr_t *bda) +{ + struct step *step = g_new0(struct step, 1); + bt_property_t *props[1]; + + step->callback = CB_GATTC_OPEN; + step->callback_result.status = status; + step->callback_result.conn_id = conn_id; + step->callback_result.client_id = client_if; + + /* Utilize property verification mechanism for bdaddr */ + props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); + + step->callback_result.num_properties = 1; + step->callback_result.properties = repack_properties(1, props); + + g_free(props[0]->val); + g_free(props[0]); + + schedule_callback_call(step); +} + +static void gattc_disconnect_cb(int conn_id, int status, int client_if, + bt_bdaddr_t *bda) +{ + struct step *step = g_new0(struct step, 1); + bt_property_t *props[1]; + + step->callback = CB_GATTC_CLOSE; + step->callback_result.status = status; + step->callback_result.conn_id = conn_id; + step->callback_result.client_id = client_if; + + /* Utilize property verification mechanism for bdaddr */ + props[0] = create_property(BT_PROPERTY_BDADDR, bda, sizeof(*bda)); + + step->callback_result.num_properties = 1; + step->callback_result.properties = repack_properties(1, props); + + g_free(props[0]->val); + g_free(props[0]); + + schedule_callback_call(step); +} + +static void gattc_listen_cb(int status, int server_if) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_GATTC_LISTEN; + step->callback_result.status = status; + + schedule_callback_call(step); +} + +static void gattc_search_result_cb(int conn_id, btgatt_srvc_id_t *srvc_id) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_GATTC_SEARCH_RESULT; + step->callback_result.conn_id = conn_id; + step->callback_result.service = g_memdup(srvc_id, sizeof(*srvc_id)); + + schedule_callback_call(step); +} + +static void gattc_search_complete_cb(int conn_id, int status) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_GATTC_SEARCH_COMPLETE; + step->callback_result.conn_id = conn_id; + + schedule_callback_call(step); +} + +static void gattc_get_characteristic_cb(int conn_id, int status, + btgatt_srvc_id_t *srvc_id, btgatt_gatt_id_t *char_id, + int char_prop) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_GATTC_GET_CHARACTERISTIC; + step->callback_result.status = status; + step->callback_result.conn_id = conn_id; + step->callback_result.service = g_memdup(srvc_id, sizeof(*srvc_id)); + step->callback_result.characteristic = g_memdup(char_id, + sizeof(*char_id)); + step->callback_result.char_prop = char_prop; + + schedule_callback_call(step); +} + +static void pan_control_state_cb(btpan_control_state_t state, + bt_status_t error, int local_role, + const char *ifname) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_PAN_CONTROL_STATE; + step->callback_result.state = local_role; + step->callback_result.ctrl_state = error; + step->callback_result.local_role = state; + + schedule_callback_call(step); +} + +static void pan_connection_state_cb(btpan_connection_state_t state, + bt_status_t error, + const bt_bdaddr_t *bd_addr, + int local_role, int remote_role) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_PAN_CONNECTION_STATE; + step->callback_result.state = error; + step->callback_result.conn_state = state; + step->callback_result.local_role = local_role; + step->callback_result.remote_role = remote_role; + + schedule_callback_call(step); +} + +static btpan_callbacks_t btpan_callbacks = { + .size = sizeof(btpan_callbacks), + .control_state_cb = pan_control_state_cb, + .connection_state_cb = pan_connection_state_cb, +}; + +static void hdp_app_reg_state_cb(int app_id, bthl_app_reg_state_t state) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_HDP_APP_REG_STATE; + step->callback_result.app_id = app_id; + step->callback_result.app_state = state; + + schedule_callback_call(step); +} + +static void hdp_channel_state_cb(int app_id, bt_bdaddr_t *bd_addr, + int mdep_cfg_index, int channel_id, + bthl_channel_state_t state, int fd) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_HDP_CHANNEL_STATE; + step->callback_result.app_id = app_id; + step->callback_result.channel_id = channel_id; + step->callback_result.mdep_cfg_index = mdep_cfg_index; + step->callback_result.channel_state = state; + + schedule_callback_call(step); +} + +static bthl_callbacks_t bthl_callbacks = { + .size = sizeof(bthl_callbacks), + .app_reg_state_cb = hdp_app_reg_state_cb, + .channel_state_cb = hdp_channel_state_cb, +}; + +static void a2dp_connection_state_cb(btav_connection_state_t state, + bt_bdaddr_t *bd_addr) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_A2DP_CONN_STATE; + step->callback_result.state = state; + + schedule_callback_call(step); +} + +static void a2dp_audio_state_cb(btav_audio_state_t state, bt_bdaddr_t *bd_addr) +{ + struct step *step = g_new0(struct step, 1); + + step->callback = CB_A2DP_AUDIO_STATE; + step->callback_result.state = state; + + schedule_callback_call(step); +} + +static btav_callbacks_t bta2dp_callbacks = { + .size = sizeof(bta2dp_callbacks), + .connection_state_cb = a2dp_connection_state_cb, + .audio_state_cb = a2dp_audio_state_cb, +}; + +static btrc_callbacks_t btavrcp_callbacks = { + .size = sizeof(btavrcp_callbacks), +}; + +static const btgatt_client_callbacks_t btgatt_client_callbacks = { + .register_client_cb = gattc_register_client_cb, + .scan_result_cb = gattc_scan_result_cb, + .open_cb = gattc_connect_cb, + .close_cb = gattc_disconnect_cb, + .search_complete_cb = gattc_search_complete_cb, + .search_result_cb = gattc_search_result_cb, + .get_characteristic_cb = gattc_get_characteristic_cb, + .get_descriptor_cb = NULL, + .get_included_service_cb = NULL, + .register_for_notification_cb = NULL, + .notify_cb = NULL, + .read_characteristic_cb = NULL, + .write_characteristic_cb = NULL, + .read_descriptor_cb = NULL, + .write_descriptor_cb = NULL, + .execute_write_cb = NULL, + .read_remote_rssi_cb = NULL, + .listen_cb = gattc_listen_cb +}; + +static const btgatt_server_callbacks_t btgatt_server_callbacks = { + .register_server_cb = NULL, + .connection_cb = NULL, + .service_added_cb = NULL, + .included_service_added_cb = NULL, + .characteristic_added_cb = NULL, + .descriptor_added_cb = NULL, + .service_started_cb = NULL, + .service_stopped_cb = NULL, + .service_deleted_cb = NULL, + .request_read_cb = NULL, + .request_write_cb = NULL, + .request_exec_write_cb = NULL, + .response_confirmation_cb = NULL +}; + +static const btgatt_callbacks_t btgatt_callbacks = { + .size = sizeof(btgatt_callbacks), + .client = &btgatt_client_callbacks, + .server = &btgatt_server_callbacks +}; + +static bool setup_base(struct test_data *data) +{ + const hw_module_t *module; + hw_device_t *device; + int signal_fd[2]; + char buf[1024]; + pid_t pid; + int len; + int err; + + if (pipe(signal_fd)) + return false; + + pid = fork(); + + if (pid < 0) { + close(signal_fd[0]); + close(signal_fd[1]); + return false; + } + + if (pid == 0) { + if (!tester_use_debug()) + fclose(stderr); + + close(signal_fd[0]); + emulator(signal_fd[1], data->mgmt_index); + exit(0); + } + + close(signal_fd[1]); + data->bluetoothd_pid = pid; + + len = read(signal_fd[0], buf, sizeof(buf)); + if (len <= 0 || strcmp(buf, EMULATOR_SIGNAL)) { + close(signal_fd[0]); + return false; + } + + close(signal_fd[0]); + + err = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, + AUDIO_HARDWARE_MODULE_ID_A2DP, &module); + if (err) + return false; + + err = audio_hw_device_open(module, &data->audio); + if (err) + return false; + + err = hw_get_module(BT_HARDWARE_MODULE_ID, &module); + if (err) + return false; + + err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device); + if (err) + return false; + + data->device = device; + + data->if_bluetooth = ((bluetooth_device_t *) + device)->get_bluetooth_interface(); + if (!data->if_bluetooth) + return false; + + if (!(data->steps = queue_new())) + return false; + + data->pdus = queue_new(); + if (!data->pdus) { + queue_destroy(data->steps, NULL); + return false; + } + + return true; +} + +static void setup(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + + if (!setup_base(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_socket(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + const void *sock; + + if (!setup_base(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + sock = data->if_bluetooth->get_profile_interface(BT_PROFILE_SOCKETS_ID); + if (!sock) { + tester_setup_failed(); + return; + } + + data->if_sock = sock; + + tester_setup_complete(); +} + +static void setup_hidhost(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + const void *hid; + + if (!setup_base(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + hid = data->if_bluetooth->get_profile_interface(BT_PROFILE_HIDHOST_ID); + if (!hid) { + tester_setup_failed(); + return; + } + + data->if_hid = hid; + + status = data->if_hid->init(&bthh_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_hid = NULL; + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_pan(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + const void *pan; + + if (!setup_base(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + pan = data->if_bluetooth->get_profile_interface(BT_PROFILE_PAN_ID); + if (!pan) { + tester_setup_failed(); + return; + } + + data->if_pan = pan; + + status = data->if_pan->init(&btpan_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_pan = NULL; + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_hdp(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + const void *hdp; + + if (!setup_base(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + hdp = data->if_bluetooth->get_profile_interface(BT_PROFILE_HEALTH_ID); + if (!hdp) { + tester_setup_failed(); + return; + } + + data->if_hdp = hdp; + + status = data->if_hdp->init(&bthl_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_hdp = NULL; + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_a2dp(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_interface_t *if_bt; + bt_status_t status; + const void *a2dp; + + if (!setup_base(data)) { + tester_setup_failed(); + return; + } + + if_bt = data->if_bluetooth; + + status = if_bt->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + a2dp = if_bt->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID); + if (!a2dp) { + tester_setup_failed(); + return; + } + + data->if_a2dp = a2dp; + + status = data->if_a2dp->init(&bta2dp_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_a2dp = NULL; + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_avrcp(const void *test_data) +{ + struct test_data *data = tester_get_data(); + const bt_interface_t *if_bt; + bt_status_t status; + const void *a2dp, *avrcp; + + if (!setup_base(data)) { + tester_setup_failed(); + return; + } + + if_bt = data->if_bluetooth; + + status = if_bt->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + a2dp = if_bt->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID); + if (!a2dp) { + tester_setup_failed(); + return; + } + + data->if_a2dp = a2dp; + + status = data->if_a2dp->init(&bta2dp_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_a2dp = NULL; + tester_setup_failed(); + return; + } + + avrcp = if_bt->get_profile_interface(BT_PROFILE_AV_RC_ID); + if (!a2dp) { + tester_setup_failed(); + return; + } + + data->if_avrcp = avrcp; + + status = data->if_avrcp->init(&btavrcp_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_avrcp = NULL; + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void setup_gatt(const void *test_data) +{ + struct test_data *data = tester_get_data(); + bt_status_t status; + const void *gatt; + + if (!setup_base(data)) { + tester_setup_failed(); + return; + } + + status = data->if_bluetooth->init(&bt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_bluetooth = NULL; + tester_setup_failed(); + return; + } + + gatt = data->if_bluetooth->get_profile_interface(BT_PROFILE_GATT_ID); + if (!gatt) { + tester_setup_failed(); + return; + } + + data->if_gatt = gatt; + + status = data->if_gatt->init(&btgatt_callbacks); + if (status != BT_STATUS_SUCCESS) { + data->if_gatt = NULL; + + tester_setup_failed(); + return; + } + + tester_setup_complete(); +} + +static void teardown(const void *test_data) +{ + struct test_data *data = tester_get_data(); + + queue_destroy(data->steps, NULL); + data->steps = NULL; + + queue_destroy(data->pdus, NULL); + data->pdus = NULL; + + if (data->if_gatt) { + data->if_gatt->cleanup(); + data->if_gatt = NULL; + } + + if (data->if_hid) { + data->if_hid->cleanup(); + data->if_hid = NULL; + } + + if (data->if_pan) { + data->if_pan->cleanup(); + data->if_pan = NULL; + } + + if (data->if_hdp) { + data->if_hdp->cleanup(); + data->if_hdp = NULL; + } + + if (data->if_stream) { + data->audio->close_output_stream(data->audio, data->if_stream); + data->if_stream = NULL; + } + + if (data->if_a2dp) { + data->if_a2dp->cleanup(); + data->if_a2dp = NULL; + } + + if (data->if_avrcp) { + data->if_avrcp->cleanup(); + data->if_avrcp = NULL; + } + + if (data->if_bluetooth) { + data->if_bluetooth->cleanup(); + data->if_bluetooth = NULL; + } + + data->device->close(data->device); + audio_hw_device_close(data->audio); + + /* + * Ssp_request_cb pointer can be set do default_ssp_req_cb. + * Set it back to ssp_request_cb + */ + bt_callbacks.ssp_request_cb = ssp_request_cb; + + if (!data->bluetoothd_pid) + tester_teardown_complete(); +} + +static void emu_connectable_complete(uint16_t opcode, uint8_t status, + const void *param, uint8_t len, + void *user_data) +{ + struct step *step; + struct test_data *data = user_data; + + switch (opcode) { + case BT_HCI_CMD_WRITE_SCAN_ENABLE: + break; + case BT_HCI_CMD_LE_SET_ADV_ENABLE: + /* + * For BREDRLE emulator we want to verify step after scan + * enable and not after le_set_adv_enable + */ + if (data->hciemu_type == HCIEMU_TYPE_BREDRLE) + return; + + break; + default: + return; + } + + step = g_new0(struct step, 1); + + if (status) { + tester_warn("Emulated remote setup failed."); + step->action_status = BT_STATUS_FAIL; + } else { + tester_warn("Emulated remote setup done."); + step->action_status = BT_STATUS_SUCCESS; + } + + schedule_action_verification(step); +} + +void emu_setup_powered_remote_action(void) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + + bthost = hciemu_client_get_host(data->hciemu); + bthost_set_cmd_complete_cb(bthost, emu_connectable_complete, data); + + if ((data->hciemu_type == HCIEMU_TYPE_LE) || + (data->hciemu_type == HCIEMU_TYPE_BREDRLE)) + bthost_set_adv_enable(bthost, 0x01, 0x02); + + if (data->hciemu_type != HCIEMU_TYPE_LE) + bthost_write_scan_enable(bthost, 0x03); +} + +void emu_set_pin_code_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct bthost *bthost; + struct step *step = g_new0(struct step, 1); + + bthost = hciemu_client_get_host(data->hciemu); + + bthost_set_pin_code(bthost, action_data->pin->pin, + action_data->pin_len); + + step->action_status = BT_STATUS_SUCCESS; + + tester_print("Setting emu pin done."); + + schedule_action_verification(step); +} + +void emu_set_ssp_mode_action(void) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + struct step *step = g_new0(struct step, 1); + + bthost = hciemu_client_get_host(data->hciemu); + + bthost_write_ssp_mode(bthost, 0x01); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +void emu_set_connect_cb_action(void) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + struct step *current_data_step = queue_peek_head(data->steps); + void *cb = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + bthost_set_connect_cb(bthost, cb, data); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +void emu_remote_connect_hci_action(void) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + const uint8_t *master_addr; + + master_addr = hciemu_get_master_bdaddr(data->hciemu); + + tester_print("Trying to connect hci"); + + if (action_data) + bthost_hci_connect(bthost, master_addr, + action_data->bearer_type); + else + bthost_hci_connect(bthost, master_addr, BDADDR_BREDR); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +void emu_remote_disconnect_hci_action(void) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + struct step *current_data_step = queue_peek_head(data->steps); + uint16_t *handle = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + if (handle) { + bthost_hci_disconnect(bthost, *handle, 0x13); + step->action_status = BT_STATUS_SUCCESS; + } else { + step->action_status = BT_STATUS_FAIL; + } + + schedule_action_verification(step); +} + +void emu_set_io_cap(void) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost; + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + bthost = hciemu_client_get_host(data->hciemu); + + if (action_data) + bthost_set_io_capability(bthost, action_data->io_cap); + else + bthost_set_io_capability(bthost, 0x01); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +void emu_add_l2cap_server_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct emu_set_l2cap_data *l2cap_data = current_data_step->set_data; + struct bthost *bthost; + struct step *step = g_new0(struct step, 1); + + if (!l2cap_data) { + tester_warn("Invalid l2cap_data params"); + return; + } + + bthost = hciemu_client_get_host(data->hciemu); + + bthost_add_l2cap_server(bthost, l2cap_data->psm, l2cap_data->func, + l2cap_data->user_data); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +static void rfcomm_connect_cb(uint16_t handle, uint16_t cid, void *user_data, + bool status) +{ + struct step *step = g_new0(struct step, 1); + + tester_print("Connect handle %d, cid %d cb status: %d", handle, cid, + status); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +void emu_add_rfcomm_server_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *rfcomm_data = current_data_step->set_data; + struct bthost *bthost; + struct step *step = g_new0(struct step, 1); + + if (!rfcomm_data) { + tester_warn("Invalid l2cap_data params"); + return; + } + + bthost = hciemu_client_get_host(data->hciemu); + + bthost_add_rfcomm_server(bthost, rfcomm_data->channel, + rfcomm_connect_cb, data); + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +void dummy_action(void) +{ + struct step *step = g_new0(struct step, 1); + + step->action = dummy_action; + + schedule_action_verification(step); +} + +void bluetooth_enable_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_bluetooth->enable(); + + schedule_action_verification(step); +} + +void bluetooth_disable_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_bluetooth->disable(); + + schedule_action_verification(step); +} + +void bt_set_property_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + struct step *current_data_step = queue_peek_head(data->steps); + bt_property_t *prop; + + if (!current_data_step->set_data) { + tester_debug("BT property not set for step"); + tester_test_failed(); + return; + } + + prop = (bt_property_t *)current_data_step->set_data; + + step->action_status = data->if_bluetooth->set_adapter_property(prop); + + schedule_action_verification(step); +} + +void bt_get_property_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + struct step *current_data_step = queue_peek_head(data->steps); + bt_property_t *prop; + + if (!current_data_step->set_data) { + tester_debug("BT property to get not defined"); + tester_test_failed(); + return; + } + + prop = (bt_property_t *)current_data_step->set_data; + + step->action_status = data->if_bluetooth->get_adapter_property( + prop->type); + + schedule_action_verification(step); +} + +void bt_start_discovery_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_bluetooth->start_discovery(); + + schedule_action_verification(step); +} + +void bt_cancel_discovery_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_bluetooth->cancel_discovery(); + + schedule_action_verification(step); +} + +void bt_get_device_props_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct step *step = g_new0(struct step, 1); + + if (!current_data_step->set_data) { + tester_debug("bdaddr not defined"); + tester_test_failed(); + return; + } + + step->action_status = + data->if_bluetooth->get_remote_device_properties( + current_data_step->set_data); + + schedule_action_verification(step); +} + +void bt_get_device_prop_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + if (!action_data) { + tester_warn("No arguments for 'get remote device prop' req."); + tester_test_failed(); + return; + } + + step->action_status = data->if_bluetooth->get_remote_device_property( + action_data->addr, + action_data->prop_type); + + schedule_action_verification(step); +} + +void bt_set_device_prop_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + if (!action_data) { + tester_warn("No arguments for 'set remote device prop' req."); + tester_test_failed(); + return; + } + + step->action_status = data->if_bluetooth->set_remote_device_property( + action_data->addr, + action_data->prop); + + schedule_action_verification(step); +} + +void bt_create_bond_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + if (!action_data || !action_data->addr) { + tester_warn("Bad arguments for 'create bond' req."); + tester_test_failed(); + return; + } + + step->action_status = + data->if_bluetooth->create_bond(action_data->addr); + + schedule_action_verification(step); +} + +void bt_pin_reply_accept_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + if (!action_data || !action_data->addr || !action_data->pin) { + tester_warn("Bad arguments for 'pin reply' req."); + tester_test_failed(); + return; + } + + step->action_status = data->if_bluetooth->pin_reply(action_data->addr, + TRUE, + action_data->pin_len, + action_data->pin); + + schedule_action_verification(step); +} + +void bt_ssp_reply_accept_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_bluetooth->ssp_reply(action_data->addr, + action_data->ssp_variant, + action_data->accept, 0); + + schedule_action_verification(step); +} + +void bt_cancel_bond_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + bt_bdaddr_t *addr = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_bluetooth->cancel_bond(addr); + + schedule_action_verification(step); +} + +void bt_remove_bond_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + bt_bdaddr_t *addr = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_bluetooth->remove_bond(addr); + + schedule_action_verification(step); +} + +static void default_ssp_req_cb(bt_bdaddr_t *remote_bd_addr, bt_bdname_t *name, + uint32_t cod, bt_ssp_variant_t pairing_variant, + uint32_t pass_key) +{ + struct test_data *t_data = tester_get_data(); + + t_data->if_bluetooth->ssp_reply(remote_bd_addr, pairing_variant, true, + pass_key); +} + +void set_default_ssp_request_handler(void) +{ + struct step *step = g_new0(struct step, 1); + + bt_callbacks.ssp_request_cb = default_ssp_req_cb; + + step->action_status = BT_STATUS_SUCCESS; + + schedule_action_verification(step); +} + +static void generic_test_function(const void *test_data) +{ + struct test_data *data = tester_get_data(); + struct step *first_step; + + init_test_steps(data); + + /* first step action */ + first_step = queue_peek_head(data->steps); + if (!first_step->action) { + tester_print("tester: No initial action declared"); + tester_test_failed(); + return; + } + first_step->action(); +} + +#define test(data, test_setup, test, test_teardown) \ + do { \ + struct test_data *user; \ + user = g_malloc0(sizeof(struct test_data)); \ + if (!user) \ + break; \ + user->hciemu_type = data->emu_type; \ + user->test_data = data; \ + tester_add_full(data->title, data, test_pre_setup, \ + test_setup, test, test_teardown, \ + test_post_teardown, 3, user, g_free); \ + } while (0) + +static void tester_testcases_cleanup(void) +{ + remove_bluetooth_tests(); + remove_socket_tests(); + remove_hidhost_tests(); + remove_gatt_tests(); + remove_a2dp_tests(); + remove_avrcp_tests(); + remove_hdp_tests(); + remove_pan_tests(); +} + +static void add_bluetooth_tests(void *data, void *user_data) +{ + struct test_case *tc = data; + + test(tc, setup, generic_test_function, teardown); +} + +static void add_socket_tests(void *data, void *user_data) +{ + struct test_case *tc = data; + + test(tc, setup_socket, generic_test_function, teardown); +} + +static void add_hidhost_tests(void *data, void *user_data) +{ + struct test_case *tc = data; + + test(tc, setup_hidhost, generic_test_function, teardown); +} + +static void add_pan_tests(void *data, void *user_data) +{ + struct test_case *tc = data; + + test(tc, setup_pan, generic_test_function, teardown); +} + +static void add_hdp_tests(void *data, void *user_data) +{ + struct test_case *tc = data; + + test(tc, setup_hdp, generic_test_function, teardown); +} + +static void add_a2dp_tests(void *data, void *user_data) +{ + struct test_case *tc = data; + + test(tc, setup_a2dp, generic_test_function, teardown); +} + +static void add_avrcp_tests(void *data, void *user_data) +{ + struct test_case *tc = data; + + test(tc, setup_avrcp, generic_test_function, teardown); +} + +static void add_gatt_tests(void *data, void *user_data) +{ + struct test_case *tc = data; + + test(tc, setup_gatt, generic_test_function, teardown); +} + +int main(int argc, char *argv[]) +{ + snprintf(exec_dir, sizeof(exec_dir), "%s", dirname(argv[0])); + + tester_init(&argc, &argv); + + queue_foreach(get_bluetooth_tests(), add_bluetooth_tests, NULL); + queue_foreach(get_socket_tests(), add_socket_tests, NULL); + queue_foreach(get_hidhost_tests(), add_hidhost_tests, NULL); + queue_foreach(get_pan_tests(), add_pan_tests, NULL); + queue_foreach(get_hdp_tests(), add_hdp_tests, NULL); + queue_foreach(get_a2dp_tests(), add_a2dp_tests, NULL); + queue_foreach(get_avrcp_tests(), add_avrcp_tests, NULL); + queue_foreach(get_gatt_tests(), add_gatt_tests, NULL); + + if (tester_run()) + return 1; + + tester_testcases_cleanup(); + + return 0; +} diff -Nru bluez-4.101/android/tester-main.h bluez-5.23/android/tester-main.h --- bluez-4.101/android/tester-main.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-main.h 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,483 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/mgmt.h" + +#include "src/shared/tester.h" +#include "src/shared/hciemu.h" +#include "src/shared/mgmt.h" +#include "src/shared/queue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEST_CASE_BREDR(text, ...) { \ + HCIEMU_TYPE_BREDR, \ + text, \ + sizeof((struct step[]) {__VA_ARGS__}) / sizeof(struct step), \ + (struct step[]) {__VA_ARGS__}, \ + } + +#define TEST_CASE_BREDRLE(text, ...) { \ + HCIEMU_TYPE_BREDRLE, \ + text, \ + sizeof((struct step[]) {__VA_ARGS__}) / sizeof(struct step), \ + (struct step[]) {__VA_ARGS__}, \ + } + +#define ACTION(status, act_fun, data_set) { \ + .action_status = status, \ + .action = act_fun, \ + .set_data = data_set, \ + } + +#define ACTION_FAIL(act_fun, data_set) \ + ACTION(BT_STATUS_FAIL, act_fun, data_set) + +#define ACTION_SUCCESS(act_fun, data_set) \ + ACTION(BT_STATUS_SUCCESS, act_fun, data_set) + +#define CALLBACK(cb) { \ + .callback = cb, \ + } + +#define CALLBACK_STATE(cb, cb_res) { \ + .callback = cb, \ + .callback_result.state = cb_res, \ + } + +#define CALLBACK_STATUS(cb, cb_res) { \ + .callback = cb, \ + .callback_result.status = cb_res, \ + } + +#define CALLBACK_ADAPTER_PROPS(props, prop_cnt) { \ + .callback = CB_BT_ADAPTER_PROPERTIES, \ + .callback_result.properties = props, \ + .callback_result.num_properties = prop_cnt, \ + } + +#define CALLBACK_PROPS(cb, props, prop_cnt) { \ + .callback = cb, \ + .callback_result.properties = props, \ + .callback_result.num_properties = prop_cnt, \ + } + +#define CALLBACK_HH_MODE(cb, cb_res, cb_mode) { \ + .callback = cb, \ + .callback_result.status = cb_res, \ + .callback_result.mode = cb_mode, \ + } + +#define CALLBACK_HHREPORT(cb, cb_res, cb_rep_size) { \ + .callback = cb, \ + .callback_result.status = cb_res, \ + .callback_result.report_size = cb_rep_size, \ + } + +#define CLLBACK_GATTC_SCAN_RES(props, prop_cnt, cb_adv_data) {\ + .callback = CB_GATTC_SCAN_RESULT, \ + .callback_result.properties = props, \ + .callback_result.num_properties = prop_cnt, \ + .callback_result.adv_data = cb_adv_data, \ + } + +#define CALLBACK_GATTC_CONNECT(cb_res, cb_prop, cb_conn_id, cb_client_id) { \ + .callback = CB_GATTC_OPEN, \ + .callback_result.status = cb_res, \ + .callback_result.properties = cb_prop, \ + .callback_result.num_properties = 1, \ + .callback_result.conn_id = cb_conn_id, \ + .callback_result.client_id = cb_client_id, \ + } + +#define CALLBACK_GATTC_SEARCH_RESULT(cb_conn_id, cb_service) { \ + .callback = CB_GATTC_SEARCH_RESULT, \ + .callback_result.conn_id = cb_conn_id, \ + .callback_result.service = cb_service \ + } + +#define CALLBACK_GATTC_SEARCH_COMPLETE(cb_res, cb_conn_id) { \ + .callback = CB_GATTC_SEARCH_COMPLETE, \ + .callback_result.conn_id = cb_conn_id \ + } +#define CALLBACK_GATTC_GET_CHARACTERISTIC_CB(cb_res, cb_conn_id, cb_service, \ + cb_char, cb_char_prop) { \ + .callback = CB_GATTC_GET_CHARACTERISTIC, \ + .callback_result.conn_id = cb_conn_id, \ + .callback_result.status = cb_res, \ + .callback_result.service = cb_service, \ + .callback_result.characteristic = cb_char, \ + .callback_result.char_prop = cb_char_prop \ + } + +#define CALLBACK_GATTC_DISCONNECT(cb_res, cb_prop, cb_conn_id, cb_client_id) { \ + .callback = CB_GATTC_CLOSE, \ + .callback_result.status = cb_res, \ + .callback_result.properties = cb_prop, \ + .callback_result.num_properties = 1, \ + .callback_result.conn_id = cb_conn_id, \ + .callback_result.client_id = cb_client_id, \ + } + +#define CALLBACK_PAN_CTRL_STATE(cb, cb_res, cb_state, cb_local_role) { \ + .callback = cb, \ + .callback_result.status = cb_res, \ + .callback_result.ctrl_state = cb_state, \ + .callback_result.local_role = cb_local_role, \ + } + +#define CALLBACK_PAN_CONN_STATE(cb, cb_res, cb_state, cb_local_role, \ + cb_remote_role) { \ + .callback = cb, \ + .callback_result.status = cb_res, \ + .callback_result.conn_state = cb_state, \ + .callback_result.local_role = cb_local_role, \ + .callback_result.remote_role = cb_remote_role, \ + } + +#define CALLBACK_HDP_APP_REG_STATE(cb, cb_app_id, cb_state) { \ + .callback = cb, \ + .callback_result.app_id = cb_app_id, \ + .callback_result.app_state = cb_state, \ + } + +#define CALLBACK_HDP_CHANNEL_STATE(cb, cb_app_id, cb_channel_id, \ + cb_mdep_cfg_index, cb_state) { \ + .callback = cb, \ + .callback_result.app_id = cb_app_id, \ + .callback_result.channel_id = cb_channel_id, \ + .callback_result.mdep_cfg_index = cb_mdep_cfg_index, \ + .callback_result.channel_state = cb_state, \ + } + +#define CALLBACK_AV_CONN_STATE(cb, cb_state) { \ + .callback = cb, \ + .callback_result.state = cb_state, \ + } + +#define CALLBACK_AV_AUDIO_STATE(cb, cb_state) { \ + .callback = cb, \ + .callback_result.state = cb_state, \ + } + +#define CALLBACK_DEVICE_PROPS(props, prop_cnt) \ + CALLBACK_PROPS(CB_BT_REMOTE_DEVICE_PROPERTIES, props, prop_cnt) + +#define CALLBACK_DEVICE_FOUND(props, prop_cnt) \ + CALLBACK_PROPS(CB_BT_DEVICE_FOUND, props, prop_cnt) + +#define CALLBACK_BOND_STATE(cb_res, props, prop_cnt) { \ + .callback = CB_BT_BOND_STATE_CHANGED, \ + .callback_result.state = cb_res, \ + .callback_result.properties = props, \ + .callback_result.num_properties = prop_cnt, \ + } + +#define CALLBACK_BOND_STATE_FAILED(cb_res, props, prop_cnt, reason) { \ + .callback = CB_BT_BOND_STATE_CHANGED, \ + .callback_result.state = cb_res, \ + .callback_result.status = reason, \ + .callback_result.properties = props, \ + .callback_result.num_properties = prop_cnt, \ + } + +#define CALLBACK_SSP_REQ(pair_var, props, prop_cnt) { \ + .callback = CB_BT_SSP_REQUEST, \ + .callback_result.pairing_variant = pair_var, \ + .callback_result.properties = props, \ + .callback_result.num_properties = prop_cnt, \ + } + +/* + * NOTICE: + * Callback enum sections should be + * updated while adding new HAL to tester. + */ +typedef enum { + CB_BT_ADAPTER_STATE_CHANGED = 1, + CB_BT_ADAPTER_PROPERTIES, + CB_BT_REMOTE_DEVICE_PROPERTIES, + CB_BT_DEVICE_FOUND, + CB_BT_DISCOVERY_STATE_CHANGED, + CB_BT_PIN_REQUEST, + CB_BT_SSP_REQUEST, + CB_BT_BOND_STATE_CHANGED, + CB_BT_ACL_STATE_CHANGED, + CB_BT_THREAD_EVT, + CB_BT_DUT_MODE_RECV, + CB_BT_LE_TEST_MODE, + + /* Hidhost cb */ + CB_HH_CONNECTION_STATE, + CB_HH_HID_INFO, + CB_HH_PROTOCOL_MODE, + CB_HH_IDLE_TIME, + CB_HH_GET_REPORT, + CB_HH_VIRTUAL_UNPLUG, + + /* PAN cb */ + CB_PAN_CONTROL_STATE, + CB_PAN_CONNECTION_STATE, + + /* HDP cb */ + CB_HDP_APP_REG_STATE, + CB_HDP_CHANNEL_STATE, + + /* A2DP cb */ + CB_A2DP_CONN_STATE, + CB_A2DP_AUDIO_STATE, + + /* Gatt client */ + CB_GATTC_REGISTER_CLIENT, + CB_GATTC_SCAN_RESULT, + CB_GATTC_OPEN, + CB_GATTC_CLOSE, + CB_GATTC_SEARCH_COMPLETE, + CB_GATTC_SEARCH_RESULT, + CB_GATTC_GET_CHARACTERISTIC, + CB_GATTC_GET_DESCRIPTOR, + CB_GATTC_GET_INCLUDED_SERVICE, + CB_GATTC_REGISTER_FOR_NOTIFICATION, + CB_GATTC_NOTIFY, + CB_GATTC_READ_CHARACTERISTIC, + CB_GATTC_WRITE_CHARACTERISTIC, + CB_GATTC_READ_DESCRIPTOR, + CB_GATTC_WRITE_DESCRIPTOR, + CB_GATTC_EXECUTE_WRITE, + CB_GATTC_READ_REMOTE_RSSI, + CB_GATTC_LISTEN, + + /* Gatt server */ + CB_GATTS_REGISTER_SERVER, + CB_GATTS_CONNECTION, + CB_GATTS_SERVICE_ADDED, + CB_GATTS_INCLUDED_SERVICE_ADDED, + CB_GATTS_CHARACTERISTIC_ADDED, + CB_GATTS_DESCRIPTOR_ADDED, + CB_GATTS_SERVICE_STARTED, + CB_GATTS_SERVICE_STOPPED, + CB_GATTS_SERVICE_DELETED, + CB_GATTS_REQUEST_READ, + CB_GATTS_REQUEST_WRITE, + CB_GATTS_REQUEST_EXEC_WRITE, + CB_GATTS_RESPONSE_CONFIRMATION, +} expected_bt_callback_t; + +struct test_data { + struct mgmt *mgmt; + audio_hw_device_t *audio; + struct hw_device_t *device; + struct hciemu *hciemu; + enum hciemu_type hciemu_type; + + const bt_interface_t *if_bluetooth; + const btsock_interface_t *if_sock; + const bthh_interface_t *if_hid; + const btpan_interface_t *if_pan; + const bthl_interface_t *if_hdp; + const btav_interface_t *if_a2dp; + struct audio_stream_out *if_stream; + const btrc_interface_t *if_avrcp; + const btgatt_interface_t *if_gatt; + + const void *test_data; + struct queue *steps; + + guint signalfd; + uint16_t mgmt_index; + pid_t bluetoothd_pid; + + struct queue *pdus; +}; + +/* + * Struct holding bluetooth HAL action parameters + */ +struct bt_action_data { + bt_bdaddr_t *addr; + + /* Remote props action arguments */ + const int prop_type; + const bt_property_t *prop; + + /* Bonding requests parameters */ + bt_pin_code_t *pin; + const uint8_t pin_len; + const uint8_t ssp_variant; + const bool accept; + const uint16_t io_cap; + + /* Socket HAL specific params */ + const btsock_type_t sock_type; + const int channel; + const uint8_t *service_uuid; + const char *service_name; + const int flags; + int *fd; + + /* HidHost params */ + const int report_size; + + /*Connection params*/ + const uint8_t bearer_type; +}; + +/* bthost's l2cap server setup parameters */ +struct emu_set_l2cap_data { + const uint16_t psm; + const bthost_l2cap_connect_cb func; + void *user_data; +}; + +/* + * Callback data structure should be enhanced with data + * returned by callbacks. It's used for test case step + * matching with expected step data. + */ +struct bt_callback_data { + bt_state_t state; + bt_status_t status; + int num_properties; + bt_property_t *properties; + + bt_ssp_variant_t pairing_variant; + + bthh_protocol_mode_t mode; + int report_size; + + bool adv_data; + + int client_id; + int conn_id; + btgatt_srvc_id_t *service; + btgatt_gatt_id_t *characteristic; + int char_prop; + + btpan_control_state_t ctrl_state; + btpan_connection_state_t conn_state; + int local_role; + int remote_role; + + int app_id; + int channel_id; + int mdep_cfg_index; + bthl_app_reg_state_t app_state; + bthl_channel_state_t channel_state; +}; + +/* + * Step structure contains expected step data and step + * action, which should be performed before step check. + */ +struct step { + void (*action)(void); + int action_status; + + expected_bt_callback_t callback; + struct bt_callback_data callback_result; + + void *set_data; + int set_data_len; +}; + +struct test_case { + const uint8_t emu_type; + const char *title; + const uint16_t step_num; + const struct step *step; +}; + +/* Get, remove test cases API */ +struct queue *get_bluetooth_tests(void); +void remove_bluetooth_tests(void); +struct queue *get_socket_tests(void); +void remove_socket_tests(void); +struct queue *get_hidhost_tests(void); +void remove_hidhost_tests(void); +struct queue *get_pan_tests(void); +void remove_pan_tests(void); +struct queue *get_hdp_tests(void); +void remove_hdp_tests(void); +struct queue *get_a2dp_tests(void); +void remove_a2dp_tests(void); +struct queue *get_avrcp_tests(void); +void remove_avrcp_tests(void); +struct queue *get_gatt_tests(void); +void remove_gatt_tests(void); + +/* Generic tester API */ +void schedule_action_verification(struct step *step); + +/* Emulator actions */ +void emu_setup_powered_remote_action(void); +void emu_set_pin_code_action(void); +void emu_set_ssp_mode_action(void); +void emu_set_connect_cb_action(void); +void emu_remote_connect_hci_action(void); +void emu_remote_disconnect_hci_action(void); +void emu_set_io_cap(void); +void emu_add_l2cap_server_action(void); +void emu_add_rfcomm_server_action(void); + +/* Actions */ +void dummy_action(void); +void bluetooth_enable_action(void); +void bluetooth_disable_action(void); +void bt_set_property_action(void); +void bt_get_property_action(void); +void bt_start_discovery_action(void); +void bt_cancel_discovery_action(void); +void bt_get_device_props_action(void); +void bt_get_device_prop_action(void); +void bt_set_device_prop_action(void); +void bt_create_bond_action(void); +void bt_pin_reply_accept_action(void); +void bt_ssp_reply_accept_action(void); +void bt_cancel_bond_action(void); +void bt_remove_bond_action(void); +void set_default_ssp_request_handler(void); diff -Nru bluez-4.101/android/tester-pan.c bluez-5.23/android/tester-pan.c --- bluez-4.101/android/tester-pan.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-pan.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "emulator/bthost.h" +#include "tester-main.h" +#include "android/utils.h" + +static struct queue *list; /* List of pan test cases */ + +struct emu_cid_data { + uint16_t nap_handle; + uint16_t nap_cid; +}; + +static struct emu_cid_data cid_data; +static uint8_t pan_conn_req_pdu[] = { 0x01, 0x01, 0x02, 0x11, 0x16, + 0x11, 0x15 }; +static uint8_t pan_conn_rsp_pdu[] = { 0x01, 0x02, 0x00, 0x00 }; + +static void pan_nap_cid_hook_cb(const void *data, uint16_t len, void *user_data) +{ + struct test_data *t_data = tester_get_data(); + struct emu_cid_data *cid_data = user_data; + struct bthost *bthost = hciemu_client_get_host(t_data->hciemu); + + if (!memcmp((uint8_t *) data, pan_conn_req_pdu, + sizeof(pan_conn_req_pdu))) + bthost_send_cid(bthost, cid_data->nap_handle, cid_data->nap_cid, + pan_conn_rsp_pdu, sizeof(pan_conn_rsp_pdu)); +} + +static void pan_connect_request_cb(uint16_t handle, uint16_t cid, + void *user_data) +{ + struct test_data *data = tester_get_data(); + struct bthost *bthost = hciemu_client_get_host(data->hciemu); + + cid_data.nap_handle = handle; + cid_data.nap_cid = cid; + + bthost_add_cid_hook(bthost, handle, cid, pan_nap_cid_hook_cb, + &cid_data); +} + +static struct emu_set_l2cap_data l2cap_setup_data = { + .psm = 15, + .func = pan_connect_request_cb, + .user_data = NULL, +}; + +static void pan_connect_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr); + + step->action_status = data->if_pan->connect(&bdaddr, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP); + + schedule_action_verification(step); +} + +static void pan_disconnect_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + + bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr); + + step->action_status = data->if_pan->disconnect(&bdaddr); + + schedule_action_verification(step); +} + +static void pan_get_local_role_action(void) +{ + struct test_data *data = tester_get_data(); + const uint8_t *pan_addr = hciemu_get_client_bdaddr(data->hciemu); + struct step *step = g_new0(struct step, 1); + bt_bdaddr_t bdaddr; + int role; + + bdaddr2android((const bdaddr_t *) pan_addr, &bdaddr); + + role = data->if_pan->get_local_role(); + if (role == BTPAN_ROLE_PANU) + step->action_status = BT_STATUS_SUCCESS; + else + step->action_status = BT_STATUS_FAIL; + + schedule_action_verification(step); +} + +static void pan_enable_nap_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_pan->enable(BTPAN_ROLE_PANNAP); + + schedule_action_verification(step); +} + +static void pan_enable_panu_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_pan->enable(BTPAN_ROLE_PANU); + + schedule_action_verification(step); +} + +static void pan_enable_none_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *step = g_new0(struct step, 1); + + step->action_status = data->if_pan->enable(BTPAN_ROLE_NONE); + + schedule_action_verification(step); +} + +static struct test_case test_cases[] = { + TEST_CASE_BREDRLE("PAN Init", + ACTION_SUCCESS(dummy_action, NULL), + ), + TEST_CASE_BREDRLE("PAN Connect - Success", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(pan_connect_action, NULL), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_CONNECTING, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, + BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_CONNECTED, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_DISCONNECTED, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("PAN Disconnect - Success", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(pan_connect_action, NULL), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_CONNECTING, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, + BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_CONNECTED, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + ACTION_SUCCESS(pan_disconnect_action, NULL), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_DISCONNECTED, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("PAN GetLocalRole - Success", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(pan_connect_action, NULL), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_CONNECTING, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, + BTPAN_STATE_ENABLED, BTPAN_ROLE_PANU), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_CONNECTED, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + ACTION_SUCCESS(pan_get_local_role_action, NULL), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_PAN_CONN_STATE(CB_PAN_CONNECTION_STATE, + BT_STATUS_SUCCESS, + BTPAN_STATE_DISCONNECTED, + BTPAN_ROLE_PANU, BTPAN_ROLE_PANNAP), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("PAN Enable NAP - Success", + ACTION_SUCCESS(pan_enable_nap_action, NULL), + CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, + BTPAN_STATE_ENABLED, BTPAN_ROLE_PANNAP), + ), + TEST_CASE_BREDRLE("PAN Enable PANU - Success", + ACTION(BT_STATUS_UNSUPPORTED, pan_enable_panu_action, NULL), + ), + TEST_CASE_BREDRLE("PAN Enable NONE - Success", + ACTION_SUCCESS(pan_enable_nap_action, NULL), + CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, + BTPAN_STATE_ENABLED, BTPAN_ROLE_PANNAP), + ACTION_SUCCESS(pan_enable_none_action, NULL), + CALLBACK_PAN_CTRL_STATE(CB_PAN_CONTROL_STATE, BT_STATUS_SUCCESS, + BTPAN_STATE_DISABLED, BTPAN_ROLE_NONE), + ), +}; + +struct queue *get_pan_tests(void) +{ + uint16_t i = 0; + + list = queue_new(); + + for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) + if (!queue_push_tail(list, &test_cases[i])) + return NULL; + + return list; +} + +void remove_pan_tests(void) +{ + queue_destroy(list, NULL); +} diff -Nru bluez-4.101/android/tester-socket.c bluez-5.23/android/tester-socket.c --- bluez-4.101/android/tester-socket.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/tester-socket.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include "emulator/bthost.h" +#include "tester-main.h" + +static struct queue *list; /* List of socket test cases */ + +static bt_bdaddr_t bdaddr_dummy = { + .address = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55} +}; + +static int got_fd_result = -1; + +static struct bt_action_data btsock_param_socktype_0 = { + .addr = &bdaddr_dummy, + .sock_type = 0, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .fd = &got_fd_result, +}; + +static struct bt_action_data btsock_param_socktype_l2cap = { + .addr = &bdaddr_dummy, + .sock_type = BTSOCK_L2CAP, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .fd = &got_fd_result, +}; + +static struct bt_action_data btsock_param_channel_0 = { + .addr = &bdaddr_dummy, + .sock_type = BTSOCK_RFCOMM, + .channel = 0, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .fd = &got_fd_result, +}; + +static struct bt_action_data btsock_param = { + .addr = &bdaddr_dummy, + .sock_type = BTSOCK_RFCOMM, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .fd = &got_fd_result, +}; + +static struct bt_action_data btsock_param_inv_bdaddr = { + .addr = NULL, + .sock_type = BTSOCK_RFCOMM, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .fd = &got_fd_result, +}; + +static bt_bdaddr_t emu_remote_bdaddr_val = { + .address = { 0x00, 0xaa, 0x01, 0x01, 0x00, 0x00 }, +}; +static bt_property_t prop_emu_remote_bdadr = { + .type = BT_PROPERTY_BDADDR, + .val = &emu_remote_bdaddr_val, + .len = sizeof(emu_remote_bdaddr_val), +}; +static bt_property_t prop_emu_remotes_default_set[] = { + { BT_PROPERTY_BDADDR, sizeof(emu_remote_bdaddr_val), + &emu_remote_bdaddr_val }, +}; + +static struct bt_action_data btsock_param_emu_bdaddr = { + .addr = &emu_remote_bdaddr_val, + .sock_type = BTSOCK_RFCOMM, + .channel = 1, + .service_uuid = NULL, + .service_name = "Test service", + .flags = 0, + .fd = &got_fd_result, +}; + +static struct emu_set_l2cap_data l2cap_setup_data = { + .psm = 0x0003, + .func = NULL, + .user_data = NULL, +}; + +static struct bt_action_data prop_emu_remote_bdaddr_req = { + .addr = &emu_remote_bdaddr_val, + .prop_type = BT_PROPERTY_BDADDR, + .prop = &prop_emu_remote_bdadr, +}; + +static void socket_listen_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + *action_data->fd = -1; + + step->action_status = data->if_sock->listen(action_data->sock_type, + action_data->service_name, + action_data->service_uuid, + action_data->channel, + action_data->fd, + action_data->flags); + + schedule_action_verification(step); +} + +static void socket_connect_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step; + int status; + + *action_data->fd = -1; + + status = data->if_sock->connect(action_data->addr, + action_data->sock_type, + action_data->service_uuid, + action_data->channel, + action_data->fd, + action_data->flags); + + tester_print("status %d sock_fd %d", status, *action_data->fd); + + if (!status) + return; + + step = g_new0(struct step, 1); + step->action_status = status; + + schedule_action_verification(step); +} + +static gboolean socket_chan_cb(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + int sock_fd = g_io_channel_unix_get_fd(io); + struct step *step = g_new0(struct step, 1); + int channel, len; + + tester_print("%s", __func__); + + if (cond & G_IO_HUP) { + tester_warn("Socket %d hang up", sock_fd); + + step->action_status = BT_STATUS_FAIL; + goto done; + } + + if (cond & (G_IO_ERR | G_IO_NVAL)) { + tester_warn("Socket error: sock %d cond %d", sock_fd, cond); + + step->action_status = BT_STATUS_FAIL; + goto done; + } + + len = read(sock_fd, &channel, sizeof(channel)); + if (len != sizeof(channel)) { + tester_warn("Socket read failed"); + + step->action_status = BT_STATUS_FAIL; + goto done; + } + + tester_print("read correct channel: %d", channel); + + step->action_status = BT_STATUS_SUCCESS; + +done: + schedule_action_verification(step); + return FALSE; +} + +static void socket_read_fd_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + GIOChannel *io; + + io = g_io_channel_unix_new(*action_data->fd); + g_io_channel_set_close_on_unref(io, TRUE); + + g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + socket_chan_cb, NULL); + + g_io_channel_unref(io); +} + +static void socket_verify_fd_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + if (!*action_data->fd) { + step->action_status = BT_STATUS_FAIL; + goto done; + } + + step->action_status = (fcntl(*action_data->fd, F_GETFD) < 0) ? + BT_STATUS_FAIL : BT_STATUS_SUCCESS; + +done: + schedule_action_verification(step); +} + +static void socket_verify_channel_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + int channel, len; + struct step *step = g_new0(struct step, 1); + + if (!*action_data->fd) { + tester_warn("Ups no action_data->fd"); + + step->action_status = BT_STATUS_FAIL; + goto done; + } + + len = read(*action_data->fd, &channel, sizeof(channel)); + if (len != sizeof(channel) || channel != action_data->channel) { + tester_warn("Ups bad channel"); + + step->action_status = BT_STATUS_FAIL; + goto done; + } + + step->action_status = BT_STATUS_SUCCESS; + +done: + schedule_action_verification(step); +} + +static void socket_close_channel_action(void) +{ + struct test_data *data = tester_get_data(); + struct step *current_data_step = queue_peek_head(data->steps); + struct bt_action_data *action_data = current_data_step->set_data; + struct step *step = g_new0(struct step, 1); + + if (!*action_data->fd) { + tester_warn("Ups no action_data->fd"); + + step->action_status = BT_STATUS_FAIL; + goto done; + } + + close(*action_data->fd); + *action_data->fd = -1; + + step->action_status = BT_STATUS_SUCCESS; + +done: + schedule_action_verification(step); +} + +static struct test_case test_cases[] = { + TEST_CASE_BREDRLE("Socket Init", + ACTION_SUCCESS(dummy_action, NULL), + ), + TEST_CASE_BREDRLE("Socket Listen - Invalid: sock_type 0", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION(BT_STATUS_PARM_INVALID, socket_listen_action, + &btsock_param_socktype_0), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Listen - Invalid: sock_type L2CAP", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION(BT_STATUS_UNSUPPORTED, socket_listen_action, + &btsock_param_socktype_l2cap), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Listen - Invalid: chan, uuid", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION(BT_STATUS_PARM_INVALID, socket_listen_action, + &btsock_param_channel_0), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Listen - Check returned fd valid", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(socket_listen_action, &btsock_param), + ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Listen - Check returned channel", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(socket_listen_action, &btsock_param), + ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), + ACTION_SUCCESS(socket_verify_channel_action, &btsock_param), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Listen - Close and Listen again", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(socket_listen_action, &btsock_param), + ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), + ACTION_SUCCESS(socket_verify_channel_action, &btsock_param), + ACTION_SUCCESS(socket_close_channel_action, &btsock_param), + ACTION_SUCCESS(socket_listen_action, &btsock_param), + ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), + ACTION_SUCCESS(socket_verify_channel_action, &btsock_param), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Listen - Invalid: double Listen", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(socket_listen_action, &btsock_param), + ACTION_SUCCESS(socket_verify_fd_action, &btsock_param), + ACTION_SUCCESS(socket_verify_channel_action, &btsock_param), + ACTION(BT_STATUS_BUSY, socket_listen_action, &btsock_param), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Connect - Invalid: sock_type 0", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION(BT_STATUS_PARM_INVALID, socket_connect_action, + &btsock_param_socktype_0), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Connect - Invalid: sock_type L2CAP", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION(BT_STATUS_UNSUPPORTED, socket_connect_action, + &btsock_param_socktype_l2cap), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Connect - Invalid: chan, uuid", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION(BT_STATUS_PARM_INVALID, socket_connect_action, + &btsock_param_channel_0), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Connect - Invalid: bdaddr", + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION(BT_STATUS_PARM_INVALID, socket_connect_action, + &btsock_param_inv_bdaddr), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Connect - Check returned fd valid", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(bt_create_bond_action, + &prop_emu_remote_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 1), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, + &prop_emu_remote_bdadr, 1), + CALLBACK_DEVICE_PROPS(NULL, 0), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(emu_add_rfcomm_server_action, + &btsock_param_emu_bdaddr), + ACTION_SUCCESS(socket_connect_action, &btsock_param_emu_bdaddr), + ACTION_SUCCESS(socket_verify_fd_action, + &btsock_param_emu_bdaddr), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), + TEST_CASE_BREDRLE("Socket Connect - Check returned chann", + ACTION_SUCCESS(set_default_ssp_request_handler, NULL), + ACTION_SUCCESS(bluetooth_enable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON), + ACTION_SUCCESS(emu_setup_powered_remote_action, NULL), + ACTION_SUCCESS(emu_set_ssp_mode_action, NULL), + ACTION_SUCCESS(bt_create_bond_action, + &prop_emu_remote_bdaddr_req), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDING, + &prop_emu_remote_bdadr, 1), + CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_set, 1), + CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED, + &prop_emu_remote_bdadr, 1), + CALLBACK_DEVICE_PROPS(NULL, 0), + ACTION_SUCCESS(emu_add_l2cap_server_action, &l2cap_setup_data), + ACTION_SUCCESS(emu_add_rfcomm_server_action, + &btsock_param_emu_bdaddr), + ACTION_SUCCESS(socket_connect_action, &btsock_param_emu_bdaddr), + ACTION_SUCCESS(socket_verify_fd_action, + &btsock_param_emu_bdaddr), + ACTION_SUCCESS(socket_verify_channel_action, + &btsock_param_emu_bdaddr), + ACTION_SUCCESS(socket_read_fd_action, &btsock_param_emu_bdaddr), + ACTION_SUCCESS(bluetooth_disable_action, NULL), + CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF), + ), +}; + +struct queue *get_socket_tests(void) +{ + uint16_t i = 0; + + list = queue_new(); + + for (; i < sizeof(test_cases) / sizeof(test_cases[0]); ++i) + if (!queue_push_tail(list, &test_cases[i])) + return NULL; + + return list; +} + +void remove_socket_tests(void) +{ + queue_destroy(list, NULL); +} diff -Nru bluez-4.101/android/test-ipc.c bluez-5.23/android/test-ipc.c --- bluez-4.101/android/test-ipc.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/test-ipc.c 2014-03-11 11:20:34.000000000 +0000 @@ -0,0 +1,577 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "src/shared/util.h" +#include "src/log.h" +#include "android/ipc-common.h" +#include "android/ipc.h" + +static const char HAL_SK_PATH[] = "\0test_hal_socket"; + +#define SERVICE_ID_MAX 10 + +struct test_data { + bool disconnect; + const void *cmd; + uint16_t cmd_size; + uint8_t service; + const struct ipc_handler *handlers; + uint8_t handlers_size; +}; + +struct context { + GMainLoop *main_loop; + + int sk; + + guint source; + guint cmd_source; + guint notif_source; + + GIOChannel *cmd_io; + GIOChannel *notif_io; + + const struct test_data *data; +}; + + +static struct ipc *ipc = NULL; + +static void context_quit(struct context *context) +{ + g_main_loop_quit(context->main_loop); +} + +static gboolean cmd_watch(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + const struct ipc_hdr *sent_msg = test_data->cmd; + uint8_t buf[128]; + int sk; + + struct ipc_hdr success_resp = { + .service_id = sent_msg->service_id, + .opcode = sent_msg->opcode, + .len = 0, + }; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_assert(test_data->disconnect); + return FALSE; + } + + g_assert(!test_data->disconnect); + + sk = g_io_channel_unix_get_fd(io); + + g_assert(read(sk, buf, sizeof(buf)) == sizeof(struct ipc_hdr)); + g_assert(!memcmp(&success_resp, buf, sizeof(struct ipc_hdr))); + + context_quit(context); + + return TRUE; +} + +static gboolean notif_watch(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_assert(test_data->disconnect); + return FALSE; + } + + g_assert(!test_data->disconnect); + + return TRUE; +} + +static gboolean connect_handler(GIOChannel *io, GIOCondition cond, + gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + GIOChannel *new_io; + GIOCondition watch_cond; + int sk; + + if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_assert(FALSE); + return FALSE; + } + + g_assert(!context->cmd_source || !context->notif_source); + + sk = accept(context->sk, NULL, NULL); + g_assert(sk >= 0); + + new_io = g_io_channel_unix_new(sk); + + watch_cond = G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL; + + if (context->cmd_source && !context->notif_source) { + context->notif_source = g_io_add_watch(new_io, watch_cond, + notif_watch, context); + g_assert(context->notif_source > 0); + context->notif_io = new_io; + } + + if (!context->cmd_source) { + context->cmd_source = g_io_add_watch(new_io, watch_cond, + cmd_watch, context); + context->cmd_io = new_io; + } + + if (context->cmd_source && context->notif_source && !test_data->cmd) + context_quit(context); + + return TRUE; +} + +static struct context *create_context(gconstpointer data) +{ + struct context *context = g_new0(struct context, 1); + struct sockaddr_un addr; + GIOChannel *io; + int ret, sk; + + context->main_loop = g_main_loop_new(NULL, FALSE); + g_assert(context->main_loop); + + sk = socket(AF_LOCAL, SOCK_SEQPACKET, 0); + g_assert(sk >= 0); + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + memcpy(addr.sun_path, HAL_SK_PATH, sizeof(HAL_SK_PATH)); + + ret = bind(sk, (struct sockaddr *) &addr, sizeof(addr)); + g_assert(ret == 0); + + ret = listen(sk, 5); + g_assert(ret == 0); + + io = g_io_channel_unix_new(sk); + + g_io_channel_set_close_on_unref(io, TRUE); + + context->source = g_io_add_watch(io, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + connect_handler, context); + g_assert(context->source > 0); + + g_io_channel_unref(io); + + context->sk = sk; + context->data = data; + + return context; +} + +static void execute_context(struct context *context) +{ + g_main_loop_run(context->main_loop); + + g_io_channel_shutdown(context->notif_io, TRUE, NULL); + g_io_channel_shutdown(context->cmd_io, TRUE, NULL); + g_io_channel_unref(context->cmd_io); + g_io_channel_unref(context->notif_io); + + g_source_remove(context->notif_source); + g_source_remove(context->cmd_source); + g_source_remove(context->source); + + g_main_loop_unref(context->main_loop); + + g_free(context); +} + +static void disconnected(void *data) +{ + struct context *context = data; + + g_assert(context->data->disconnect); + + context_quit(context); +} + +static void test_init(gconstpointer data) +{ + struct context *context = create_context(data); + + ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, + true, NULL, NULL); + + g_assert(ipc); + + execute_context(context); + + ipc_cleanup(ipc); + ipc = NULL; +} + +static gboolean send_cmd(gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + int sk; + + sk = g_io_channel_unix_get_fd(context->cmd_io); + g_assert(sk >= 0); + + g_assert(write(sk, test_data->cmd, test_data->cmd_size) == + test_data->cmd_size); + + return FALSE; +} + +static gboolean register_service(gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + + ipc_register(ipc, test_data->service, test_data->handlers, + test_data->handlers_size); + + return FALSE; +} + +static gboolean unregister_service(gpointer user_data) +{ + struct context *context = user_data; + const struct test_data *test_data = context->data; + + ipc_unregister(ipc, test_data->service); + + return FALSE; +} + +static void test_cmd(gconstpointer data) +{ + struct context *context = create_context(data); + + ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, + true, disconnected, context); + + g_assert(ipc); + + g_idle_add(send_cmd, context); + + execute_context(context); + + ipc_cleanup(ipc); + ipc = NULL; +} + +static void test_cmd_reg(gconstpointer data) +{ + struct context *context = create_context(data); + const struct test_data *test_data = context->data; + + ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, + true, disconnected, context); + + g_assert(ipc); + + g_idle_add(register_service, context); + g_idle_add(send_cmd, context); + + execute_context(context); + + ipc_unregister(ipc, test_data->service); + + ipc_cleanup(ipc); + ipc = NULL; +} + +static void test_cmd_reg_1(gconstpointer data) +{ + struct context *context = create_context(data); + + ipc = ipc_init(HAL_SK_PATH, sizeof(HAL_SK_PATH), SERVICE_ID_MAX, + true, disconnected, context); + + g_assert(ipc); + + g_idle_add(register_service, context); + g_idle_add(unregister_service, context); + g_idle_add(send_cmd, context); + + execute_context(context); + + ipc_cleanup(ipc); + ipc = NULL; +} + +static void test_cmd_handler_1(const void *buf, uint16_t len) +{ + ipc_send_rsp(ipc, 0, 1, 0); +} + +static void test_cmd_handler_2(const void *buf, uint16_t len) +{ + ipc_send_rsp(ipc, 0, 2, 0); +} + +static void test_cmd_handler_invalid(const void *buf, uint16_t len) +{ + g_assert(false); +} + +static const struct test_data test_init_1 = {}; + +static const struct ipc_hdr test_cmd_1_hdr = { + .service_id = 0, + .opcode = 1, + .len = 0 +}; + +static const struct ipc_hdr test_cmd_2_hdr = { + .service_id = 0, + .opcode = 2, + .len = 0 +}; + +static const struct test_data test_cmd_service_invalid_1 = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr), + .disconnect = true, +}; + +static const struct ipc_handler cmd_handlers[] = { + { test_cmd_handler_1, false, 0 } +}; + +static const struct test_data test_cmd_service_valid_1 = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1 +}; + +static const struct test_data test_cmd_service_invalid_2 = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct ipc_handler cmd_handlers_invalid_2[] = { + { test_cmd_handler_1, false, 0 }, + { test_cmd_handler_invalid, false, 0 } +}; + +static const struct ipc_handler cmd_handlers_invalid_1[] = { + { test_cmd_handler_invalid, false, 0 }, + { test_cmd_handler_2, false, 0 }, +}; + +static const struct test_data test_cmd_opcode_valid_1 = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr), + .service = 0, + .handlers = cmd_handlers_invalid_2, + .handlers_size = 2, +}; + +static const struct test_data test_cmd_opcode_valid_2 = { + .cmd = &test_cmd_2_hdr, + .cmd_size = sizeof(test_cmd_2_hdr), + .service = 0, + .handlers = cmd_handlers_invalid_1, + .handlers_size = 2, +}; + +static const struct test_data test_cmd_opcode_invalid_1 = { + .cmd = &test_cmd_2_hdr, + .cmd_size = sizeof(test_cmd_2_hdr), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct test_data test_cmd_hdr_invalid = { + .cmd = &test_cmd_1_hdr, + .cmd_size = sizeof(test_cmd_1_hdr) - 1, + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +#define VARDATA_EX1 "some data example" + +struct vardata { + struct ipc_hdr hdr; + uint8_t data[IPC_MTU - sizeof(struct ipc_hdr)]; +} __attribute__((packed)); + +static const struct vardata test_cmd_vardata = { + .hdr.service_id = 0, + .hdr.opcode = 1, + .hdr.len = sizeof(VARDATA_EX1), + .data = VARDATA_EX1, +}; + +static const struct ipc_handler cmd_vardata_handlers[] = { + { test_cmd_handler_1, true, sizeof(VARDATA_EX1) } +}; + +static const struct test_data test_cmd_vardata_valid = { + .cmd = &test_cmd_vardata, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), + .service = 0, + .handlers = cmd_vardata_handlers, + .handlers_size = 1, +}; + +static const struct ipc_handler cmd_vardata_handlers_valid2[] = { + { test_cmd_handler_1, true, sizeof(VARDATA_EX1) - 1 } +}; + +static const struct test_data test_cmd_vardata_valid_2 = { + .cmd = &test_cmd_vardata, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), + .service = 0, + .handlers = cmd_vardata_handlers_valid2, + .handlers_size = 1, +}; + +static const struct test_data test_cmd_vardata_invalid_1 = { + .cmd = &test_cmd_vardata, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1, + .service = 0, + .handlers = cmd_vardata_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct ipc_hdr test_cmd_service_offrange_hdr = { + .service_id = SERVICE_ID_MAX + 1, + .opcode = 1, + .len = 0 +}; + +static const struct test_data test_cmd_service_offrange = { + .cmd = &test_cmd_service_offrange_hdr, + .cmd_size = sizeof(struct ipc_hdr), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct vardata test_cmd_invalid_data_1 = { + .hdr.service_id = 0, + .hdr.opcode = 1, + .hdr.len = sizeof(VARDATA_EX1), + .data = VARDATA_EX1, +}; + +static const struct test_data test_cmd_msg_invalid_1 = { + .cmd = &test_cmd_invalid_data_1, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1) - 1, + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +static const struct vardata test_cmd_invalid_data_2 = { + .hdr.service_id = 0, + .hdr.opcode = 1, + .hdr.len = sizeof(VARDATA_EX1) - 1, + .data = VARDATA_EX1, +}; + +static const struct test_data test_cmd_msg_invalid_2 = { + .cmd = &test_cmd_invalid_data_2, + .cmd_size = sizeof(struct ipc_hdr) + sizeof(VARDATA_EX1), + .service = 0, + .handlers = cmd_handlers, + .handlers_size = 1, + .disconnect = true, +}; + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + + if (g_test_verbose()) + __btd_log_init("*", 0); + + g_test_add_data_func("/android_ipc/init", &test_init_1, test_init); + g_test_add_data_func("/android_ipc/service_invalid_1", + &test_cmd_service_invalid_1, test_cmd); + g_test_add_data_func("/android_ipc/service_valid_1", + &test_cmd_service_valid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/service_invalid_2", + &test_cmd_service_invalid_2, test_cmd_reg_1); + g_test_add_data_func("/android_ipc/opcode_valid_1", + &test_cmd_opcode_valid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/opcode_valid_2", + &test_cmd_opcode_valid_2, test_cmd_reg); + g_test_add_data_func("/android_ipc/opcode_invalid_1", + &test_cmd_opcode_invalid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/vardata_valid", + &test_cmd_vardata_valid, test_cmd_reg); + g_test_add_data_func("/android_ipc/vardata_valid_2", + &test_cmd_vardata_valid_2, test_cmd_reg); + g_test_add_data_func("/android_ipc/vardata_invalid_1", + &test_cmd_vardata_invalid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/service_offrange", + &test_cmd_service_offrange, test_cmd_reg); + g_test_add_data_func("/android_ipc/hdr_invalid", + &test_cmd_hdr_invalid, test_cmd_reg); + g_test_add_data_func("/android_ipc/msg_invalid_1", + &test_cmd_msg_invalid_1, test_cmd_reg); + g_test_add_data_func("/android_ipc/msg_invalid_2", + &test_cmd_msg_invalid_2, test_cmd_reg); + + return g_test_run(); +} diff -Nru bluez-4.101/android/utils.h bluez-5.23/android/utils.h --- bluez-4.101/android/utils.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/android/utils.h 2014-02-22 01:42:17.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2013-2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +static inline void android2bdaddr(const void *buf, bdaddr_t *dst) +{ + baswap(dst, buf); +} + +static inline void bdaddr2android(const bdaddr_t *src, void *buf) +{ + baswap(buf, src); +} diff -Nru bluez-4.101/attrib/att.c bluez-5.23/attrib/att.c --- bluez-4.101/attrib/att.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/att.c 2014-06-20 18:33:13.000000000 +0000 @@ -22,17 +22,31 @@ * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include #include -#include #include +#include "src/shared/util.h" +#include "lib/uuid.h" #include "att.h" +static inline void put_uuid_le(const bt_uuid_t *src, void *dst) +{ + if (src->type == BT_UUID16) + put_le16(src->value.u16, dst); + else + /* Convert from 128-bit BE to LE */ + bswap_128(&src->value.u128, dst); +} + const char *att_ecode2str(uint8_t status) { switch (status) { @@ -101,6 +115,9 @@ struct att_data_list *list; int i; + if (len > UINT8_MAX) + return NULL; + list = g_new0(struct att_data_list, 1); list->len = len; list->num = num; @@ -113,38 +130,51 @@ return list; } +static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid) +{ + if (type == BT_UUID16) + bt_uuid16_create(uuid, get_le16(val)); + else { + uint128_t u128; + + /* Convert from 128-bit LE to BE */ + bswap_128(val, &u128); + bt_uuid128_create(uuid, u128); + } +} + uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, - uint8_t *pdu, int len) + uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); - uint16_t length; + uint16_t uuid_len; if (!uuid) return 0; if (uuid->type == BT_UUID16) - length = 2; + uuid_len = 2; else if (uuid->type == BT_UUID128) - length = 16; + uuid_len = 16; else return 0; - if (len < min_len + length) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BY_GROUP_REQ; - att_put_u16(start, &pdu[1]); - att_put_u16(end, &pdu[3]); - - att_put_uuid(*uuid, &pdu[5]); + /* Starting Handle (2 octets) */ + put_le16(start, &pdu[1]); + /* Ending Handle (2 octets) */ + put_le16(end, &pdu[3]); + /* Attribute Group Type (2 or 16 octet UUID) */ + put_uuid_le(uuid, &pdu[5]); - return min_len + length; + return 5 + uuid_len; } -uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start, +uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); + const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); + uint8_t type; if (pdu == NULL) return 0; @@ -155,21 +185,23 @@ if (pdu[0] != ATT_OP_READ_BY_GROUP_REQ) return 0; - if (len < min_len + 2) + if (len == (min_len + 2)) + type = BT_UUID16; + else if (len == (min_len + 16)) + type = BT_UUID128; + else return 0; - *start = att_get_u16(&pdu[1]); - *end = att_get_u16(&pdu[3]); - if (len == min_len + 2) - *uuid = att_get_uuid16(&pdu[5]); - else - *uuid = att_get_uuid128(&pdu[5]); + *start = get_le16(&pdu[1]); + *end = get_le16(&pdu[3]); + + get_uuid(type, &pdu[5], uuid); return len; } uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, - int len) + size_t len) { int i; uint16_t w; @@ -178,7 +210,7 @@ if (list == NULL) return 0; - if (len < list->len + 2) + if (len < list->len + sizeof(uint8_t) * 2) return 0; pdu[0] = ATT_OP_READ_BY_GROUP_RESP; @@ -195,7 +227,7 @@ return w; } -struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len) +struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len) { struct att_data_list *list; const uint8_t *ptr; @@ -205,9 +237,29 @@ if (pdu[0] != ATT_OP_READ_BY_GROUP_RESP) return NULL; + /* PDU must contain at least: + * - Attribute Opcode (1 octet) + * - Length (1 octet) + * - Attribute Data List (at least one entry): + * - Attribute Handle (2 octets) + * - End Group Handle (2 octets) + * - Attribute Value (at least 1 octet) */ + if (len < 7) + return NULL; + elen = pdu[1]; + /* Minimum Attribute Data List size */ + if (elen < 5) + return NULL; + + /* Reject incomplete Attribute Data List */ + if ((len - 2) % elen) + return NULL; + num = (len - 2) / elen; list = att_data_list_alloc(num, elen); + if (list == NULL) + return NULL; ptr = &pdu[2]; @@ -220,7 +272,8 @@ } uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, - const uint8_t *value, int vlen, uint8_t *pdu, int len) + const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len) { uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end) + sizeof(uint16_t); @@ -234,16 +287,13 @@ if (uuid->type != BT_UUID16) return 0; - if (len < min_len) - return 0; - if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_FIND_BY_TYPE_REQ; - att_put_u16(start, &pdu[1]); - att_put_u16(end, &pdu[3]); - att_put_uuid16(*uuid, &pdu[5]); + put_le16(start, &pdu[1]); + put_le16(end, &pdu[3]); + put_le16(uuid->value.u16, &pdu[5]); if (vlen > 0) { memcpy(&pdu[7], value, vlen); @@ -253,83 +303,84 @@ return min_len; } -uint16_t dec_find_by_type_req(const uint8_t *pdu, int len, uint16_t *start, - uint16_t *end, bt_uuid_t *uuid, uint8_t *value, int *vlen) +uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, + uint16_t *end, bt_uuid_t *uuid, + uint8_t *value, size_t *vlen) { - int valuelen; - uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + - sizeof(*end) + sizeof(uint16_t); - if (pdu == NULL) return 0; - if (len < min_len) + if (len < 7) return 0; + /* Attribute Opcode (1 octet) */ if (pdu[0] != ATT_OP_FIND_BY_TYPE_REQ) return 0; - /* First requested handle number */ - if (start) - *start = att_get_u16(&pdu[1]); - - /* Last requested handle number */ - if (end) - *end = att_get_u16(&pdu[3]); - - /* Always UUID16 */ - if (uuid) - *uuid = att_get_uuid16(&pdu[5]); - - valuelen = len - min_len; + /* First requested handle number (2 octets) */ + *start = get_le16(&pdu[1]); + /* Last requested handle number (2 octets) */ + *end = get_le16(&pdu[3]); + /* 16-bit UUID to find (2 octets) */ + bt_uuid16_create(uuid, get_le16(&pdu[5])); /* Attribute value to find */ - if (valuelen > 0 && value) - memcpy(value, pdu + min_len, valuelen); - - if (vlen) - *vlen = valuelen; + *vlen = len - 7; + if (*vlen > 0) + memcpy(value, pdu + 7, *vlen); return len; } -uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, int len) +uint16_t enc_find_by_type_resp(GSList *matches, uint8_t *pdu, size_t len) { GSList *l; uint16_t offset; - if (pdu == NULL || len < 5) + if (!pdu) return 0; pdu[0] = ATT_OP_FIND_BY_TYPE_RESP; - for (l = matches, offset = 1; l && len >= (offset + 4); - l = l->next, offset += 4) { + for (l = matches, offset = 1; + l && len >= (offset + sizeof(uint16_t) * 2); + l = l->next, offset += sizeof(uint16_t) * 2) { struct att_range *range = l->data; - att_put_u16(range->start, &pdu[offset]); - att_put_u16(range->end, &pdu[offset + 2]); + put_le16(range->start, &pdu[offset]); + put_le16(range->end, &pdu[offset + 2]); } return offset; } -GSList *dec_find_by_type_resp(const uint8_t *pdu, int len) +GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len) { struct att_range *range; GSList *matches; - int offset; + off_t offset; + /* PDU should contain at least: + * - Attribute Opcode (1 octet) + * - Handles Information List (at least one entry): + * - Found Attribute Handle (2 octets) + * - Group End Handle (2 octets) */ if (pdu == NULL || len < 5) return NULL; if (pdu[0] != ATT_OP_FIND_BY_TYPE_RESP) return NULL; - for (offset = 1, matches = NULL; len >= (offset + 4); offset += 4) { + /* Reject incomplete Handles Information List */ + if ((len - 1) % 4) + return NULL; + + for (offset = 1, matches = NULL; + len >= (offset + sizeof(uint16_t) * 2); + offset += sizeof(uint16_t) * 2) { range = g_new0(struct att_range, 1); - range->start = att_get_u16(&pdu[offset]); - range->end = att_get_u16(&pdu[offset + 2]); + range->start = get_le16(&pdu[offset]); + range->end = get_le16(&pdu[offset + 2]); matches = g_slist_append(matches, range); } @@ -338,37 +389,37 @@ } uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, - uint8_t *pdu, int len) + uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); - uint16_t length; + uint16_t uuid_len; if (!uuid) return 0; if (uuid->type == BT_UUID16) - length = 2; + uuid_len = 2; else if (uuid->type == BT_UUID128) - length = 16; + uuid_len = 16; else return 0; - if (len < min_len + length) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BY_TYPE_REQ; - att_put_u16(start, &pdu[1]); - att_put_u16(end, &pdu[3]); + /* Starting Handle (2 octets) */ + put_le16(start, &pdu[1]); + /* Ending Handle (2 octets) */ + put_le16(end, &pdu[3]); + /* Attribute Type (2 or 16 octet UUID) */ + put_uuid_le(uuid, &pdu[5]); - att_put_uuid(*uuid, &pdu[5]); - - return min_len + length; + return 5 + uuid_len; } -uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start, +uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end, bt_uuid_t *uuid) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); + const size_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); + uint8_t type; if (pdu == NULL) return 0; @@ -376,27 +427,29 @@ if (start == NULL || end == NULL || uuid == NULL) return 0; - if (len < min_len + 2) + if (len == (min_len + 2)) + type = BT_UUID16; + else if (len == (min_len + 16)) + type = BT_UUID128; + else return 0; if (pdu[0] != ATT_OP_READ_BY_TYPE_REQ) return 0; - *start = att_get_u16(&pdu[1]); - *end = att_get_u16(&pdu[3]); + *start = get_le16(&pdu[1]); + *end = get_le16(&pdu[3]); - if (len == min_len + 2) - *uuid = att_get_uuid16(&pdu[5]); - else - *uuid = att_get_uuid128(&pdu[5]); + get_uuid(type, &pdu[5], uuid); return len; } -uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, int len) +uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, + size_t len) { uint8_t *ptr; - int i, w, l; + size_t i, w, l; if (list == NULL) return 0; @@ -419,7 +472,7 @@ return w; } -struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len) +struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len) { struct att_data_list *list; const uint8_t *ptr; @@ -429,9 +482,28 @@ if (pdu[0] != ATT_OP_READ_BY_TYPE_RESP) return NULL; + /* PDU must contain at least: + * - Attribute Opcode (1 octet) + * - Length (1 octet) + * - Attribute Data List (at least one entry): + * - Attribute Handle (2 octets) + * - Attribute Value (at least 1 octet) */ + if (len < 5) + return NULL; + elen = pdu[1]; + /* Minimum Attribute Data List size */ + if (elen < 3) + return NULL; + + /* Reject incomplete Attribute Data List */ + if ((len - 2) % elen) + return NULL; + num = (len - 2) / elen; list = att_data_list_alloc(num, elen); + if (list == NULL) + return NULL; ptr = &pdu[2]; @@ -443,22 +515,19 @@ return list; } -uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen, - uint8_t *pdu, int len) +uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); if (pdu == NULL) return 0; - if (len < min_len) - return 0; - if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_WRITE_CMD; - att_put_u16(handle, &pdu[1]); + put_le16(handle, &pdu[1]); if (vlen > 0) { memcpy(&pdu[3], value, vlen); @@ -468,8 +537,8 @@ return min_len; } -uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle, - uint8_t *value, int *vlen) +uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle, + uint8_t *value, size_t *vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle); @@ -485,29 +554,84 @@ if (pdu[0] != ATT_OP_WRITE_CMD) return 0; - *handle = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); memcpy(value, pdu + min_len, len - min_len); *vlen = len - min_len; return len; } -uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen, - uint8_t *pdu, int len) +uint16_t enc_signed_write_cmd(uint16_t handle, const uint8_t *value, + size_t vlen, struct bt_crypto *crypto, + const uint8_t csrk[16], + uint32_t sign_cnt, + uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); + const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(handle); + const uint16_t min_len = hdr_len + ATT_SIGNATURE_LEN; + + if (pdu == NULL) + return 0; + + if (vlen > len - min_len) + vlen = len - min_len; + + pdu[0] = ATT_OP_SIGNED_WRITE_CMD; + put_le16(handle, &pdu[1]); + + if (vlen > 0) + memcpy(&pdu[hdr_len], value, vlen); + + if (!bt_crypto_sign_att(crypto, csrk, pdu, hdr_len + vlen, sign_cnt, + &pdu[hdr_len + vlen])) + return 0; + + return min_len + vlen; +} + +uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len, + uint16_t *handle, + uint8_t *value, size_t *vlen, + uint8_t signature[12]) +{ + const uint16_t hdr_len = sizeof(pdu[0]) + sizeof(*handle); + const uint16_t min_len = hdr_len + ATT_SIGNATURE_LEN; + if (pdu == NULL) return 0; + if (value == NULL || vlen == NULL || handle == NULL) + return 0; + if (len < min_len) return 0; + if (pdu[0] != ATT_OP_SIGNED_WRITE_CMD) + return 0; + + *vlen = len - min_len; + *handle = get_le16(&pdu[1]); + memcpy(value, pdu + hdr_len, *vlen); + + memcpy(signature, pdu + hdr_len + *vlen, ATT_SIGNATURE_LEN); + + return len; +} + +uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len) +{ + const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); + + if (pdu == NULL) + return 0; + if (vlen > len - min_len) vlen = len - min_len; pdu[0] = ATT_OP_WRITE_REQ; - att_put_u16(handle, &pdu[1]); + put_le16(handle, &pdu[1]); if (vlen > 0) { memcpy(&pdu[3], value, vlen); @@ -517,8 +641,8 @@ return min_len; } -uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle, - uint8_t *value, int *vlen) +uint16_t dec_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, + uint8_t *value, size_t *vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle); @@ -534,7 +658,7 @@ if (pdu[0] != ATT_OP_WRITE_REQ) return 0; - *handle = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); *vlen = len - min_len; if (*vlen > 0) memcpy(value, pdu + min_len, *vlen); @@ -542,7 +666,7 @@ return len; } -uint16_t enc_write_resp(uint8_t *pdu, int len) +uint16_t enc_write_resp(uint8_t *pdu) { if (pdu == NULL) return 0; @@ -552,7 +676,7 @@ return sizeof(pdu[0]); } -uint16_t dec_write_resp(const uint8_t *pdu, int len) +uint16_t dec_write_resp(const uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; @@ -563,42 +687,36 @@ return len; } -uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len) +uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_REQ; - att_put_u16(handle, &pdu[1]); + /* Attribute Handle (2 octets) */ + put_le16(handle, &pdu[1]); - return min_len; + return 3; } uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu, - int len) + size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) + - sizeof(offset); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_READ_BLOB_REQ; - att_put_u16(handle, &pdu[1]); - att_put_u16(offset, &pdu[3]); + /* Attribute Handle (2 octets) */ + put_le16(handle, &pdu[1]); + /* Value Offset (2 octets) */ + put_le16(offset, &pdu[3]); - return min_len; + return 5; } -uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle) +uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle); @@ -614,12 +732,12 @@ if (pdu[0] != ATT_OP_READ_REQ) return 0; - *handle = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); return min_len; } -uint16_t dec_read_blob_req(const uint8_t *pdu, int len, uint16_t *handle, +uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) + @@ -640,13 +758,13 @@ if (pdu[0] != ATT_OP_READ_BLOB_REQ) return 0; - *handle = att_get_u16(&pdu[1]); - *offset = att_get_u16(&pdu[3]); + *handle = get_le16(&pdu[1]); + *offset = get_le16(&pdu[3]); return min_len; } -uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len) +uint16_t enc_read_resp(uint8_t *value, size_t vlen, uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; @@ -664,8 +782,8 @@ return vlen + 1; } -uint16_t enc_read_blob_resp(uint8_t *value, int vlen, uint16_t offset, - uint8_t *pdu, int len) +uint16_t enc_read_blob_resp(uint8_t *value, size_t vlen, uint16_t offset, + uint8_t *pdu, size_t len) { if (pdu == NULL) return 0; @@ -681,61 +799,58 @@ return vlen + 1; } -uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen) +ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value, + size_t vlen) { if (pdu == NULL) - return 0; - - if (value == NULL || vlen == NULL) - return 0; + return -EINVAL; if (pdu[0] != ATT_OP_READ_RESP) - return 0; + return -EINVAL; - memcpy(value, pdu + 1, len - 1); + if (value == NULL) + return len - 1; - *vlen = len - 1; + if (vlen < (len - 1)) + return -ENOBUFS; - return len; + memcpy(value, pdu + 1, len - 1); + + return len - 1; } uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status, - uint8_t *pdu, int len) + uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(opcode) + - sizeof(handle) + sizeof(status); - uint16_t u16; - - if (len < min_len) - return 0; - - u16 = htobs(handle); + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_ERROR; + /* Request Opcode In Error (1 octet) */ pdu[1] = opcode; - memcpy(&pdu[2], &u16, sizeof(u16)); + /* Attribute Handle In Error (2 octets) */ + put_le16(handle, &pdu[2]); + /* Error Code (1 octet) */ pdu[4] = status; - return min_len; + return 5; } -uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len) +uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, + size_t len) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(start) + sizeof(end); - if (pdu == NULL) return 0; - if (len < min_len) - return 0; - + /* Attribute Opcode (1 octet) */ pdu[0] = ATT_OP_FIND_INFO_REQ; - att_put_u16(start, &pdu[1]); - att_put_u16(end, &pdu[3]); + /* Starting Handle (2 octets) */ + put_le16(start, &pdu[1]); + /* Ending Handle (2 octets) */ + put_le16(end, &pdu[3]); - return min_len; + return 5; } -uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start, +uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*start) + sizeof(*end); @@ -752,17 +867,17 @@ if (pdu[0] != ATT_OP_FIND_INFO_REQ) return 0; - *start = att_get_u16(&pdu[1]); - *end = att_get_u16(&pdu[3]); + *start = get_le16(&pdu[1]); + *end = get_le16(&pdu[3]); return min_len; } uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list, - uint8_t *pdu, int len) + uint8_t *pdu, size_t len) { uint8_t *ptr; - int i, w; + size_t i, w; if (pdu == NULL) return 0; @@ -770,7 +885,7 @@ if (list == NULL) return 0; - if (len < list->len + 2) + if (len < list->len + sizeof(uint8_t) * 2) return 0; pdu[0] = ATT_OP_FIND_INFO_RESP; @@ -786,7 +901,7 @@ return w; } -struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len, +struct att_data_list *dec_find_info_resp(const uint8_t *pdu, size_t len, uint8_t *format) { struct att_data_list *list; @@ -815,6 +930,8 @@ ptr = (void *) &pdu[2]; list = att_data_list_alloc(num, elen); + if (list == NULL) + return NULL; for (i = 0; i < num; i++) { memcpy(list->data[i], ptr, list->len); @@ -824,8 +941,8 @@ return list; } -uint16_t enc_notification(uint16_t handle, uint8_t *value, int vlen, - uint8_t *pdu, int len) +uint16_t enc_notification(uint16_t handle, uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); @@ -836,14 +953,14 @@ return 0; pdu[0] = ATT_OP_HANDLE_NOTIFY; - att_put_u16(handle, &pdu[1]); + put_le16(handle, &pdu[1]); memcpy(&pdu[3], value, vlen); return vlen + min_len; } -uint16_t enc_indication(uint16_t handle, uint8_t *value, int vlen, - uint8_t *pdu, int len) +uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); @@ -854,14 +971,14 @@ return 0; pdu[0] = ATT_OP_HANDLE_IND; - att_put_u16(handle, &pdu[1]); + put_le16(handle, &pdu[1]); memcpy(&pdu[3], value, vlen); return vlen + min_len; } -uint16_t dec_indication(const uint8_t *pdu, int len, uint16_t *handle, - uint8_t *value, int vlen) +uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle, + uint8_t *value, size_t vlen) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); uint16_t dlen; @@ -878,45 +995,72 @@ dlen = MIN(len - min_len, vlen); if (handle) - *handle = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); memcpy(value, &pdu[3], dlen); return dlen; } -uint16_t enc_confirmation(uint8_t *pdu, int len) +uint16_t enc_confirmation(uint8_t *pdu, size_t len) { - const uint16_t min_len = sizeof(pdu[0]); - if (pdu == NULL) return 0; - if (len < min_len) + /* Attribute Opcode (1 octet) */ + pdu[0] = ATT_OP_HANDLE_CNF; + + return 1; +} + +uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len) +{ + if (pdu == NULL) return 0; - pdu[0] = ATT_OP_HANDLE_CNF; + /* Attribute Opcode (1 octet) */ + pdu[0] = ATT_OP_MTU_REQ; + /* Client Rx MTU (2 octets) */ + put_le16(mtu, &pdu[1]); - return min_len; + return 3; } -uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len) +uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu); + const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu); if (pdu == NULL) return 0; + if (mtu == NULL) + return 0; + if (len < min_len) return 0; - pdu[0] = ATT_OP_MTU_REQ; - att_put_u16(mtu, &pdu[1]); + if (pdu[0] != ATT_OP_MTU_REQ) + return 0; + + *mtu = get_le16(&pdu[1]); return min_len; } -uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu) +uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len) +{ + if (pdu == NULL) + return 0; + + /* Attribute Opcode (1 octet) */ + pdu[0] = ATT_OP_MTU_RESP; + /* Server Rx MTU (2 octets) */ + put_le16(mtu, &pdu[1]); + + return 3; +} + +uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu) { const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu); @@ -929,47 +1073,179 @@ if (len < min_len) return 0; - if (pdu[0] != ATT_OP_MTU_REQ) + if (pdu[0] != ATT_OP_MTU_RESP) + return 0; + + *mtu = get_le16(&pdu[1]); + + return min_len; +} + +uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset, + const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len) +{ + const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) + + sizeof(offset); + + if (pdu == NULL) return 0; - *mtu = att_get_u16(&pdu[1]); + if (vlen > len - min_len) + vlen = len - min_len; + + pdu[0] = ATT_OP_PREP_WRITE_REQ; + put_le16(handle, &pdu[1]); + put_le16(offset, &pdu[3]); + + if (vlen > 0) { + memcpy(&pdu[5], value, vlen); + return min_len + vlen; + } return min_len; } -uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len) +uint16_t dec_prep_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, + uint16_t *offset, uint8_t *value, size_t *vlen) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(mtu); + const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) + + sizeof(*offset); if (pdu == NULL) return 0; + if (handle == NULL || offset == NULL || value == NULL || vlen == NULL) + return 0; + if (len < min_len) return 0; - pdu[0] = ATT_OP_MTU_RESP; - att_put_u16(mtu, &pdu[1]); + if (pdu[0] != ATT_OP_PREP_WRITE_REQ) + return 0; + + *handle = get_le16(&pdu[1]); + *offset = get_le16(&pdu[3]); + + *vlen = len - min_len; + if (*vlen > 0) + memcpy(value, pdu + min_len, *vlen); + + return len; +} + +uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset, + const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len) +{ + const uint16_t min_len = sizeof(pdu[0]) + sizeof(handle) + + sizeof(offset); + + if (pdu == NULL) + return 0; + + if (vlen > len - min_len) + vlen = len - min_len; + + pdu[0] = ATT_OP_PREP_WRITE_RESP; + put_le16(handle, &pdu[1]); + put_le16(offset, &pdu[3]); + + if (vlen > 0) { + memcpy(&pdu[5], value, vlen); + return min_len + vlen; + } return min_len; } -uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu) +uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle, + uint16_t *offset, uint8_t *value, size_t *vlen) { - const uint16_t min_len = sizeof(pdu[0]) + sizeof(*mtu); + const uint16_t min_len = sizeof(pdu[0]) + sizeof(*handle) + + sizeof(*offset); if (pdu == NULL) return 0; - if (mtu == NULL) + if (handle == NULL || offset == NULL || value == NULL || vlen == NULL) return 0; if (len < min_len) return 0; - if (pdu[0] != ATT_OP_MTU_RESP) + if (pdu[0] != ATT_OP_PREP_WRITE_REQ) return 0; - *mtu = att_get_u16(&pdu[1]); + *handle = get_le16(&pdu[1]); + *offset = get_le16(&pdu[3]); + *vlen = len - min_len; + if (*vlen > 0) + memcpy(value, pdu + min_len, *vlen); + + return len; +} + +uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len) +{ + if (pdu == NULL) + return 0; + + if (flags > 1) + return 0; + + /* Attribute Opcode (1 octet) */ + pdu[0] = ATT_OP_EXEC_WRITE_REQ; + /* Flags (1 octet) */ + pdu[1] = flags; + + return 2; +} + +uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags) +{ + const uint16_t min_len = sizeof(pdu[0]) + sizeof(*flags); + + if (pdu == NULL) + return 0; + + if (flags == NULL) + return 0; + + if (len < min_len) + return 0; + + if (pdu[0] != ATT_OP_EXEC_WRITE_REQ) + return 0; + + *flags = pdu[1]; return min_len; } + +uint16_t enc_exec_write_resp(uint8_t *pdu) +{ + if (pdu == NULL) + return 0; + + /* Attribute Opcode (1 octet) */ + pdu[0] = ATT_OP_EXEC_WRITE_RESP; + + return 1; +} + +uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len) +{ + const uint16_t min_len = sizeof(pdu[0]); + + if (pdu == NULL) + return 0; + + if (len < min_len) + return 0; + + if (pdu[0] != ATT_OP_EXEC_WRITE_RESP) + return 0; + + return len; +} diff -Nru bluez-4.101/attrib/att-database.h bluez-5.23/attrib/att-database.h --- bluez-4.101/attrib/att-database.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/att-database.h 2012-12-24 17:46:54.000000000 +0000 @@ -31,13 +31,13 @@ struct attribute { uint16_t handle; bt_uuid_t uuid; - int read_reqs; - int write_reqs; + int read_req; /* Read requirement */ + int write_req; /* Write requirement */ uint8_t (*read_cb)(struct attribute *a, struct btd_device *device, gpointer user_data); uint8_t (*write_cb)(struct attribute *a, struct btd_device *device, gpointer user_data); gpointer cb_user_data; - int len; + size_t len; uint8_t *data; }; diff -Nru bluez-4.101/attrib/att.h bluez-5.23/attrib/att.h --- bluez-4.101/attrib/att.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/att.h 2014-06-20 18:33:13.000000000 +0000 @@ -22,6 +22,11 @@ * */ +#include "src/shared/crypto.h" + +/* Len of signature in write signed packet */ +#define ATT_SIGNATURE_LEN 12 + /* Attribute Protocol Opcodes */ #define ATT_OP_ERROR 0x01 #define ATT_OP_MTU_REQ 0x02 @@ -75,23 +80,21 @@ #define ATT_ECODE_TIMEOUT 0x81 #define ATT_ECODE_ABORTED 0x82 -/* Characteristic Property bit field */ -#define ATT_CHAR_PROPER_BROADCAST 0x01 -#define ATT_CHAR_PROPER_READ 0x02 -#define ATT_CHAR_PROPER_WRITE_WITHOUT_RESP 0x04 -#define ATT_CHAR_PROPER_WRITE 0x08 -#define ATT_CHAR_PROPER_NOTIFY 0x10 -#define ATT_CHAR_PROPER_INDICATE 0x20 -#define ATT_CHAR_PROPER_AUTH 0x40 -#define ATT_CHAR_PROPER_EXT_PROPER 0x80 - -#define ATT_MAX_MTU 256 +#define ATT_MAX_VALUE_LEN 512 #define ATT_DEFAULT_L2CAP_MTU 48 #define ATT_DEFAULT_LE_MTU 23 #define ATT_CID 4 #define ATT_PSM 31 +/* Flags for Execute Write Request Operation */ +#define ATT_CANCEL_ALL_PREP_WRITES 0x00 +#define ATT_WRITE_ALL_PREP_WRITES 0x01 + +/* Find Information Response Formats */ +#define ATT_FIND_INFO_RESP_FMT_16BIT 0x01 +#define ATT_FIND_INFO_RESP_FMT_128BIT 0x02 + struct att_data_list { uint16_t num; uint16_t len; @@ -103,156 +106,97 @@ uint16_t end; }; -/* These functions do byte conversion */ -static inline uint8_t att_get_u8(const void *ptr) -{ - const uint8_t *u8_ptr = ptr; - return bt_get_unaligned(u8_ptr); -} - -static inline uint16_t att_get_u16(const void *ptr) -{ - const uint16_t *u16_ptr = ptr; - return btohs(bt_get_unaligned(u16_ptr)); -} - -static inline uint32_t att_get_u32(const void *ptr) -{ - const uint32_t *u32_ptr = ptr; - return btohl(bt_get_unaligned(u32_ptr)); -} - -static inline uint128_t att_get_u128(const void *ptr) -{ - const uint128_t *u128_ptr = ptr; - uint128_t dst; - - btoh128(u128_ptr, &dst); - - return dst; -} - -static inline void att_put_u8(uint8_t src, void *dst) -{ - bt_put_unaligned(src, (uint8_t *) dst); -} - -static inline void att_put_u16(uint16_t src, void *dst) -{ - bt_put_unaligned(htobs(src), (uint16_t *) dst); -} - -static inline void att_put_u32(uint32_t src, void *dst) -{ - bt_put_unaligned(htobl(src), (uint32_t *) dst); -} - -static inline void att_put_u128(uint128_t src, void *dst) -{ - uint128_t *d128 = dst; - - htob128(&src, d128); -} - -static inline void att_put_uuid16(bt_uuid_t src, void *dst) -{ - att_put_u16(src.value.u16, dst); -} - -static inline void att_put_uuid128(bt_uuid_t src, void *dst) -{ - att_put_u128(src.value.u128, dst); -} - -static inline void att_put_uuid(bt_uuid_t src, void *dst) -{ - if (src.type == BT_UUID16) - att_put_uuid16(src, dst); - else - att_put_uuid128(src, dst); -} - -static inline bt_uuid_t att_get_uuid16(const void *ptr) -{ - bt_uuid_t uuid; - - bt_uuid16_create(&uuid, att_get_u16(ptr)); - - return uuid; -} - -static inline bt_uuid_t att_get_uuid128(const void *ptr) -{ - bt_uuid_t uuid; - uint128_t value; - - value = att_get_u128(ptr); - bt_uuid128_create(&uuid, value); - - return uuid; -} - struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len); void att_data_list_free(struct att_data_list *list); const char *att_ecode2str(uint8_t status); uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, - uint8_t *pdu, int len); -uint16_t dec_read_by_grp_req(const uint8_t *pdu, int len, uint16_t *start, - uint16_t *end, bt_uuid_t *uuid); -uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, int len); + uint8_t *pdu, size_t len); +uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start, + uint16_t *end, bt_uuid_t *uuid); +uint16_t enc_read_by_grp_resp(struct att_data_list *list, uint8_t *pdu, + size_t len); uint16_t enc_find_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, - const uint8_t *value, int vlen, uint8_t *pdu, int len); -uint16_t dec_find_by_type_req(const uint8_t *pdu, int len, uint16_t *start, - uint16_t *end, bt_uuid_t *uuid, uint8_t *value, int *vlen); -uint16_t enc_find_by_type_resp(GSList *ranges, uint8_t *pdu, int len); -GSList *dec_find_by_type_resp(const uint8_t *pdu, int len); -struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, int len); + const uint8_t *value, size_t vlen, uint8_t *pdu, + size_t len); +uint16_t dec_find_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, + uint16_t *end, bt_uuid_t *uuid, uint8_t *value, size_t *vlen); +uint16_t enc_find_by_type_resp(GSList *ranges, uint8_t *pdu, size_t len); +GSList *dec_find_by_type_resp(const uint8_t *pdu, size_t len); +struct att_data_list *dec_read_by_grp_resp(const uint8_t *pdu, size_t len); uint16_t enc_read_by_type_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, - uint8_t *pdu, int len); -uint16_t dec_read_by_type_req(const uint8_t *pdu, int len, uint16_t *start, - uint16_t *end, bt_uuid_t *uuid); + uint8_t *pdu, size_t len); +uint16_t dec_read_by_type_req(const uint8_t *pdu, size_t len, uint16_t *start, + uint16_t *end, bt_uuid_t *uuid); uint16_t enc_read_by_type_resp(struct att_data_list *list, uint8_t *pdu, - int len); -uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, int vlen, - uint8_t *pdu, int len); -uint16_t dec_write_cmd(const uint8_t *pdu, int len, uint16_t *handle, - uint8_t *value, int *vlen); -struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, int len); -uint16_t enc_write_req(uint16_t handle, const uint8_t *value, int vlen, - uint8_t *pdu, int len); -uint16_t dec_write_req(const uint8_t *pdu, int len, uint16_t *handle, - uint8_t *value, int *vlen); -uint16_t enc_write_resp(uint8_t *pdu, int len); -uint16_t dec_write_resp(const uint8_t *pdu, int len); -uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, int len); + size_t len); +uint16_t enc_write_cmd(uint16_t handle, const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len); +uint16_t dec_write_cmd(const uint8_t *pdu, size_t len, uint16_t *handle, + uint8_t *value, size_t *vlen); +uint16_t enc_signed_write_cmd(uint16_t handle, + const uint8_t *value, size_t vlen, + struct bt_crypto *crypto, + const uint8_t csrk[16], + uint32_t sign_cnt, + uint8_t *pdu, size_t len); +uint16_t dec_signed_write_cmd(const uint8_t *pdu, size_t len, + uint16_t *handle, + uint8_t *value, size_t *vlen, + uint8_t signature[12]); +struct att_data_list *dec_read_by_type_resp(const uint8_t *pdu, size_t len); +uint16_t enc_write_req(uint16_t handle, const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len); +uint16_t dec_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, + uint8_t *value, size_t *vlen); +uint16_t enc_write_resp(uint8_t *pdu); +uint16_t dec_write_resp(const uint8_t *pdu, size_t len); +uint16_t enc_read_req(uint16_t handle, uint8_t *pdu, size_t len); uint16_t enc_read_blob_req(uint16_t handle, uint16_t offset, uint8_t *pdu, - int len); -uint16_t dec_read_req(const uint8_t *pdu, int len, uint16_t *handle); -uint16_t dec_read_blob_req(const uint8_t *pdu, int len, uint16_t *handle, + size_t len); +uint16_t dec_read_req(const uint8_t *pdu, size_t len, uint16_t *handle); +uint16_t dec_read_blob_req(const uint8_t *pdu, size_t len, uint16_t *handle, uint16_t *offset); -uint16_t enc_read_resp(uint8_t *value, int vlen, uint8_t *pdu, int len); -uint16_t enc_read_blob_resp(uint8_t *value, int vlen, uint16_t offset, - uint8_t *pdu, int len); -uint16_t dec_read_resp(const uint8_t *pdu, int len, uint8_t *value, int *vlen); +uint16_t enc_read_resp(uint8_t *value, size_t vlen, uint8_t *pdu, size_t len); +uint16_t enc_read_blob_resp(uint8_t *value, size_t vlen, uint16_t offset, + uint8_t *pdu, size_t len); +ssize_t dec_read_resp(const uint8_t *pdu, size_t len, uint8_t *value, + size_t vlen); uint16_t enc_error_resp(uint8_t opcode, uint16_t handle, uint8_t status, - uint8_t *pdu, int len); -uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, int len); -uint16_t dec_find_info_req(const uint8_t *pdu, int len, uint16_t *start, + uint8_t *pdu, size_t len); +uint16_t enc_find_info_req(uint16_t start, uint16_t end, uint8_t *pdu, + size_t len); +uint16_t dec_find_info_req(const uint8_t *pdu, size_t len, uint16_t *start, uint16_t *end); uint16_t enc_find_info_resp(uint8_t format, struct att_data_list *list, - uint8_t *pdu, int len); -struct att_data_list *dec_find_info_resp(const uint8_t *pdu, int len, + uint8_t *pdu, size_t len); +struct att_data_list *dec_find_info_resp(const uint8_t *pdu, size_t len, uint8_t *format); -uint16_t enc_notification(uint16_t handle, uint8_t *value, int vlen, - uint8_t *pdu, int len); -uint16_t enc_indication(uint16_t handle, uint8_t *value, int vlen, - uint8_t *pdu, int len); -uint16_t dec_indication(const uint8_t *pdu, int len, uint16_t *handle, - uint8_t *value, int vlen); -uint16_t enc_confirmation(uint8_t *pdu, int len); - -uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, int len); -uint16_t dec_mtu_req(const uint8_t *pdu, int len, uint16_t *mtu); -uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, int len); -uint16_t dec_mtu_resp(const uint8_t *pdu, int len, uint16_t *mtu); +uint16_t enc_notification(uint16_t handle, uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len); +uint16_t enc_indication(uint16_t handle, uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len); +uint16_t dec_indication(const uint8_t *pdu, size_t len, uint16_t *handle, + uint8_t *value, size_t vlen); +uint16_t enc_confirmation(uint8_t *pdu, size_t len); + +uint16_t enc_mtu_req(uint16_t mtu, uint8_t *pdu, size_t len); +uint16_t dec_mtu_req(const uint8_t *pdu, size_t len, uint16_t *mtu); +uint16_t enc_mtu_resp(uint16_t mtu, uint8_t *pdu, size_t len); +uint16_t dec_mtu_resp(const uint8_t *pdu, size_t len, uint16_t *mtu); + +uint16_t enc_prep_write_req(uint16_t handle, uint16_t offset, + const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len); +uint16_t dec_prep_write_req(const uint8_t *pdu, size_t len, uint16_t *handle, + uint16_t *offset, uint8_t *value, size_t *vlen); +uint16_t enc_prep_write_resp(uint16_t handle, uint16_t offset, + const uint8_t *value, size_t vlen, + uint8_t *pdu, size_t len); +uint16_t dec_prep_write_resp(const uint8_t *pdu, size_t len, uint16_t *handle, + uint16_t *offset, uint8_t *value, + size_t *vlen); +uint16_t enc_exec_write_req(uint8_t flags, uint8_t *pdu, size_t len); +uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags); +uint16_t enc_exec_write_resp(uint8_t *pdu); +uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len); diff -Nru bluez-4.101/attrib/client.c bluez-5.23/attrib/client.c --- bluez-4.101/attrib/client.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1144 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2010 Nokia Corporation - * Copyright (C) 2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include - -#include "adapter.h" -#include "device.h" -#include "log.h" -#include "gdbus.h" -#include "error.h" -#include "dbus-common.h" -#include "btio.h" -#include "storage.h" - -#include "att.h" -#include "gattrib.h" -#include "attio.h" -#include "gatt.h" -#include "client.h" - -#define CHAR_INTERFACE "org.bluez.Characteristic" - -struct format { - guint8 format; - guint8 exponent; - guint16 unit; - guint8 namespace; - guint16 desc; -} __attribute__ ((packed)); - -struct query { - DBusMessage *msg; - GSList *list; -}; - -struct gatt_service { - struct btd_device *dev; - struct gatt_primary *prim; - DBusConnection *conn; - GAttrib *attrib; - guint attioid; - int psm; - char *path; - GSList *chars; - GSList *offline_chars; - GSList *watchers; - struct query *query; -}; - -struct characteristic { - struct gatt_service *gatt; - char *path; - uint16_t handle; - uint16_t end; - uint8_t perm; - char type[MAX_LEN_UUID_STR + 1]; - char *name; - char *desc; - struct format *format; - uint8_t *value; - size_t vlen; -}; - -struct query_data { - struct gatt_service *gatt; - struct characteristic *chr; - uint16_t handle; -}; - -struct watcher { - guint id; - char *name; - char *path; - struct gatt_service *gatt; -}; - -static GSList *gatt_services = NULL; - -static void characteristic_free(void *user_data) -{ - struct characteristic *chr = user_data; - - g_free(chr->path); - g_free(chr->desc); - g_free(chr->format); - g_free(chr->value); - g_free(chr->name); - g_free(chr); -} - -static void watcher_free(void *user_data) -{ - struct watcher *watcher = user_data; - - g_free(watcher->path); - g_free(watcher->name); - g_free(watcher); -} - -static void gatt_service_free(struct gatt_service *gatt) -{ - g_slist_free_full(gatt->watchers, watcher_free); - g_slist_free_full(gatt->chars, characteristic_free); - g_slist_free(gatt->offline_chars); - g_free(gatt->path); - btd_device_unref(gatt->dev); - dbus_connection_unref(gatt->conn); - g_free(gatt); -} - -static void remove_attio(struct gatt_service *gatt) -{ - if (gatt->offline_chars || gatt->watchers || gatt->query) - return; - - if (gatt->attioid) { - btd_device_remove_attio_callback(gatt->dev, gatt->attioid); - gatt->attioid = 0; - } - - if (gatt->attrib) { - g_attrib_unref(gatt->attrib); - gatt->attrib = NULL; - } -} - -static void gatt_get_address(struct gatt_service *gatt, bdaddr_t *sba, - bdaddr_t *dba, uint8_t *bdaddr_type) -{ - struct btd_device *device = gatt->dev; - struct btd_adapter *adapter; - - adapter = device_get_adapter(device); - adapter_get_address(adapter, sba); - device_get_address(device, dba, bdaddr_type); -} - -static int characteristic_handle_cmp(gconstpointer a, gconstpointer b) -{ - const struct characteristic *chr = a; - uint16_t handle = GPOINTER_TO_UINT(b); - - return chr->handle - handle; -} - -static int watcher_cmp(gconstpointer a, gconstpointer b) -{ - const struct watcher *watcher = a; - const struct watcher *match = b; - int ret; - - ret = g_strcmp0(watcher->name, match->name); - if (ret != 0) - return ret; - - return g_strcmp0(watcher->path, match->path); -} - -static void append_char_dict(DBusMessageIter *iter, struct characteristic *chr) -{ - DBusMessageIter dict; - const char *name = ""; - char *uuid; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - uuid = g_strdup(chr->type); - dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid); - g_free(uuid); - - /* FIXME: Translate UUID to name. */ - dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &name); - - if (chr->desc) - dict_append_entry(&dict, "Description", DBUS_TYPE_STRING, - &chr->desc); - - if (chr->value) - dict_append_array(&dict, "Value", DBUS_TYPE_BYTE, &chr->value, - chr->vlen); - - /* FIXME: Missing Format, Value and Representation */ - - dbus_message_iter_close_container(iter, &dict); -} - -static void watcher_exit(DBusConnection *conn, void *user_data) -{ - struct watcher *watcher = user_data; - struct gatt_service *gatt = watcher->gatt; - - DBG("%s watcher %s exited", gatt->path, watcher->name); - - gatt->watchers = g_slist_remove(gatt->watchers, watcher); - g_dbus_remove_watch(gatt->conn, watcher->id); - remove_attio(gatt); -} - -static int characteristic_set_value(struct characteristic *chr, - const uint8_t *value, size_t vlen) -{ - chr->value = g_try_realloc(chr->value, vlen); - if (chr->value == NULL) - return -ENOMEM; - - memcpy(chr->value, value, vlen); - chr->vlen = vlen; - - return 0; -} - -static void update_watchers(gpointer data, gpointer user_data) -{ - struct watcher *w = data; - struct characteristic *chr = user_data; - DBusConnection *conn = w->gatt->conn; - DBusMessage *msg; - - msg = dbus_message_new_method_call(w->name, w->path, - "org.bluez.Watcher", "ValueChanged"); - if (msg == NULL) - return; - - dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &chr->path, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &chr->value, chr->vlen, DBUS_TYPE_INVALID); - - dbus_message_set_no_reply(msg, TRUE); - g_dbus_send_message(conn, msg); -} - -static void events_handler(const uint8_t *pdu, uint16_t len, - gpointer user_data) -{ - struct gatt_service *gatt = user_data; - struct characteristic *chr; - GSList *l; - uint8_t opdu[ATT_MAX_MTU]; - guint handle; - uint16_t olen; - - if (len < 3) { - DBG("Malformed notification/indication packet (opcode 0x%02x)", - pdu[0]); - return; - } - - handle = att_get_u16(&pdu[1]); - - l = g_slist_find_custom(gatt->chars, GUINT_TO_POINTER(handle), - characteristic_handle_cmp); - if (!l) - return; - - chr = l->data; - - if (chr == NULL) { - DBG("Attribute handle 0x%02x not found", handle); - return; - } - - switch (pdu[0]) { - case ATT_OP_HANDLE_IND: - olen = enc_confirmation(opdu, sizeof(opdu)); - g_attrib_send(gatt->attrib, 0, opdu[0], opdu, olen, - NULL, NULL, NULL); - case ATT_OP_HANDLE_NOTIFY: - if (characteristic_set_value(chr, &pdu[3], len - 3) < 0) - DBG("Can't change Characteristic 0x%02x", handle); - - g_slist_foreach(gatt->watchers, update_watchers, chr); - break; - } -} - -static void offline_char_written(gpointer user_data) -{ - struct characteristic *chr = user_data; - struct gatt_service *gatt = chr->gatt; - - gatt->offline_chars = g_slist_remove(gatt->offline_chars, chr); - - remove_attio(gatt); -} - -static void offline_char_write(gpointer data, gpointer user_data) -{ - struct characteristic *chr = data; - GAttrib *attrib = user_data; - - gatt_write_cmd(attrib, chr->handle, chr->value, chr->vlen, - offline_char_written, chr); -} - -static void char_discovered_cb(GSList *characteristics, guint8 status, - gpointer user_data); - -static void attio_connected(GAttrib *attrib, gpointer user_data) -{ - struct gatt_service *gatt = user_data; - - gatt->attrib = g_attrib_ref(attrib); - - g_attrib_register(gatt->attrib, ATT_OP_HANDLE_NOTIFY, - events_handler, gatt, NULL); - g_attrib_register(gatt->attrib, ATT_OP_HANDLE_IND, - events_handler, gatt, NULL); - - g_slist_foreach(gatt->offline_chars, offline_char_write, attrib); - - if (gatt->query) { - struct gatt_primary *prim = gatt->prim; - struct query_data *qchr; - - qchr = g_slist_nth_data(gatt->query->list, 0); - gatt_discover_char(gatt->attrib, prim->range.start, - prim->range.end, NULL, - char_discovered_cb, qchr); - } -} - -static void attio_disconnected(gpointer user_data) -{ - struct gatt_service *gatt = user_data; - - if (gatt->query && gatt->query->msg) { - DBusMessage *reply; - - reply = btd_error_failed(gatt->query->msg, - "ATT IO channel was disconnected"); - g_dbus_send_message(gatt->conn, reply); - dbus_message_unref(gatt->query->msg); - } - - if (gatt->query) { - g_slist_free_full(gatt->query->list, g_free); - gatt->query = NULL; - } - - if (gatt->attrib) { - g_attrib_unref(gatt->attrib); - gatt->attrib = NULL; - } -} - -static DBusMessage *register_watcher(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *sender = dbus_message_get_sender(msg); - struct gatt_service *gatt = data; - struct watcher *watcher; - char *path; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - watcher = g_new0(struct watcher, 1); - watcher->name = g_strdup(sender); - watcher->gatt = gatt; - watcher->path = g_strdup(path); - watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit, - watcher, watcher_free); - - if (gatt->attioid == 0) - gatt->attioid = btd_device_add_attio_callback(gatt->dev, - attio_connected, - attio_disconnected, - gatt); - - gatt->watchers = g_slist_append(gatt->watchers, watcher); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *unregister_watcher(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *sender = dbus_message_get_sender(msg); - struct gatt_service *gatt = data; - struct watcher *watcher, *match; - GSList *l; - char *path; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - match = g_new0(struct watcher, 1); - match->name = g_strdup(sender); - match->path = g_strdup(path); - l = g_slist_find_custom(gatt->watchers, match, watcher_cmp); - watcher_free(match); - if (!l) - return btd_error_not_authorized(msg); - - watcher = l->data; - gatt->watchers = g_slist_remove(gatt->watchers, watcher); - g_dbus_remove_watch(conn, watcher->id); - remove_attio(gatt); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *set_value(DBusConnection *conn, DBusMessage *msg, - DBusMessageIter *iter, struct characteristic *chr) -{ - struct gatt_service *gatt = chr->gatt; - DBusMessageIter sub; - uint8_t *value; - int len; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE) - return btd_error_invalid_args(msg); - - dbus_message_iter_recurse(iter, &sub); - - dbus_message_iter_get_fixed_array(&sub, &value, &len); - - characteristic_set_value(chr, value, len); - - if (gatt->attioid == 0) - gatt->attioid = btd_device_add_attio_callback(gatt->dev, - attio_connected, - attio_disconnected, - gatt); - - if (gatt->attrib) - gatt_write_cmd(gatt->attrib, chr->handle, value, len, - NULL, NULL); - else - gatt->offline_chars = g_slist_append(gatt->offline_chars, chr); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct characteristic *chr = data; - DBusMessage *reply; - DBusMessageIter iter; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - append_char_dict(&iter, chr); - - return reply; -} - -static DBusMessage *set_property(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct characteristic *chr = data; - DBusMessageIter iter; - DBusMessageIter sub; - const char *property; - - if (!dbus_message_iter_init(msg, &iter)) - return btd_error_invalid_args(msg); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - return btd_error_invalid_args(msg); - - dbus_message_iter_get_basic(&iter, &property); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) - return btd_error_invalid_args(msg); - - dbus_message_iter_recurse(&iter, &sub); - - if (g_str_equal("Value", property)) - return set_value(conn, msg, &sub, chr); - - return btd_error_invalid_args(msg); -} - -static const GDBusMethodTable char_methods[] = { - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - get_properties) }, - { GDBUS_METHOD("SetProperty", - GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL, - set_property) }, - { } -}; - -static char *characteristic_list_to_string(GSList *chars) -{ - GString *characteristics; - GSList *l; - - characteristics = g_string_new(NULL); - - for (l = chars; l; l = l->next) { - struct characteristic *chr = l->data; - char chr_str[64]; - - memset(chr_str, 0, sizeof(chr_str)); - - snprintf(chr_str, sizeof(chr_str), "%04X#%02X#%04X#%s ", - chr->handle, chr->perm, chr->end, chr->type); - - characteristics = g_string_append(characteristics, chr_str); - } - - return g_string_free(characteristics, FALSE); -} - -static void store_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, - uint8_t bdaddr_type, uint16_t start, - GSList *chars) -{ - char *characteristics; - - characteristics = characteristic_list_to_string(chars); - - write_device_characteristics(sba, dba, bdaddr_type, start, - characteristics); - - g_free(characteristics); -} - -static void register_characteristic(gpointer data, gpointer user_data) -{ - struct characteristic *chr = data; - DBusConnection *conn = chr->gatt->conn; - const char *gatt_path = user_data; - - chr->path = g_strdup_printf("%s/characteristic%04x", gatt_path, - chr->handle); - - g_dbus_register_interface(conn, chr->path, CHAR_INTERFACE, - char_methods, NULL, NULL, chr, NULL); - - DBG("Registered: %s", chr->path); -} - -static GSList *string_to_characteristic_list(struct gatt_service *gatt, - const char *str) -{ - GSList *l = NULL; - char **chars; - int i; - - if (str == NULL) - return NULL; - - chars = g_strsplit(str, " ", 0); - if (chars == NULL) - return NULL; - - for (i = 0; chars[i]; i++) { - struct characteristic *chr; - int ret; - - chr = g_new0(struct characteristic, 1); - - ret = sscanf(chars[i], "%04hX#%02hhX#%04hX#%s", &chr->handle, - &chr->perm, &chr->end, chr->type); - if (ret < 4) { - g_free(chr); - continue; - } - - chr->gatt = gatt; - l = g_slist_append(l, chr); - } - - g_strfreev(chars); - - return l; -} - -static GSList *load_characteristics(struct gatt_service *gatt, uint16_t start) -{ - GSList *chrs_list; - bdaddr_t sba, dba; - uint8_t bdaddr_type; - char *str; - - gatt_get_address(gatt, &sba, &dba, &bdaddr_type); - - str = read_device_characteristics(&sba, &dba, bdaddr_type, start); - if (str == NULL) - return NULL; - - chrs_list = string_to_characteristic_list(gatt, str); - - free(str); - - return chrs_list; -} - -static void store_attribute(struct gatt_service *gatt, uint16_t handle, - uint16_t type, uint8_t *value, gsize len) -{ - struct btd_device *device = gatt->dev; - bdaddr_t sba, dba; - uint8_t bdaddr_type; - bt_uuid_t uuid; - char *str, *tmp; - guint i; - - str = g_malloc0(MAX_LEN_UUID_STR + len * 2 + 1); - - bt_uuid16_create(&uuid, type); - bt_uuid_to_string(&uuid, str, MAX_LEN_UUID_STR); - - str[MAX_LEN_UUID_STR - 1] = '#'; - - for (i = 0, tmp = str + MAX_LEN_UUID_STR; i < len; i++, tmp += 2) - sprintf(tmp, "%02X", value[i]); - - gatt_get_address(gatt, &sba, &dba, NULL); - - bdaddr_type = device_get_addr_type(device); - - write_device_attribute(&sba, &dba, bdaddr_type, handle, str); - - g_free(str); -} - -static void query_list_append(struct gatt_service *gatt, struct query_data *data) -{ - struct query *query = gatt->query; - - query->list = g_slist_append(query->list, data); -} - -static void query_list_remove(struct gatt_service *gatt, struct query_data *data) -{ - struct query *query = gatt->query; - - query->list = g_slist_remove(query->list, data); - if (query->list != NULL) - return; - - g_free(query); - gatt->query = NULL; - - remove_attio(gatt); -} - -static void update_char_desc(guint8 status, const guint8 *pdu, guint16 len, - gpointer user_data) -{ - struct query_data *current = user_data; - struct gatt_service *gatt = current->gatt; - struct characteristic *chr = current->chr; - - if (status == 0) { - - g_free(chr->desc); - - chr->desc = g_malloc(len); - memcpy(chr->desc, pdu + 1, len - 1); - chr->desc[len - 1] = '\0'; - - store_attribute(gatt, current->handle, - GATT_CHARAC_USER_DESC_UUID, - (void *) chr->desc, len); - } else if (status == ATT_ECODE_INSUFF_ENC) { - GIOChannel *io = g_attrib_get_channel(gatt->attrib); - BtIOSecLevel level = BT_IO_SEC_HIGH; - - bt_io_get(io, BT_IO_L2CAP, NULL, - BT_IO_OPT_SEC_LEVEL, &level, - BT_IO_OPT_INVALID); - - if (level < BT_IO_SEC_HIGH) - level++; - - if (bt_io_set(io, BT_IO_L2CAP, NULL, - BT_IO_OPT_SEC_LEVEL, level, - BT_IO_OPT_INVALID)) { - gatt_read_char(gatt->attrib, current->handle, 0, - update_char_desc, current); - return; - } - } - - query_list_remove(gatt, current); - g_free(current); -} - -static void update_char_format(guint8 status, const guint8 *pdu, guint16 len, - gpointer user_data) -{ - struct query_data *current = user_data; - struct gatt_service *gatt = current->gatt; - struct characteristic *chr = current->chr; - - if (status != 0) - goto done; - - if (len < 8) - goto done; - - g_free(chr->format); - - chr->format = g_new0(struct format, 1); - memcpy(chr->format, pdu + 1, 7); - - store_attribute(gatt, current->handle, GATT_CHARAC_FMT_UUID, - (void *) chr->format, sizeof(*chr->format)); - -done: - query_list_remove(gatt, current); - g_free(current); -} - -static void update_char_value(guint8 status, const guint8 *pdu, - guint16 len, gpointer user_data) -{ - struct query_data *current = user_data; - struct gatt_service *gatt = current->gatt; - struct characteristic *chr = current->chr; - - if (status == 0) - characteristic_set_value(chr, pdu + 1, len - 1); - else if (status == ATT_ECODE_INSUFF_ENC) { - GIOChannel *io = g_attrib_get_channel(gatt->attrib); - - if (bt_io_set(io, BT_IO_L2CAP, NULL, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH, - BT_IO_OPT_INVALID)) { - gatt_read_char(gatt->attrib, chr->handle, 0, - update_char_value, current); - return; - } - } - - query_list_remove(gatt, current); - g_free(current); -} - -static int uuid_desc16_cmp(bt_uuid_t *uuid, guint16 desc) -{ - bt_uuid_t u16; - - bt_uuid16_create(&u16, desc); - - return bt_uuid_cmp(uuid, &u16); -} - -static void descriptor_cb(guint8 status, const guint8 *pdu, guint16 plen, - gpointer user_data) -{ - struct query_data *current = user_data; - struct gatt_service *gatt = current->gatt; - struct att_data_list *list; - guint8 format; - int i; - - if (status != 0) - goto done; - - DBG("Find Information Response received"); - - list = dec_find_info_resp(pdu, plen, &format); - if (list == NULL) - goto done; - - for (i = 0; i < list->num; i++) { - guint16 handle; - bt_uuid_t uuid; - uint8_t *info = list->data[i]; - struct query_data *qfmt; - - handle = att_get_u16(info); - - if (format == 0x01) { - uuid = att_get_uuid16(&info[2]); - } else { - /* Currently, only "user description" and "presentation - * format" descriptors are used, and both have 16-bit - * UUIDs. Therefore there is no need to support format - * 0x02 yet. */ - continue; - } - qfmt = g_new0(struct query_data, 1); - qfmt->gatt = current->gatt; - qfmt->chr = current->chr; - qfmt->handle = handle; - - if (uuid_desc16_cmp(&uuid, GATT_CHARAC_USER_DESC_UUID) == 0) { - query_list_append(gatt, qfmt); - gatt_read_char(gatt->attrib, handle, 0, update_char_desc, - qfmt); - } else if (uuid_desc16_cmp(&uuid, GATT_CHARAC_FMT_UUID) == 0) { - query_list_append(gatt, qfmt); - gatt_read_char(gatt->attrib, handle, 0, - update_char_format, qfmt); - } else - g_free(qfmt); - } - - att_data_list_free(list); -done: - query_list_remove(gatt, current); - g_free(current); -} - -static void update_all_chars(gpointer data, gpointer user_data) -{ - struct query_data *qdesc, *qvalue; - struct characteristic *chr = data; - struct gatt_service *gatt = user_data; - - qdesc = g_new0(struct query_data, 1); - qdesc->gatt = gatt; - qdesc->chr = chr; - - query_list_append(gatt, qdesc); - - gatt_find_info(gatt->attrib, chr->handle + 1, chr->end, descriptor_cb, - qdesc); - - qvalue = g_new0(struct query_data, 1); - qvalue->gatt = gatt; - qvalue->chr = chr; - - query_list_append(gatt, qvalue); - - gatt_read_char(gatt->attrib, chr->handle, 0, update_char_value, qvalue); -} - -static DBusMessage *create_discover_char_reply(DBusMessage *msg, GSList *chars) -{ - DBusMessage *reply; - DBusMessageIter iter, array_iter; - GSList *l; - - reply = dbus_message_new_method_return(msg); - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_OBJECT_PATH_AS_STRING, &array_iter); - - for (l = chars; l; l = l->next) { - struct characteristic *chr = l->data; - - dbus_message_iter_append_basic(&array_iter, - DBUS_TYPE_OBJECT_PATH, &chr->path); - } - - dbus_message_iter_close_container(&iter, &array_iter); - - return reply; -} - -static void char_discovered_cb(GSList *characteristics, guint8 status, - gpointer user_data) -{ - DBusMessage *reply; - struct query_data *current = user_data; - struct gatt_service *gatt = current->gatt; - struct gatt_primary *prim = gatt->prim; - uint16_t *previous_end = NULL; - GSList *l; - bdaddr_t sba, dba; - uint8_t bdaddr_type; - - if (status != 0) { - const char *str = att_ecode2str(status); - - DBG("Discover all characteristics failed: %s", str); - reply = btd_error_failed(gatt->query->msg, str); - goto fail; - } - - for (l = characteristics; l; l = l->next) { - struct gatt_char *current_chr = l->data; - struct characteristic *chr; - guint handle = current_chr->value_handle; - GSList *lchr; - - lchr = g_slist_find_custom(gatt->chars, - GUINT_TO_POINTER(handle), characteristic_handle_cmp); - if (lchr) - continue; - - chr = g_new0(struct characteristic, 1); - chr->gatt = gatt; - chr->perm = current_chr->properties; - chr->handle = current_chr->value_handle; - strncpy(chr->type, current_chr->uuid, sizeof(chr->type)); - - if (previous_end) - *previous_end = current_chr->handle; - - previous_end = &chr->end; - - gatt->chars = g_slist_append(gatt->chars, chr); - register_characteristic(chr, gatt->path); - } - - if (previous_end) - *previous_end = prim->range.end; - - gatt_get_address(gatt, &sba, &dba, &bdaddr_type); - store_characteristics(&sba, &dba, bdaddr_type, prim->range.start, - gatt->chars); - - g_slist_foreach(gatt->chars, update_all_chars, gatt); - - reply = create_discover_char_reply(gatt->query->msg, gatt->chars); - -fail: - dbus_message_unref(gatt->query->msg); - gatt->query->msg = NULL; - - g_dbus_send_message(gatt->conn, reply); - query_list_remove(gatt, current); - g_free(current); -} - -static DBusMessage *discover_char(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct gatt_service *gatt = data; - struct query *query; - struct query_data *qchr; - - if (gatt->query) - return btd_error_busy(msg); - - query = g_new0(struct query, 1); - - qchr = g_new0(struct query_data, 1); - qchr->gatt = gatt; - - query->msg = dbus_message_ref(msg); - - if (gatt->attioid == 0) { - gatt->attioid = btd_device_add_attio_callback(gatt->dev, - attio_connected, - attio_disconnected, - gatt); - } else if (gatt->attrib) { - struct gatt_primary *prim = gatt->prim; - gatt_discover_char(gatt->attrib, prim->range.start, - prim->range.end, NULL, - char_discovered_cb, qchr); - } - - gatt->query = query; - - query_list_append(gatt, qchr); - - return NULL; -} - -static DBusMessage *prim_get_properties(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct gatt_service *gatt = data; - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter dict; - GSList *l; - char **chars; - const char *uuid; - int i; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - chars = g_new0(char *, g_slist_length(gatt->chars) + 1); - - for (i = 0, l = gatt->chars; l; l = l->next, i++) { - struct characteristic *chr = l->data; - chars[i] = chr->path; - } - - dict_append_array(&dict, "Characteristics", DBUS_TYPE_OBJECT_PATH, - &chars, i); - uuid = gatt->prim->uuid; - dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid); - - g_free(chars); - - dbus_message_iter_close_container(&iter, &dict); - - return reply; -} - -static const GDBusMethodTable prim_methods[] = { - { GDBUS_ASYNC_METHOD("DiscoverCharacteristics", - NULL, GDBUS_ARGS({ "characteristics", "ao" }), - discover_char) }, - { GDBUS_METHOD("RegisterCharacteristicsWatcher", - GDBUS_ARGS({ "agent", "o" }), NULL, - register_watcher) }, - { GDBUS_METHOD("UnregisterCharacteristicsWatcher", - GDBUS_ARGS({ "agent", "o" }), NULL, - unregister_watcher) }, - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - prim_get_properties) }, - { } -}; - -static struct gatt_service *primary_register(DBusConnection *conn, - struct btd_device *device, - struct gatt_primary *prim, - int psm) -{ - struct gatt_service *gatt; - const char *device_path; - - device_path = device_get_path(device); - - gatt = g_new0(struct gatt_service, 1); - gatt->dev = btd_device_ref(device); - gatt->prim = prim; - gatt->psm = psm; - gatt->conn = dbus_connection_ref(conn); - gatt->path = g_strdup_printf("%s/service%04x", device_path, - prim->range.start); - - g_dbus_register_interface(gatt->conn, gatt->path, - CHAR_INTERFACE, prim_methods, - NULL, NULL, gatt, NULL); - gatt->chars = load_characteristics(gatt, prim->range.start); - g_slist_foreach(gatt->chars, register_characteristic, gatt->path); - - return gatt; -} - -GSList *attrib_client_register(DBusConnection *connection, - struct btd_device *device, int psm, - GAttrib *attrib, GSList *primaries) -{ - GSList *l, *services; - - for (l = primaries, services = NULL; l; l = l->next) { - struct gatt_primary *prim = l->data; - struct gatt_service *gatt; - - gatt = primary_register(connection, device, prim, psm); - - DBG("Registered: %s", gatt->path); - - services = g_slist_append(services, g_strdup(gatt->path)); - gatt_services = g_slist_append(gatt_services, gatt); - - } - - return services; -} - -static void primary_unregister(struct gatt_service *gatt) -{ - GSList *l; - - for (l = gatt->chars; l; l = l->next) { - struct characteristic *chr = l->data; - g_dbus_unregister_interface(gatt->conn, chr->path, - CHAR_INTERFACE); - } - - g_dbus_unregister_interface(gatt->conn, gatt->path, CHAR_INTERFACE); - - remove_attio(gatt); -} - -static int path_cmp(gconstpointer data, gconstpointer user_data) -{ - const char *path = data; - const char *gatt_path = user_data; - - return g_strcmp0(path, gatt_path); -} - -void attrib_client_unregister(GSList *services) -{ - GSList *l, *left; - - for (l = gatt_services, left = NULL; l; l = l->next) { - struct gatt_service *gatt = l->data; - - if (!g_slist_find_custom(services, gatt->path, path_cmp)) { - left = g_slist_append(left, gatt); - continue; - } - - primary_unregister(gatt); - gatt_service_free(gatt); - } - - g_slist_free(gatt_services); - gatt_services = left; -} diff -Nru bluez-4.101/attrib/client.h bluez-5.23/attrib/client.h --- bluez-4.101/attrib/client.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/client.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2010 Nokia Corporation - * Copyright (C) 2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -GSList *attrib_client_register(DBusConnection *connection, - struct btd_device *device, int psm, - GAttrib *attrib, GSList *primaries); -void attrib_client_unregister(GSList *services); diff -Nru bluez-4.101/attrib/gatt.c bluez-5.23/attrib/gatt.c --- bluez-4.101/attrib/gatt.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/gatt.c 2014-08-06 17:25:36.000000000 +0000 @@ -29,15 +29,17 @@ #include #include #include -#include #include #include +#include "src/shared/util.h" +#include "lib/uuid.h" #include "att.h" #include "gattrib.h" #include "gatt.h" struct discover_primary { + int ref; GAttrib *attrib; bt_uuid_t uuid; GSList *primaries; @@ -45,7 +47,24 @@ void *user_data; }; +/* Used for the Included Services Discovery (ISD) procedure */ +struct included_discovery { + GAttrib *attrib; + int refs; + int err; + uint16_t end_handle; + GSList *includes; + gatt_cb_t cb; + void *user_data; +}; + +struct included_uuid_query { + struct included_discovery *isd; + struct gatt_included *included; +}; + struct discover_char { + int ref; GAttrib *attrib; bt_uuid_t *uuid; uint16_t end; @@ -54,21 +73,129 @@ void *user_data; }; -static void discover_primary_free(struct discover_primary *dp) +struct discover_desc { + int ref; + GAttrib *attrib; + bt_uuid_t *uuid; + uint16_t end; + GSList *descriptors; + gatt_cb_t cb; + void *user_data; +}; + +static void discover_primary_unref(void *data) { - g_slist_free(dp->primaries); + struct discover_primary *dp = data; + + dp->ref--; + + if (dp->ref > 0) + return; + + g_slist_free_full(dp->primaries, g_free); g_attrib_unref(dp->attrib); g_free(dp); } -static void discover_char_free(struct discover_char *dc) +static struct discover_primary *discover_primary_ref( + struct discover_primary *dp) +{ + dp->ref++; + + return dp; +} + +static struct included_discovery *isd_ref(struct included_discovery *isd) { + __sync_fetch_and_add(&isd->refs, 1); + + return isd; +} + +static void isd_unref(struct included_discovery *isd) +{ + if (__sync_sub_and_fetch(&isd->refs, 1) > 0) + return; + + if (isd->err) + isd->cb(isd->err, NULL, isd->user_data); + else + isd->cb(isd->err, isd->includes, isd->user_data); + + g_slist_free_full(isd->includes, g_free); + g_attrib_unref(isd->attrib); + g_free(isd); +} + +static void discover_char_unref(void *data) +{ + struct discover_char *dc = data; + + dc->ref--; + + if (dc->ref > 0) + return; + g_slist_free_full(dc->characteristics, g_free); g_attrib_unref(dc->attrib); g_free(dc->uuid); g_free(dc); } +static struct discover_char *discover_char_ref(struct discover_char *dc) +{ + dc->ref++; + + return dc; +} + +static void discover_desc_unref(void *data) +{ + struct discover_desc *dd = data; + + dd->ref--; + + if (dd->ref > 0) + return; + + g_slist_free_full(dd->descriptors, g_free); + g_attrib_unref(dd->attrib); + g_free(dd->uuid); + g_free(dd); +} + +static struct discover_desc *discover_desc_ref(struct discover_desc *dd) +{ + dd->ref++; + + return dd; +} + +static void put_uuid_le(const bt_uuid_t *uuid, void *dst) +{ + if (uuid->type == BT_UUID16) + put_le16(uuid->value.u16, dst); + else + /* Convert from 128-bit BE to LE */ + bswap_128(&uuid->value.u128, dst); +} + +static void get_uuid128(uint8_t type, const void *val, bt_uuid_t *uuid) +{ + if (type == BT_UUID16) { + bt_uuid_t uuid16; + + bt_uuid16_create(&uuid16, get_le16(val)); + bt_uuid_to_uuid128(&uuid16, uuid); + } else { + uint128_t u128; + + /* Convert from 128-bit LE to BE */ + bswap_128(val, &u128); + bt_uuid128_create(uuid, u128); + } +} + static guint16 encode_discover_primary(uint16_t start, uint16_t end, bt_uuid_t *uuid, uint8_t *pdu, size_t len) { @@ -81,22 +208,12 @@ /* Discover all primary services */ plen = enc_read_by_grp_req(start, end, &prim, pdu, len); } else { - uint16_t u16; - uint128_t u128; - const void *value; - int vlen; + uint8_t value[16]; + size_t vlen; /* Discover primary service by service UUID */ - - if (uuid->type == BT_UUID16) { - u16 = htobs(uuid->value.u16); - value = &u16; - vlen = sizeof(u16); - } else { - htob128(&uuid->value.u128, &u128); - value = &u128; - vlen = sizeof(u128); - } + put_uuid_le(uuid, value); + vlen = bt_uuid_len(uuid); plen = enc_find_by_type_req(start, end, &prim, value, vlen, pdu, len); @@ -114,7 +231,8 @@ struct att_range *range; uint8_t *buf; guint16 oplen; - int err = 0, buflen; + int err = 0; + size_t buflen; if (status) { err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status; @@ -140,13 +258,12 @@ if (oplen == 0) goto done; - g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_by_uuid_cb, - dp, NULL); + g_attrib_send(dp->attrib, 0, buf, oplen, primary_by_uuid_cb, + discover_primary_ref(dp), discover_primary_unref); return; done: - dp->cb(dp->primaries, err, dp->user_data); - discover_primary_free(dp); + dp->cb(err, dp->primaries, dp->user_data); } static void primary_all_cb(guint8 status, const guint8 *ipdu, guint16 iplen, @@ -156,6 +273,7 @@ struct att_data_list *list; unsigned int i, err; uint16_t start, end; + uint8_t type; if (status) { err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status; @@ -168,32 +286,35 @@ goto done; } + if (list->len == 6) + type = BT_UUID16; + else if (list->len == 20) + type = BT_UUID128; + else { + att_data_list_free(list); + err = ATT_ECODE_INVALID_PDU; + goto done; + } + for (i = 0, end = 0; i < list->num; i++) { const uint8_t *data = list->data[i]; struct gatt_primary *primary; - bt_uuid_t uuid; + bt_uuid_t uuid128; - start = att_get_u16(&data[0]); - end = att_get_u16(&data[2]); + start = get_le16(&data[0]); + end = get_le16(&data[2]); - if (list->len == 6) { - bt_uuid_t uuid16 = att_get_uuid16(&data[4]); - bt_uuid_to_uuid128(&uuid16, &uuid); - } else if (list->len == 20) { - uuid = att_get_uuid128(&data[4]); - } else { - /* Skipping invalid data */ - continue; - } + get_uuid128(type, &data[4], &uuid128); primary = g_try_new0(struct gatt_primary, 1); if (!primary) { + att_data_list_free(list); err = ATT_ECODE_INSUFF_RESOURCES; goto done; } primary->range.start = start; primary->range.end = end; - bt_uuid_to_string(&uuid, primary->uuid, sizeof(primary->uuid)); + bt_uuid_to_string(&uuid128, primary->uuid, sizeof(primary->uuid)); dp->primaries = g_slist_append(dp->primaries, primary); } @@ -201,27 +322,27 @@ err = 0; if (end != 0xffff) { - int buflen; + size_t buflen; uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen); guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL, buf, buflen); - g_attrib_send(dp->attrib, 0, buf[0], buf, oplen, primary_all_cb, - dp, NULL); + g_attrib_send(dp->attrib, 0, buf, oplen, primary_all_cb, + discover_primary_ref(dp), + discover_primary_unref); return; } done: - dp->cb(dp->primaries, err, dp->user_data); - discover_primary_free(dp); + dp->cb(err, dp->primaries, dp->user_data); } guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { struct discover_primary *dp; - int buflen; + size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); GAttribResultFunc cb; guint16 plen; @@ -244,7 +365,168 @@ } else cb = primary_all_cb; - return g_attrib_send(attrib, 0, buf[0], buf, plen, cb, dp, NULL); + return g_attrib_send(attrib, 0, buf, plen, cb, + discover_primary_ref(dp), + discover_primary_unref); +} + +static void resolve_included_uuid_cb(uint8_t status, const uint8_t *pdu, + uint16_t len, gpointer user_data) +{ + struct included_uuid_query *query = user_data; + struct included_discovery *isd = query->isd; + struct gatt_included *incl = query->included; + unsigned int err = status; + bt_uuid_t uuid128; + size_t buflen; + uint8_t *buf; + + if (err) + goto done; + + buf = g_attrib_get_buffer(isd->attrib, &buflen); + if (dec_read_resp(pdu, len, buf, buflen) != 16) { + err = ATT_ECODE_IO; + goto done; + } + + get_uuid128(BT_UUID128, buf, &uuid128); + + bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid)); + isd->includes = g_slist_append(isd->includes, incl); + query->included = NULL; + +done: + if (isd->err == 0) + isd->err = err; +} + +static void inc_query_free(void *data) +{ + struct included_uuid_query *query = data; + + isd_unref(query->isd); + g_free(query->included); + g_free(query); +} + +static guint resolve_included_uuid(struct included_discovery *isd, + struct gatt_included *incl) +{ + struct included_uuid_query *query; + size_t buflen; + uint8_t *buf = g_attrib_get_buffer(isd->attrib, &buflen); + guint16 oplen = enc_read_req(incl->range.start, buf, buflen); + + query = g_new0(struct included_uuid_query, 1); + query->isd = isd_ref(isd); + query->included = incl; + + return g_attrib_send(isd->attrib, 0, buf, oplen, + resolve_included_uuid_cb, query, + inc_query_free); +} + +static struct gatt_included *included_from_buf(const uint8_t *buf, gsize len) +{ + struct gatt_included *incl = g_new0(struct gatt_included, 1); + + incl->handle = get_le16(&buf[0]); + incl->range.start = get_le16(&buf[2]); + incl->range.end = get_le16(&buf[4]); + + if (len == 8) { + bt_uuid_t uuid128; + + get_uuid128(BT_UUID16, &buf[6], &uuid128); + bt_uuid_to_string(&uuid128, incl->uuid, sizeof(incl->uuid)); + } + + return incl; +} + +static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len, + gpointer user_data); + +static guint find_included(struct included_discovery *isd, uint16_t start) +{ + bt_uuid_t uuid; + size_t buflen; + uint8_t *buf = g_attrib_get_buffer(isd->attrib, &buflen); + guint16 oplen; + + bt_uuid16_create(&uuid, GATT_INCLUDE_UUID); + oplen = enc_read_by_type_req(start, isd->end_handle, &uuid, + buf, buflen); + + return g_attrib_send(isd->attrib, 0, buf, oplen, find_included_cb, + isd_ref(isd), (GDestroyNotify) isd_unref); +} + +static void find_included_cb(uint8_t status, const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + struct included_discovery *isd = user_data; + uint16_t last_handle = isd->end_handle; + unsigned int err = status; + struct att_data_list *list; + int i; + + if (err == ATT_ECODE_ATTR_NOT_FOUND) + err = 0; + + if (status) + goto done; + + list = dec_read_by_type_resp(pdu, len); + if (list == NULL) { + err = ATT_ECODE_IO; + goto done; + } + + if (list->len != 6 && list->len != 8) { + err = ATT_ECODE_IO; + att_data_list_free(list); + goto done; + } + + for (i = 0; i < list->num; i++) { + struct gatt_included *incl; + + incl = included_from_buf(list->data[i], list->len); + last_handle = incl->handle; + + /* 128 bit UUID, needs resolving */ + if (list->len == 6) { + resolve_included_uuid(isd, incl); + continue; + } + + isd->includes = g_slist_append(isd->includes, incl); + } + + att_data_list_free(list); + + if (last_handle < isd->end_handle) + find_included(isd, last_handle + 1); + +done: + if (isd->err == 0) + isd->err = err; +} + +unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end, + gatt_cb_t func, gpointer user_data) +{ + struct included_discovery *isd; + + isd = g_new0(struct included_discovery, 1); + isd->attrib = g_attrib_ref(attrib); + isd->end_handle = end; + isd->cb = func; + isd->user_data = user_data; + + return find_included(isd, start); } static void char_discovered_cb(guint8 status, const guint8 *ipdu, guint16 iplen, @@ -252,15 +534,12 @@ { struct discover_char *dc = user_data; struct att_data_list *list; - unsigned int i, err; - int buflen; - uint8_t *buf; - guint16 oplen; - bt_uuid_t uuid; + unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND; uint16_t last = 0; + uint8_t type; if (status) { - err = status == ATT_ECODE_ATTR_NOT_FOUND ? 0 : status; + err = status; goto done; } @@ -270,40 +549,46 @@ goto done; } + if (list->len == 7) + type = BT_UUID16; + else + type = BT_UUID128; + for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; struct gatt_char *chars; - bt_uuid_t uuid; + bt_uuid_t uuid128; - last = att_get_u16(value); + last = get_le16(value); - if (list->len == 7) { - bt_uuid_t uuid16 = att_get_uuid16(&value[5]); - bt_uuid_to_uuid128(&uuid16, &uuid); - } else - uuid = att_get_uuid128(&value[5]); + get_uuid128(type, &value[5], &uuid128); + + if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid128)) + continue; chars = g_try_new0(struct gatt_char, 1); if (!chars) { + att_data_list_free(list); err = ATT_ECODE_INSUFF_RESOURCES; goto done; } - if (dc->uuid && bt_uuid_cmp(dc->uuid, &uuid)) - break; - chars->handle = last; chars->properties = value[2]; - chars->value_handle = att_get_u16(&value[3]); - bt_uuid_to_string(&uuid, chars->uuid, sizeof(chars->uuid)); + chars->value_handle = get_le16(&value[3]); + bt_uuid_to_string(&uuid128, chars->uuid, sizeof(chars->uuid)); dc->characteristics = g_slist_append(dc->characteristics, chars); } att_data_list_free(list); - err = 0; if (last != 0 && (last + 1 < dc->end)) { + bt_uuid_t uuid; + guint16 oplen; + size_t buflen; + uint8_t *buf; + buf = g_attrib_get_buffer(dc->attrib, &buflen); bt_uuid16_create(&uuid, GATT_CHARAC_UUID); @@ -314,22 +599,22 @@ if (oplen == 0) return; - g_attrib_send(dc->attrib, 0, buf[0], buf, oplen, - char_discovered_cb, dc, NULL); + g_attrib_send(dc->attrib, 0, buf, oplen, char_discovered_cb, + discover_char_ref(dc), discover_char_unref); return; } done: - dc->cb(dc->characteristics, err, dc->user_data); - discover_char_free(dc); + err = (dc->characteristics ? 0 : err); + dc->cb(err, dc->characteristics, dc->user_data); } guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data) { - int buflen; + size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); struct discover_char *dc; bt_uuid_t type_uuid; @@ -351,15 +636,15 @@ dc->end = end; dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t)); - return g_attrib_send(attrib, 0, buf[0], buf, plen, char_discovered_cb, - dc, NULL); + return g_attrib_send(attrib, 0, buf, plen, char_discovered_cb, + discover_char_ref(dc), discover_char_unref); } guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, GAttribResultFunc func, gpointer user_data) { - int buflen; + size_t buflen; uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); guint16 plen; @@ -367,8 +652,7 @@ if (plen == 0) return 0; - return g_attrib_send(attrib, 0, ATT_OP_READ_BY_TYPE_REQ, - buf, plen, func, user_data, NULL); + return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } struct read_long_data { @@ -379,14 +663,14 @@ guint16 size; guint16 handle; guint id; - gint ref; + int ref; }; static void read_long_destroy(gpointer user_data) { struct read_long_data *long_read = user_data; - if (g_atomic_int_dec_and_test(&long_read->ref) == FALSE) + if (__sync_sub_and_fetch(&long_read->ref, 1) > 0) return; if (long_read->buffer != NULL) @@ -400,7 +684,7 @@ { struct read_long_data *long_read = user_data; uint8_t *buf; - int buflen; + size_t buflen; guint8 *tmp; guint16 plen; guint id; @@ -427,12 +711,11 @@ plen = enc_read_blob_req(long_read->handle, long_read->size - 1, buf, buflen); - id = g_attrib_send(long_read->attrib, long_read->id, - ATT_OP_READ_BLOB_REQ, buf, plen, + id = g_attrib_send(long_read->attrib, long_read->id, buf, plen, read_blob_helper, long_read, read_long_destroy); if (id != 0) { - g_atomic_int_inc(&long_read->ref); + __sync_fetch_and_add(&long_read->ref, 1); return; } @@ -447,7 +730,7 @@ guint16 rlen, gpointer user_data) { struct read_long_data *long_read = user_data; - int buflen; + size_t buflen; uint8_t *buf = g_attrib_get_buffer(long_read->attrib, &buflen); guint16 plen; guint id; @@ -456,20 +739,20 @@ goto done; long_read->buffer = g_malloc(rlen); - - if (long_read->buffer == NULL) + if (long_read->buffer == NULL) { + status = ATT_ECODE_INSUFF_RESOURCES; goto done; + } memcpy(long_read->buffer, rpdu, rlen); long_read->size = rlen; plen = enc_read_blob_req(long_read->handle, rlen - 1, buf, buflen); - id = g_attrib_send(long_read->attrib, long_read->id, - ATT_OP_READ_BLOB_REQ, buf, plen, read_blob_helper, - long_read, read_long_destroy); + id = g_attrib_send(long_read->attrib, long_read->id, buf, plen, + read_blob_helper, long_read, read_long_destroy); if (id != 0) { - g_atomic_int_inc(&long_read->ref); + __sync_fetch_and_add(&long_read->ref, 1); return; } @@ -479,11 +762,11 @@ long_read->func(status, rpdu, rlen, long_read->user_data); } -guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset, - GAttribResultFunc func, gpointer user_data) +guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func, + gpointer user_data) { uint8_t *buf; - int buflen; + size_t buflen; guint16 plen; guint id; struct read_long_data *long_read; @@ -499,84 +782,308 @@ long_read->handle = handle; buf = g_attrib_get_buffer(attrib, &buflen); - if (offset > 0) { - plen = enc_read_blob_req(long_read->handle, offset, buf, - buflen); - id = g_attrib_send(attrib, 0, ATT_OP_READ_BLOB_REQ, buf, plen, - read_blob_helper, long_read, read_long_destroy); - } else { - plen = enc_read_req(handle, buf, buflen); - id = g_attrib_send(attrib, 0, ATT_OP_READ_REQ, buf, plen, - read_char_helper, long_read, read_long_destroy); - } - + plen = enc_read_req(handle, buf, buflen); + id = g_attrib_send(attrib, 0, buf, plen, read_char_helper, + long_read, read_long_destroy); if (id == 0) g_free(long_read); else { - g_atomic_int_inc(&long_read->ref); + __sync_fetch_and_add(&long_read->ref, 1); long_read->id = id; } return id; } -guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value, - int vlen, GAttribResultFunc func, gpointer user_data) +struct write_long_data { + GAttrib *attrib; + GAttribResultFunc func; + gpointer user_data; + guint16 handle; + uint16_t offset; + uint8_t *value; + size_t vlen; +}; + +static guint execute_write(GAttrib *attrib, uint8_t flags, + GAttribResultFunc func, gpointer user_data) { uint8_t *buf; - int buflen; + size_t buflen; guint16 plen; buf = g_attrib_get_buffer(attrib, &buflen); - if (func) + plen = enc_exec_write_req(flags, buf, buflen); + if (plen == 0) + return 0; + + return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); +} + +static guint prepare_write(struct write_long_data *long_write); + +static void prepare_write_cb(guint8 status, const guint8 *rpdu, guint16 rlen, + gpointer user_data) +{ + struct write_long_data *long_write = user_data; + + if (status != 0) { + long_write->func(status, rpdu, rlen, long_write->user_data); + return; + } + + /* Skip Prepare Write Response PDU header (5 bytes) */ + long_write->offset += rlen - 5; + + if (long_write->offset == long_write->vlen) { + execute_write(long_write->attrib, ATT_WRITE_ALL_PREP_WRITES, + long_write->func, long_write->user_data); + g_free(long_write->value); + g_free(long_write); + + return; + } + + prepare_write(long_write); +} + +static guint prepare_write(struct write_long_data *long_write) +{ + GAttrib *attrib = long_write->attrib; + uint16_t handle = long_write->handle; + uint16_t offset = long_write->offset; + uint8_t *buf, *value = long_write->value + offset; + size_t buflen, vlen = long_write->vlen - offset; + guint16 plen; + + buf = g_attrib_get_buffer(attrib, &buflen); + + plen = enc_prep_write_req(handle, offset, value, vlen, buf, buflen); + if (plen == 0) + return 0; + + return g_attrib_send(attrib, 0, buf, plen, prepare_write_cb, long_write, + NULL); +} + +guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value, + size_t vlen, GAttribResultFunc func, gpointer user_data) +{ + uint8_t *buf; + size_t buflen; + struct write_long_data *long_write; + + buf = g_attrib_get_buffer(attrib, &buflen); + + /* Use Write Request if payload fits on a single transfer, including 3 + * bytes for the header. */ + if (vlen <= buflen - 3) { + uint16_t plen; + plen = enc_write_req(handle, value, vlen, buf, buflen); - else - plen = enc_write_cmd(handle, value, vlen, buf, buflen); + if (plen == 0) + return 0; + + return g_attrib_send(attrib, 0, buf, plen, func, user_data, + NULL); + } + + /* Write Long Characteristic Values */ + long_write = g_try_new0(struct write_long_data, 1); + if (long_write == NULL) + return 0; + + long_write->attrib = attrib; + long_write->func = func; + long_write->user_data = user_data; + long_write->handle = handle; + long_write->value = g_memdup(value, vlen); + long_write->vlen = vlen; + + return prepare_write(long_write); +} + +guint gatt_execute_write(GAttrib *attrib, uint8_t flags, + GAttribResultFunc func, gpointer user_data) +{ + return execute_write(attrib, flags, func, user_data); +} + +guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle, + const uint8_t *value, size_t vlen, + GAttribResultFunc func, + gpointer user_data) +{ + uint8_t *buf; + guint16 plen; + size_t buflen; + + buf = g_attrib_get_buffer(attrib, &buflen); + + plen = enc_prep_write_req(handle, 0, value, vlen, buf, buflen); + if (!plen) + return 0; - return g_attrib_send(attrib, 0, buf[0], buf, plen, func, - user_data, NULL); + return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func, gpointer user_data) { uint8_t *buf; - int buflen; + size_t buflen; guint16 plen; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_mtu_req(mtu, buf, buflen); - return g_attrib_send(attrib, 0, ATT_OP_MTU_REQ, buf, plen, func, - user_data, NULL); + return g_attrib_send(attrib, 0, buf, plen, func, user_data, NULL); } -guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end, - GAttribResultFunc func, gpointer user_data) + +static void desc_discovered_cb(guint8 status, const guint8 *ipdu, + guint16 iplen, gpointer user_data) { - uint8_t *buf; - int buflen; + struct discover_desc *dd = user_data; + struct att_data_list *list; + unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND; + guint8 format; + uint16_t last = 0xffff; + uint8_t type; + gboolean uuid_found = FALSE; + + if (status) { + err = status; + goto done; + } + + list = dec_find_info_resp(ipdu, iplen, &format); + if (!list) { + err = ATT_ECODE_IO; + goto done; + } + + if (format == ATT_FIND_INFO_RESP_FMT_16BIT) + type = BT_UUID16; + else + type = BT_UUID128; + + for (i = 0; i < list->num; i++) { + uint8_t *value = list->data[i]; + struct gatt_desc *desc; + bt_uuid_t uuid128; + + last = get_le16(value); + + get_uuid128(type, &value[2], &uuid128); + + if (dd->uuid) { + if (bt_uuid_cmp(dd->uuid, &uuid128)) + continue; + else + uuid_found = TRUE; + } + + desc = g_try_new0(struct gatt_desc, 1); + if (!desc) { + att_data_list_free(list); + err = ATT_ECODE_INSUFF_RESOURCES; + goto done; + } + + bt_uuid_to_string(&uuid128, desc->uuid, sizeof(desc->uuid)); + desc->handle = last; + + if (type == BT_UUID16) + desc->uuid16 = get_le16(&value[2]); + + dd->descriptors = g_slist_append(dd->descriptors, desc); + + if (uuid_found) + break; + } + + att_data_list_free(list); + + if (last < dd->end && !uuid_found) { + guint16 oplen; + size_t buflen; + uint8_t *buf; + + buf = g_attrib_get_buffer(dd->attrib, &buflen); + + oplen = enc_find_info_req(last + 1, dd->end, buf, buflen); + if (oplen == 0) + return; + + g_attrib_send(dd->attrib, 0, buf, oplen, desc_discovered_cb, + discover_desc_ref(dd), discover_desc_unref); + + return; + } + +done: + err = (dd->descriptors ? 0 : err); + dd->cb(err, dd->descriptors, dd->user_data); +} + +guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data) +{ + size_t buflen; + uint8_t *buf = g_attrib_get_buffer(attrib, &buflen); + struct discover_desc *dd; guint16 plen; - buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_find_info_req(start, end, buf, buflen); if (plen == 0) return 0; - return g_attrib_send(attrib, 0, ATT_OP_FIND_INFO_REQ, buf, plen, func, - user_data, NULL); + dd = g_try_new0(struct discover_desc, 1); + if (dd == NULL) + return 0; + + dd->attrib = g_attrib_ref(attrib); + dd->cb = func; + dd->user_data = user_data; + dd->end = end; + dd->uuid = g_memdup(uuid, sizeof(bt_uuid_t)); + + return g_attrib_send(attrib, 0, buf, plen, desc_discovered_cb, + discover_desc_ref(dd), discover_desc_unref); } -guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen, - GDestroyNotify notify, gpointer user_data) +guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value, + int vlen, GDestroyNotify notify, gpointer user_data) { uint8_t *buf; - int buflen; + size_t buflen; guint16 plen; buf = g_attrib_get_buffer(attrib, &buflen); plen = enc_write_cmd(handle, value, vlen, buf, buflen); - return g_attrib_send(attrib, 0, ATT_OP_WRITE_CMD, buf, plen, NULL, - user_data, notify); + return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify); +} + +guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle, + const uint8_t *value, int vlen, + struct bt_crypto *crypto, + const uint8_t csrk[16], + uint32_t sign_cnt, + GDestroyNotify notify, + gpointer user_data) +{ + uint8_t *buf; + size_t buflen; + guint16 plen; + + buf = g_attrib_get_buffer(attrib, &buflen); + plen = enc_signed_write_cmd(handle, value, vlen, crypto, csrk, sign_cnt, + buf, buflen); + if (plen == 0) + return 0; + + return g_attrib_send(attrib, 0, buf, plen, NULL, user_data, notify); } static sdp_data_t *proto_seq_find(sdp_list_t *proto_list) diff -Nru bluez-4.101/attrib/gatt.h bluez-5.23/attrib/gatt.h --- bluez-4.101/attrib/gatt.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/gatt.h 2014-06-20 18:33:13.000000000 +0000 @@ -24,37 +24,39 @@ #include -/* GATT Profile Attribute types */ -#define GATT_PRIM_SVC_UUID 0x2800 -#define GATT_SND_SVC_UUID 0x2801 -#define GATT_INCLUDE_UUID 0x2802 -#define GATT_CHARAC_UUID 0x2803 - -/* GATT Characteristic Types */ -#define GATT_CHARAC_DEVICE_NAME 0x2A00 -#define GATT_CHARAC_APPEARANCE 0x2A01 -#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02 -#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03 -#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04 -#define GATT_CHARAC_SERVICE_CHANGED 0x2A05 - -/* GATT Characteristic Descriptors */ -#define GATT_CHARAC_EXT_PROPER_UUID 0x2900 -#define GATT_CHARAC_USER_DESC_UUID 0x2901 -#define GATT_CLIENT_CHARAC_CFG_UUID 0x2902 -#define GATT_SERVER_CHARAC_CFG_UUID 0x2903 -#define GATT_CHARAC_FMT_UUID 0x2904 -#define GATT_CHARAC_AGREG_FMT_UUID 0x2905 -#define GATT_CHARAC_VALID_RANGE_UUID 0x2906 +/* + * GATT Characteristic Property bit field + * Reference: Core SPEC 4.1 page 2183 (Table 3.5: Characteristic Properties + * bit field) defines how the Characteristic Value can be used, or how the + * characteristic descriptors (see Section 3.3.3 - page 2184) can be accessed. + * In the core spec, regular properties are included in the characteristic + * declaration, and the extended properties are defined as descriptor. + */ + +#define GATT_CHR_PROP_BROADCAST 0x01 +#define GATT_CHR_PROP_READ 0x02 +#define GATT_CHR_PROP_WRITE_WITHOUT_RESP 0x04 +#define GATT_CHR_PROP_WRITE 0x08 +#define GATT_CHR_PROP_NOTIFY 0x10 +#define GATT_CHR_PROP_INDICATE 0x20 +#define GATT_CHR_PROP_AUTH 0x40 +#define GATT_CHR_PROP_EXT_PROP 0x80 /* Client Characteristic Configuration bit field */ #define GATT_CLIENT_CHARAC_CFG_NOTIF_BIT 0x0001 #define GATT_CLIENT_CHARAC_CFG_IND_BIT 0x0002 -typedef void (*gatt_cb_t) (GSList *l, guint8 status, gpointer user_data); +typedef void (*gatt_cb_t) (uint8_t status, GSList *l, void *user_data); struct gatt_primary { char uuid[MAX_LEN_UUID_STR + 1]; + gboolean changed; + struct att_range range; +}; + +struct gatt_included { + char uuid[MAX_LEN_UUID_STR + 1]; + uint16_t handle; struct att_range range; }; @@ -65,25 +67,51 @@ uint16_t value_handle; }; +struct gatt_desc { + char uuid[MAX_LEN_UUID_STR + 1]; + uint16_t handle; + uint16_t uuid16; +}; + guint gatt_discover_primary(GAttrib *attrib, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data); +unsigned int gatt_find_included(GAttrib *attrib, uint16_t start, uint16_t end, + gatt_cb_t func, gpointer user_data); + guint gatt_discover_char(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, gatt_cb_t func, gpointer user_data); -guint gatt_read_char(GAttrib *attrib, uint16_t handle, uint16_t offset, - GAttribResultFunc func, gpointer user_data); +guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func, + gpointer user_data); -guint gatt_write_char(GAttrib *attrib, uint16_t handle, uint8_t *value, - int vlen, GAttribResultFunc func, gpointer user_data); +guint gatt_write_char(GAttrib *attrib, uint16_t handle, const uint8_t *value, + size_t vlen, GAttribResultFunc func, + gpointer user_data); + +guint gatt_discover_desc(GAttrib *attrib, uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data); + +guint gatt_reliable_write_char(GAttrib *attrib, uint16_t handle, + const uint8_t *value, size_t vlen, + GAttribResultFunc func, + gpointer user_data); -guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end, +guint gatt_execute_write(GAttrib *attrib, uint8_t flags, GAttribResultFunc func, gpointer user_data); -guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, uint8_t *value, int vlen, - GDestroyNotify notify, gpointer user_data); +guint gatt_write_cmd(GAttrib *attrib, uint16_t handle, const uint8_t *value, + int vlen, GDestroyNotify notify, gpointer user_data); +guint gatt_signed_write_cmd(GAttrib *attrib, uint16_t handle, + const uint8_t *value, int vlen, + struct bt_crypto *crypto, + const uint8_t csrk[16], + uint32_t sign_cnt, + GDestroyNotify notify, + gpointer user_data); guint gatt_read_char_by_uuid(GAttrib *attrib, uint16_t start, uint16_t end, bt_uuid_t *uuid, GAttribResultFunc func, gpointer user_data); diff -Nru bluez-4.101/attrib/gattrib.c bluez-5.23/attrib/gattrib.c --- bluez-4.101/attrib/gattrib.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/gattrib.c 2014-03-25 20:53:41.000000000 +0000 @@ -22,27 +22,33 @@ * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include +#include #include #include #include #include -#include -#include "log.h" -#include "att.h" -#include "btio.h" -#include "gattrib.h" +#include "btio/btio.h" +#include "lib/uuid.h" +#include "src/shared/util.h" +#include "src/log.h" +#include "attrib/att.h" +#include "attrib/gattrib.h" #define GATT_TIMEOUT 30 struct _GAttrib { GIOChannel *io; - gint refs; + int refs; uint8_t *buf; - int buflen; + size_t buflen; guint read_watch; guint write_watch; guint timeout_watch; @@ -52,7 +58,7 @@ guint next_cmd_id; GDestroyNotify destroy; gpointer destroy_user_data; - gboolean stale; + bool stale; }; struct command { @@ -61,7 +67,7 @@ guint8 *pdu; guint16 len; guint8 expected; - gboolean sent; + bool sent; GAttribResultFunc func; gpointer user_data; GDestroyNotify notify; @@ -70,6 +76,7 @@ struct event { guint id; guint8 expected; + guint16 handle; GAttribNotifyFunc func; gpointer user_data; GDestroyNotify notify; @@ -118,7 +125,7 @@ return 0; } -static gboolean is_response(guint8 opcode) +static bool is_response(guint8 opcode) { switch (opcode) { case ATT_OP_ERROR: @@ -134,20 +141,22 @@ case ATT_OP_PREP_WRITE_RESP: case ATT_OP_EXEC_WRITE_RESP: case ATT_OP_HANDLE_CNF: - return TRUE; + return true; } - return FALSE; + return false; } GAttrib *g_attrib_ref(GAttrib *attrib) { + int refs; + if (!attrib) return NULL; - g_atomic_int_inc(&attrib->refs); + refs = __sync_add_and_fetch(&attrib->refs, 1); - DBG("%p: ref=%d", attrib, attrib->refs); + DBG("%p: ref=%d", attrib, refs); return attrib; } @@ -214,16 +223,16 @@ void g_attrib_unref(GAttrib *attrib) { - gboolean ret; + int refs; if (!attrib) return; - ret = g_atomic_int_dec_and_test(&attrib->refs); + refs = __sync_sub_and_fetch(&attrib->refs, 1); - DBG("%p: ref=%d", attrib, attrib->refs); + DBG("%p: ref=%d", attrib, refs); - if (ret == FALSE) + if (refs > 0) return; attrib_destroy(attrib); @@ -272,7 +281,7 @@ } done: - attrib->stale = TRUE; + attrib->stale = true; g_attrib_unref(attrib); @@ -311,10 +320,16 @@ if (cmd->sent) return FALSE; - iostat = g_io_channel_write_chars(io, (gchar *) cmd->pdu, cmd->len, + iostat = g_io_channel_write_chars(io, (char *) cmd->pdu, cmd->len, &len, &gerr); - if (iostat != G_IO_STATUS_NORMAL) + if (iostat != G_IO_STATUS_NORMAL) { + if (gerr) { + error("%s", gerr->message); + g_error_free(gerr); + } + return FALSE; + } if (cmd->expected == 0) { g_queue_pop_head(queue); @@ -323,7 +338,7 @@ return TRUE; } - cmd->sent = TRUE; + cmd->sent = true; if (attrib->timeout_watch == 0) attrib->timeout_watch = g_timeout_add_seconds(GATT_TIMEOUT, @@ -351,6 +366,30 @@ can_write_data, attrib, destroy_sender); } +static bool match_event(struct event *evt, const uint8_t *pdu, gsize len) +{ + guint16 handle; + + if (evt->expected == GATTRIB_ALL_EVENTS) + return true; + + if (!is_response(pdu[0]) && evt->expected == GATTRIB_ALL_REQS) + return true; + + if (evt->expected == pdu[0] && evt->handle == GATTRIB_ALL_HANDLES) + return true; + + if (len < 3) + return false; + + handle = get_le16(&pdu[1]); + + if (evt->expected == pdu[0] && evt->handle == handle) + return true; + + return false; +} + static gboolean received_data(GIOChannel *io, GIOCondition cond, gpointer data) { struct _GAttrib *attrib = data; @@ -359,19 +398,27 @@ uint8_t buf[512], status; gsize len; GIOStatus iostat; - gboolean norequests, noresponses; if (attrib->stale) return FALSE; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + struct command *c; + + while ((c = g_queue_pop_head(attrib->requests))) { + if (c->func) + c->func(ATT_ECODE_IO, NULL, 0, c->user_data); + command_destroy(c); + } + attrib->read_watch = 0; + return FALSE; } memset(buf, 0, sizeof(buf)); - iostat = g_io_channel_read_chars(io, (gchar *) buf, sizeof(buf), + iostat = g_io_channel_read_chars(io, (char *) buf, sizeof(buf), &len, NULL); if (iostat != G_IO_STATUS_NORMAL) { status = ATT_ECODE_IO; @@ -381,14 +428,11 @@ for (l = attrib->events; l; l = l->next) { struct event *evt = l->data; - if (evt->expected == buf[0] || - evt->expected == GATTRIB_ALL_EVENTS || - (is_response(buf[0]) == FALSE && - evt->expected == GATTRIB_ALL_REQS)) + if (match_event(evt, buf, len)) evt->func(buf, len, evt->user_data); } - if (is_response(buf[0]) == FALSE) + if (!is_response(buf[0])) return TRUE; if (attrib->timeout_watch > 0) { @@ -415,10 +459,9 @@ status = 0; done: - norequests = attrib->requests == NULL || - g_queue_is_empty(attrib->requests); - noresponses = attrib->responses == NULL || - g_queue_is_empty(attrib->responses); + if (!g_queue_is_empty(attrib->requests) || + !g_queue_is_empty(attrib->responses)) + wake_up_sender(attrib); if (cmd) { if (cmd->func) @@ -427,9 +470,6 @@ command_destroy(cmd); } - if (!norequests || !noresponses) - wake_up_sender(attrib); - return TRUE; } @@ -444,11 +484,8 @@ g_io_channel_set_encoding(io, NULL, NULL); g_io_channel_set_buffered(io, FALSE); - bt_io_get(io, BT_IO_L2CAP, &gerr, - BT_IO_OPT_IMTU, &imtu, - BT_IO_OPT_CID, &cid, - BT_IO_OPT_INVALID); - + bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, + BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID); if (gerr) { error("%s", gerr->message); g_error_free(gerr); @@ -475,12 +512,13 @@ return g_attrib_ref(attrib); } -guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode, - const guint8 *pdu, guint16 len, GAttribResultFunc func, - gpointer user_data, GDestroyNotify notify) +guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len, + GAttribResultFunc func, gpointer user_data, + GDestroyNotify notify) { struct command *c; GQueue *queue; + uint8_t opcode; if (attrib->stale) return 0; @@ -489,6 +527,8 @@ if (c == NULL) return 0; + opcode = pdu[0]; + c->opcode = opcode; c->expected = opcode2expected(opcode); c->pdu = g_malloc(len); @@ -526,7 +566,7 @@ return c->id; } -static gint command_cmp_by_id(gconstpointer a, gconstpointer b) +static int command_cmp_by_id(gconstpointer a, gconstpointer b) { const struct command *cmd = a; guint id = GPOINTER_TO_UINT(b); @@ -617,7 +657,7 @@ return TRUE; } -uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len) +uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len) { if (len == NULL) return NULL; @@ -639,7 +679,7 @@ return TRUE; } -guint g_attrib_register(GAttrib *attrib, guint8 opcode, +guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle, GAttribNotifyFunc func, gpointer user_data, GDestroyNotify notify) { @@ -651,6 +691,7 @@ return 0; event->expected = opcode; + event->handle = handle; event->func = func; event->user_data = user_data; event->notify = notify; @@ -661,7 +702,7 @@ return event->id; } -static gint event_cmp_by_id(gconstpointer a, gconstpointer b) +static int event_cmp_by_id(gconstpointer a, gconstpointer b) { const struct event *evt = a; guint id = GPOINTER_TO_UINT(b); @@ -673,7 +714,7 @@ { BtIOSecLevel sec_level; - if (!bt_io_get(attrib->io, BT_IO_L2CAP, NULL, + if (!bt_io_get(attrib->io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level, BT_IO_OPT_INVALID)) return FALSE; @@ -686,6 +727,11 @@ struct event *evt; GSList *l; + if (id == 0) { + warn("%s: invalid id", __func__); + return FALSE; + } + l = g_slist_find_custom(attrib->events, GUINT_TO_POINTER(id), event_cmp_by_id); if (l == NULL) diff -Nru bluez-4.101/attrib/gattrib.h bluez-5.23/attrib/gattrib.h --- bluez-4.101/attrib/gattrib.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/gattrib.h 2012-12-24 17:46:54.000000000 +0000 @@ -30,6 +30,7 @@ #define GATTRIB_ALL_EVENTS 0xFF #define GATTRIB_ALL_REQS 0xFE +#define GATTRIB_ALL_HANDLES 0x0000 struct _GAttrib; typedef struct _GAttrib GAttrib; @@ -50,9 +51,9 @@ gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy, gpointer user_data); -guint g_attrib_send(GAttrib *attrib, guint id, guint8 opcode, - const guint8 *pdu, guint16 len, GAttribResultFunc func, - gpointer user_data, GDestroyNotify notify); +guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len, + GAttribResultFunc func, gpointer user_data, + GDestroyNotify notify); gboolean g_attrib_cancel(GAttrib *attrib, guint id); gboolean g_attrib_cancel_all(GAttrib *attrib); @@ -60,13 +61,13 @@ gboolean g_attrib_set_debug(GAttrib *attrib, GAttribDebugFunc func, gpointer user_data); -guint g_attrib_register(GAttrib *attrib, guint8 opcode, - GAttribNotifyFunc func, gpointer user_data, - GDestroyNotify notify); +guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle, + GAttribNotifyFunc func, gpointer user_data, + GDestroyNotify notify); gboolean g_attrib_is_encrypted(GAttrib *attrib); -uint8_t *g_attrib_get_buffer(GAttrib *attrib, int *len); +uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len); gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu); gboolean g_attrib_unregister(GAttrib *attrib, guint id); diff -Nru bluez-4.101/attrib/gatt-service.c bluez-5.23/attrib/gatt-service.c --- bluez-4.101/attrib/gatt-service.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/gatt-service.c 2014-03-25 20:53:41.000000000 +0000 @@ -27,17 +27,17 @@ #endif #include -#include -#include -#include - -#include "gattrib.h" -#include "att.h" -#include "gatt.h" -#include "att-database.h" -#include "attrib-server.h" -#include "gatt-service.h" -#include "log.h" + +#include "src/adapter.h" +#include "src/shared/util.h" +#include "lib/uuid.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "attrib/att-database.h" +#include "src/attrib-server.h" +#include "attrib/gatt-service.h" +#include "src/log.h" struct gatt_info { bt_uuid_t uuid; @@ -56,6 +56,15 @@ void *user_data; }; +static inline void put_uuid_le(const bt_uuid_t *src, void *dst) +{ + if (src->type == BT_UUID16) + put_le16(src->value.u16, dst); + else + /* Convert from 128-bit BE to LE */ + bswap_128(&src->value.u128, dst); +} + static GSList *parse_opts(gatt_option opt1, va_list args) { gatt_option opt = opt1; @@ -68,16 +77,22 @@ while (opt != GATT_OPT_INVALID) { switch (opt) { - case GATT_OPT_CHR_UUID: + case GATT_OPT_CHR_UUID16: bt_uuid16_create(&info->uuid, va_arg(args, int)); /* characteristic declaration and value */ info->num_attrs += 2; break; + case GATT_OPT_CHR_UUID: + memcpy(&info->uuid, va_arg(args, bt_uuid_t *), + sizeof(bt_uuid_t)); + /* characteristic declaration and value */ + info->num_attrs += 2; + break; case GATT_OPT_CHR_PROPS: info->props = va_arg(args, int); - if (info->props & (ATT_CHAR_PROPER_NOTIFY | - ATT_CHAR_PROPER_INDICATE)) + if (info->props & (GATT_CHR_PROP_NOTIFY | + GATT_CHR_PROP_INDICATE)) /* client characteristic configuration */ info->num_attrs += 1; @@ -108,7 +123,7 @@ } opt = va_arg(args, gatt_option); - if (opt == GATT_OPT_CHR_UUID) { + if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID) { info = g_new0(struct gatt_info, 1); l = g_slist_append(l, info); } @@ -124,14 +139,8 @@ uint8_t atval[16]; int len; - if (uuid->type == BT_UUID16) { - att_put_u16(uuid->value.u16, &atval[0]); - len = 2; - } else if (uuid->type == BT_UUID128) { - att_put_u128(uuid->value.u128, &atval[0]); - len = 16; - } else - return NULL; + put_uuid_le(uuid, &atval[0]); + len = bt_uuid_len(uuid); bt_uuid16_create(&bt_uuid, svc); @@ -139,7 +148,7 @@ ATT_NOT_PERMITTED, atval, len); } -static int att_read_reqs(int authorization, int authentication, uint8_t props) +static int att_read_req(int authorization, int authentication, uint8_t props) { if (authorization == GATT_CHR_VALUE_READ || authorization == GATT_CHR_VALUE_BOTH) @@ -147,13 +156,13 @@ else if (authentication == GATT_CHR_VALUE_READ || authentication == GATT_CHR_VALUE_BOTH) return ATT_AUTHENTICATION; - else if (!(props & ATT_CHAR_PROPER_READ)) + else if (!(props & GATT_CHR_PROP_READ)) return ATT_NOT_PERMITTED; return ATT_NONE; } -static int att_write_reqs(int authorization, int authentication, uint8_t props) +static int att_write_req(int authorization, int authentication, uint8_t props) { if (authorization == GATT_CHR_VALUE_WRITE || authorization == GATT_CHR_VALUE_BOTH) @@ -161,14 +170,14 @@ else if (authentication == GATT_CHR_VALUE_WRITE || authentication == GATT_CHR_VALUE_BOTH) return ATT_AUTHENTICATION; - else if (!(props & (ATT_CHAR_PROPER_WRITE | - ATT_CHAR_PROPER_WRITE_WITHOUT_RESP))) + else if (!(props & (GATT_CHR_PROP_WRITE | + GATT_CHR_PROP_WRITE_WITHOUT_RESP))) return ATT_NOT_PERMITTED; return ATT_NONE; } -static gint find_callback(gconstpointer a, gconstpointer b) +static int find_callback(gconstpointer a, gconstpointer b) { const struct attrib_cb *cb = a; unsigned int event = GPOINTER_TO_UINT(b); @@ -179,26 +188,27 @@ static gboolean add_characteristic(struct btd_adapter *adapter, uint16_t *handle, struct gatt_info *info) { - int read_reqs, write_reqs; + int read_req, write_req; uint16_t h = *handle; struct attribute *a; bt_uuid_t bt_uuid; - uint8_t atval[5]; + uint8_t atval[ATT_MAX_VALUE_LEN]; GSList *l; - if (!info->uuid.value.u16 || !info->props) { + if ((info->uuid.type != BT_UUID16 && info->uuid.type != BT_UUID128) || + !info->props) { error("Characteristic UUID or properties are missing"); return FALSE; } - read_reqs = att_read_reqs(info->authorization, info->authentication, + read_req = att_read_req(info->authorization, info->authentication, info->props); - write_reqs = att_write_reqs(info->authorization, info->authentication, + write_req = att_write_req(info->authorization, info->authentication, info->props); /* TODO: static characteristic values are not supported, therefore a * callback must be always provided if a read/write property is set */ - if (read_reqs != ATT_NOT_PERMITTED) { + if (read_req != ATT_NOT_PERMITTED) { gpointer reqs = GUINT_TO_POINTER(ATTRIB_READ); if (!g_slist_find_custom(info->callbacks, reqs, @@ -208,7 +218,7 @@ } } - if (write_reqs != ATT_NOT_PERMITTED) { + if (write_req != ATT_NOT_PERMITTED) { gpointer reqs = GUINT_TO_POINTER(ATTRIB_WRITE); if (!g_slist_find_custom(info->callbacks, reqs, @@ -221,14 +231,14 @@ /* characteristic declaration */ bt_uuid16_create(&bt_uuid, GATT_CHARAC_UUID); atval[0] = info->props; - att_put_u16(h + 1, &atval[1]); - att_put_u16(info->uuid.value.u16, &atval[3]); + put_le16(h + 1, &atval[1]); + put_uuid_le(&info->uuid, &atval[3]); if (attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED, - atval, sizeof(atval)) == NULL) + atval, 3 + info->uuid.type / 8) == NULL) return FALSE; /* characteristic value */ - a = attrib_db_add(adapter, h++, &info->uuid, read_reqs, write_reqs, + a = attrib_db_add(adapter, h++, &info->uuid, read_req, write_req, NULL, 0); if (a == NULL) return FALSE; @@ -252,7 +262,7 @@ *info->value_handle = a->handle; /* client characteristic configuration descriptor */ - if (info->props & (ATT_CHAR_PROPER_NOTIFY | ATT_CHAR_PROPER_INDICATE)) { + if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) { uint8_t cfg_val[2]; bt_uuid16_create(&bt_uuid, GATT_CLIENT_CHARAC_CFG_UUID); @@ -341,7 +351,7 @@ } g_assert(size < USHRT_MAX); - g_assert(h - start_handle == (uint16_t) size); + g_assert(h == 0 || (h - start_handle == (uint16_t) size)); g_slist_free_full(chrs, free_gatt_info); return TRUE; diff -Nru bluez-4.101/attrib/gatt-service.h bluez-5.23/attrib/gatt-service.h --- bluez-4.101/attrib/gatt-service.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/gatt-service.h 2013-08-08 01:43:24.000000000 +0000 @@ -24,7 +24,13 @@ typedef enum { GATT_OPT_INVALID = 0, + + /* bt_uuid_t* value */ GATT_OPT_CHR_UUID, + + /* a uint16 value */ + GATT_OPT_CHR_UUID16, + GATT_OPT_CHR_PROPS, GATT_OPT_CHR_VALUE_CB, GATT_OPT_CHR_AUTHENTICATION, diff -Nru bluez-4.101/attrib/gatttool.c bluez-5.23/attrib/gatttool.c --- bluez-4.101/attrib/gatttool.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/gatttool.c 2014-08-06 17:25:36.000000000 +0000 @@ -34,26 +34,26 @@ #include #include #include -#include +#include "src/shared/util.h" +#include "lib/uuid.h" #include "att.h" -#include "btio.h" +#include "btio/btio.h" #include "gattrib.h" #include "gatt.h" #include "gatttool.h" -static gchar *opt_src = NULL; -static gchar *opt_dst = NULL; -static gchar *opt_dst_type = NULL; -static gchar *opt_value = NULL; -static gchar *opt_sec_level = NULL; +static char *opt_src = NULL; +static char *opt_dst = NULL; +static char *opt_dst_type = NULL; +static char *opt_value = NULL; +static char *opt_sec_level = NULL; static bt_uuid_t *opt_uuid = NULL; static int opt_start = 0x0001; static int opt_end = 0xffff; static int opt_handle = -1; static int opt_mtu = 0; static int opt_psm = 0; -static int opt_offset = 0; static gboolean opt_primary = FALSE; static gboolean opt_characteristics = FALSE; static gboolean opt_char_read = FALSE; @@ -75,10 +75,11 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { GAttrib *attrib = user_data; - uint8_t opdu[ATT_MAX_MTU]; + uint8_t *opdu; uint16_t handle, i, olen = 0; + size_t plen; - handle = att_get_u16(&pdu[1]); + handle = get_le16(&pdu[1]); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: @@ -100,20 +101,21 @@ if (pdu[0] == ATT_OP_HANDLE_NOTIFY) return; - olen = enc_confirmation(opdu, sizeof(opdu)); + opdu = g_attrib_get_buffer(attrib, &plen); + olen = enc_confirmation(opdu, plen); if (olen > 0) - g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL); + g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL); } static gboolean listen_start(gpointer user_data) { GAttrib *attrib = user_data; - g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler, - attrib, NULL); - g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler, - attrib, NULL); + g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES, + events_handler, attrib, NULL); + g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES, + events_handler, attrib, NULL); return FALSE; } @@ -136,7 +138,7 @@ operation(attrib); } -static void primary_all_cb(GSList *services, guint8 status, gpointer user_data) +static void primary_all_cb(uint8_t status, GSList *services, void *user_data) { GSList *l; @@ -156,8 +158,7 @@ g_main_loop_quit(event_loop); } -static void primary_by_uuid_cb(GSList *ranges, guint8 status, - gpointer user_data) +static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data) { GSList *l; @@ -190,8 +191,8 @@ return FALSE; } -static void char_discovered_cb(GSList *characteristics, guint8 status, - gpointer user_data) +static void char_discovered_cb(uint8_t status, GSList *characteristics, + void *user_data) { GSList *l; @@ -226,15 +227,18 @@ static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { - uint8_t value[ATT_MAX_MTU]; - int i, vlen; + uint8_t value[plen]; + ssize_t vlen; + int i; if (status != 0) { g_printerr("Characteristic value/descriptor read failed: %s\n", att_ecode2str(status)); goto done; } - if (!dec_read_resp(pdu, plen, value, &vlen)) { + + vlen = dec_read_resp(pdu, plen, value, sizeof(value)); + if (vlen < 0) { g_printerr("Protocol error\n"); goto done; } @@ -244,21 +248,16 @@ g_print("\n"); done: - if (opt_listen == FALSE) + if (!opt_listen) g_main_loop_quit(event_loop); } static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { - struct characteristic_data *char_data = user_data; struct att_data_list *list; int i; - if (status == ATT_ECODE_ATTR_NOT_FOUND && - char_data->start != opt_start) - goto done; - if (status != 0) { g_printerr("Read characteristics by UUID failed: %s\n", att_ecode2str(status)); @@ -273,9 +272,7 @@ uint8_t *value = list->data[i]; int j; - char_data->start = att_get_u16(value) + 1; - - g_print("handle: 0x%04x \t value: ", att_get_u16(value)); + g_print("handle: 0x%04x \t value: ", get_le16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) g_print("%02x ", *value); @@ -285,7 +282,6 @@ att_data_list_free(list); done: - g_free(char_data); g_main_loop_quit(event_loop); } @@ -294,15 +290,9 @@ GAttrib *attrib = user_data; if (opt_uuid != NULL) { - struct characteristic_data *char_data; - - char_data = g_new(struct characteristic_data, 1); - char_data->attrib = attrib; - char_data->start = opt_start; - char_data->end = opt_end; gatt_read_char_by_uuid(attrib, opt_start, opt_end, opt_uuid, - char_read_by_uuid_cb, char_data); + char_read_by_uuid_cb, NULL); return FALSE; } @@ -313,7 +303,7 @@ return FALSE; } - gatt_read_char(attrib, opt_handle, opt_offset, char_read_cb, attrib); + gatt_read_char(attrib, opt_handle, char_read_cb, attrib); return FALSE; } @@ -350,6 +340,7 @@ gatt_write_cmd(attrib, opt_handle, value, len, mainloop_quit, value); + g_free(value); return FALSE; error: @@ -366,7 +357,7 @@ goto done; } - if (!dec_write_resp(pdu, plen)) { + if (!dec_write_resp(pdu, plen) && !dec_exec_write_resp(pdu, plen)) { g_printerr("Protocol error\n"); goto done; } @@ -374,7 +365,7 @@ g_print("Characteristic value was written successfully\n"); done: - if (opt_listen == FALSE) + if (!opt_listen) g_main_loop_quit(event_loop); } @@ -403,6 +394,7 @@ gatt_write_char(attrib, opt_handle, value, len, char_write_req_cb, NULL); + g_free(value); return FALSE; error: @@ -410,45 +402,24 @@ return FALSE; } -static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen, - gpointer user_data) +static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data) { - struct att_data_list *list; - guint8 format; - int i; + GSList *l; - if (status != 0) { - g_printerr("Discover all characteristic descriptors failed: " - "%s\n", att_ecode2str(status)); - goto done; + if (status) { + g_printerr("Discover descriptors failed: %s\n", + att_ecode2str(status)); + return; } - list = dec_find_info_resp(pdu, plen, &format); - if (list == NULL) - goto done; - - for (i = 0; i < list->num; i++) { - char uuidstr[MAX_LEN_UUID_STR]; - uint16_t handle; - uint8_t *value; - bt_uuid_t uuid; - - value = list->data[i]; - handle = att_get_u16(value); - - if (format == 0x01) - uuid = att_get_uuid16(&value[2]); - else - uuid = att_get_uuid128(&value[2]); + for (l = descriptors; l; l = l->next) { + struct gatt_desc *desc = l->data; - bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR); - g_print("handle = 0x%04x, uuid = %s\n", handle, uuidstr); + g_print("handle = 0x%04x, uuid = %s\n", desc->handle, + desc->uuid); } - att_data_list_free(list); - -done: - if (opt_listen == FALSE) + if (!opt_listen) g_main_loop_quit(event_loop); } @@ -456,7 +427,8 @@ { GAttrib *attrib = user_data; - gatt_find_info(attrib, opt_start, opt_end, char_desc_cb, NULL); + gatt_discover_desc(attrib, opt_start, opt_end, NULL, char_desc_cb, + NULL); return FALSE; } @@ -493,8 +465,6 @@ { "value", 'n' , 0, G_OPTION_ARG_STRING, &opt_value, "Write characteristic value (required for write operation)", "0x0001" }, - { "offset", 'o', 0, G_OPTION_ARG_INT, &opt_offset, - "Offset to long read characteristic by handle", "N"}, {NULL}, }; @@ -571,9 +541,9 @@ g_option_context_add_group(context, char_rw_group); g_option_group_add_entries(char_rw_group, char_rw_options); - if (g_option_context_parse(context, &argc, &argv, &gerr) == FALSE) { + if (!g_option_context_parse(context, &argc, &argv, &gerr)) { g_printerr("%s\n", gerr->message); - g_error_free(gerr); + g_clear_error(&gerr); } if (opt_interactive) { @@ -594,16 +564,24 @@ else if (opt_char_desc) operation = characteristics_desc; else { - gchar *help = g_option_context_get_help(context, TRUE, NULL); + char *help = g_option_context_get_help(context, TRUE, NULL); g_print("%s\n", help); g_free(help); got_error = TRUE; goto done; } + if (opt_dst == NULL) { + g_print("Remote Bluetooth address required\n"); + got_error = TRUE; + goto done; + } + chan = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level, - opt_psm, opt_mtu, connect_cb); + opt_psm, opt_mtu, connect_cb, &gerr); if (chan == NULL) { + g_printerr("%s\n", gerr->message); + g_clear_error(&gerr); got_error = TRUE; goto done; } diff -Nru bluez-4.101/attrib/gatttool.h bluez-5.23/attrib/gatttool.h --- bluez-4.101/attrib/gatttool.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/gatttool.h 2013-05-14 01:04:08.000000000 +0000 @@ -21,9 +21,10 @@ * */ -int interactive(const gchar *src, const gchar *dst, const gchar *dst_type, - gboolean le); -GIOChannel *gatt_connect(const gchar *src, const gchar *dst, - const gchar *dst_type, const gchar *sec_level, - int psm, int mtu, BtIOConnect connect_cb); +int interactive(const char *src, const char *dst, const char *dst_type, + int psm); +GIOChannel *gatt_connect(const char *src, const char *dst, + const char *dst_type, const char *sec_level, + int psm, int mtu, BtIOConnect connect_cb, + GError **gerr); size_t gatt_attr_data_from_string(const char *str, uint8_t **data); diff -Nru bluez-4.101/attrib/interactive.c bluez-5.23/attrib/interactive.c --- bluez-4.101/attrib/interactive.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/interactive.c 2014-05-19 08:51:52.000000000 +0000 @@ -20,67 +20,76 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include +#include #include #include +#include +#include +#include #include -#include - #include #include +#include "src/shared/util.h" +#include "lib/uuid.h" +#include "btio/btio.h" #include "att.h" -#include "btio.h" #include "gattrib.h" #include "gatt.h" #include "gatttool.h" +#include "client/display.h" static GIOChannel *iochannel = NULL; static GAttrib *attrib = NULL; static GMainLoop *event_loop; static GString *prompt; -static gchar *opt_src = NULL; -static gchar *opt_dst = NULL; -static gchar *opt_dst_type = NULL; -static gchar *opt_sec_level = NULL; +static char *opt_src = NULL; +static char *opt_dst = NULL; +static char *opt_dst_type = NULL; +static char *opt_sec_level = NULL; static int opt_psm = 0; static int opt_mtu = 0; - -struct characteristic_data { - uint16_t orig_start; - uint16_t start; - uint16_t end; - bt_uuid_t uuid; -}; +static int start; +static int end; static void cmd_help(int argcp, char **argvp); -enum state { +static enum state { STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED } conn_state; +#define error(fmt, arg...) \ + rl_printf(COLOR_RED "Error: " COLOR_OFF fmt, ## arg) + +#define failed(fmt, arg...) \ + rl_printf(COLOR_RED "Command Failed: " COLOR_OFF fmt, ## arg) + static char *get_prompt(void) { - if (conn_state == STATE_CONNECTING) { - g_string_assign(prompt, "Connecting... "); - return prompt->str; - } - if (conn_state == STATE_CONNECTED) - g_string_assign(prompt, "[CON]"); + g_string_assign(prompt, COLOR_BLUE); else - g_string_assign(prompt, "[ ]"); + g_string_assign(prompt, ""); if (opt_dst) g_string_append_printf(prompt, "[%17s]", opt_dst); else g_string_append_printf(prompt, "[%17s]", ""); + if (conn_state == STATE_CONNECTED) + g_string_append(prompt, COLOR_OFF); + if (opt_psm) g_string_append(prompt, "[BR]"); else @@ -96,58 +105,64 @@ { conn_state = st; rl_set_prompt(get_prompt()); - rl_redisplay(); } static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) { - uint8_t opdu[ATT_MAX_MTU]; + uint8_t *opdu; uint16_t handle, i, olen; + size_t plen; + GString *s; - handle = att_get_u16(&pdu[1]); + handle = get_le16(&pdu[1]); - printf("\n"); switch (pdu[0]) { case ATT_OP_HANDLE_NOTIFY: - printf("Notification handle = 0x%04x value: ", handle); + s = g_string_new(NULL); + g_string_printf(s, "Notification handle = 0x%04x value: ", + handle); break; case ATT_OP_HANDLE_IND: - printf("Indication handle = 0x%04x value: ", handle); + s = g_string_new(NULL); + g_string_printf(s, "Indication handle = 0x%04x value: ", + handle); break; default: - printf("Invalid opcode\n"); + error("Invalid opcode\n"); return; } for (i = 3; i < len; i++) - printf("%02x ", pdu[i]); + g_string_append_printf(s, "%02x ", pdu[i]); - printf("\n"); - rl_forced_update_display(); + rl_printf("%s\n", s->str); + g_string_free(s, TRUE); if (pdu[0] == ATT_OP_HANDLE_NOTIFY) return; - olen = enc_confirmation(opdu, sizeof(opdu)); + opdu = g_attrib_get_buffer(attrib, &plen); + olen = enc_confirmation(opdu, plen); if (olen > 0) - g_attrib_send(attrib, 0, opdu[0], opdu, olen, NULL, NULL, NULL); + g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL); } static void connect_cb(GIOChannel *io, GError *err, gpointer user_data) { if (err) { - printf("connect error: %s\n", err->message); set_state(STATE_DISCONNECTED); + error("%s\n", err->message); return; } attrib = g_attrib_new(iochannel); - g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, events_handler, - attrib, NULL); - g_attrib_register(attrib, ATT_OP_HANDLE_IND, events_handler, - attrib, NULL); + g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES, + events_handler, attrib, NULL); + g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES, + events_handler, attrib, NULL); set_state(STATE_CONNECTED); + rl_printf("Connection successful\n"); } static void disconnect_io() @@ -166,176 +181,173 @@ set_state(STATE_DISCONNECTED); } -static void primary_all_cb(GSList *services, guint8 status, gpointer user_data) +static void primary_all_cb(uint8_t status, GSList *services, void *user_data) { GSList *l; if (status) { - printf("Discover all primary services failed: %s\n", - att_ecode2str(status)); + error("Discover all primary services failed: %s\n", + att_ecode2str(status)); + return; + } + + if (services == NULL) { + error("No primary service found\n"); return; } - printf("\n"); for (l = services; l; l = l->next) { struct gatt_primary *prim = l->data; - printf("attr handle: 0x%04x, end grp handle: 0x%04x " - "uuid: %s\n", prim->range.start, prim->range.end, prim->uuid); + rl_printf("attr handle: 0x%04x, end grp handle: 0x%04x uuid: %s\n", + prim->range.start, prim->range.end, prim->uuid); } - - rl_forced_update_display(); } -static void primary_by_uuid_cb(GSList *ranges, guint8 status, - gpointer user_data) +static void primary_by_uuid_cb(uint8_t status, GSList *ranges, void *user_data) { GSList *l; if (status) { - printf("Discover primary services by UUID failed: %s\n", + error("Discover primary services by UUID failed: %s\n", att_ecode2str(status)); return; } - printf("\n"); + if (ranges == NULL) { + error("No service UUID found\n"); + return; + } + for (l = ranges; l; l = l->next) { struct att_range *range = l->data; - g_print("Starting handle: 0x%04x Ending handle: 0x%04x\n", + rl_printf("Starting handle: 0x%04x Ending handle: 0x%04x\n", range->start, range->end); } +} + +static void included_cb(uint8_t status, GSList *includes, void *user_data) +{ + GSList *l; - rl_forced_update_display(); + if (status) { + error("Find included services failed: %s\n", + att_ecode2str(status)); + return; + } + + if (includes == NULL) { + rl_printf("No included services found for this range\n"); + return; + } + + for (l = includes; l; l = l->next) { + struct gatt_included *incl = l->data; + rl_printf("handle: 0x%04x, start handle: 0x%04x, " + "end handle: 0x%04x uuid: %s\n", + incl->handle, incl->range.start, + incl->range.end, incl->uuid); + } } -static void char_cb(GSList *characteristics, guint8 status, gpointer user_data) +static void char_cb(uint8_t status, GSList *characteristics, void *user_data) { GSList *l; if (status) { - printf("Discover all characteristics failed: %s\n", + error("Discover all characteristics failed: %s\n", att_ecode2str(status)); return; } - printf("\n"); for (l = characteristics; l; l = l->next) { struct gatt_char *chars = l->data; - printf("handle: 0x%04x, char properties: 0x%02x, char value " + rl_printf("handle: 0x%04x, char properties: 0x%02x, char value " "handle: 0x%04x, uuid: %s\n", chars->handle, chars->properties, chars->value_handle, chars->uuid); } - - rl_forced_update_display(); } -static void char_desc_cb(guint8 status, const guint8 *pdu, guint16 plen, - gpointer user_data) +static void char_desc_cb(uint8_t status, GSList *descriptors, void *user_data) { - struct att_data_list *list; - guint8 format; - int i; + GSList *l; - if (status != 0) { - printf("Discover all characteristic descriptors failed: " - "%s\n", att_ecode2str(status)); + if (status) { + error("Discover descriptors failed: %s\n", + att_ecode2str(status)); return; } - list = dec_find_info_resp(pdu, plen, &format); - if (list == NULL) - return; + for (l = descriptors; l; l = l->next) { + struct gatt_desc *desc = l->data; - printf("\n"); - for (i = 0; i < list->num; i++) { - char uuidstr[MAX_LEN_UUID_STR]; - uint16_t handle; - uint8_t *value; - bt_uuid_t uuid; - - value = list->data[i]; - handle = att_get_u16(value); - - if (format == 0x01) - uuid = att_get_uuid16(&value[2]); - else - uuid = att_get_uuid128(&value[2]); - - bt_uuid_to_string(&uuid, uuidstr, MAX_LEN_UUID_STR); - printf("handle: 0x%04x, uuid: %s\n", handle, uuidstr); + rl_printf("handle: 0x%04x, uuid: %s\n", desc->handle, + desc->uuid); } - - att_data_list_free(list); - - rl_forced_update_display(); } static void char_read_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { - uint8_t value[ATT_MAX_MTU]; - int i, vlen; + uint8_t value[plen]; + ssize_t vlen; + int i; + GString *s; if (status != 0) { - printf("Characteristic value/descriptor read failed: %s\n", + error("Characteristic value/descriptor read failed: %s\n", att_ecode2str(status)); return; } - if (!dec_read_resp(pdu, plen, value, &vlen)) { - printf("Protocol error\n"); + vlen = dec_read_resp(pdu, plen, value, sizeof(value)); + if (vlen < 0) { + error("Protocol error\n"); return; } - printf("\nCharacteristic value/descriptor: "); + s = g_string_new("Characteristic value/descriptor: "); for (i = 0; i < vlen; i++) - printf("%02x ", value[i]); - printf("\n"); + g_string_append_printf(s, "%02x ", value[i]); - rl_forced_update_display(); + rl_printf("%s\n", s->str); + g_string_free(s, TRUE); } static void char_read_by_uuid_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { - struct characteristic_data *char_data = user_data; struct att_data_list *list; int i; - - if (status == ATT_ECODE_ATTR_NOT_FOUND && - char_data->start != char_data->orig_start) - goto done; + GString *s; if (status != 0) { - printf("Read characteristics by UUID failed: %s\n", + error("Read characteristics by UUID failed: %s\n", att_ecode2str(status)); - goto done; + return; } list = dec_read_by_type_resp(pdu, plen); if (list == NULL) - goto done; + return; + s = g_string_new(NULL); for (i = 0; i < list->num; i++) { uint8_t *value = list->data[i]; int j; - char_data->start = att_get_u16(value) + 1; - - printf("\nhandle: 0x%04x \t value: ", att_get_u16(value)); + g_string_printf(s, "handle: 0x%04x \t value: ", + get_le16(value)); value += 2; for (j = 0; j < list->len - 2; j++, value++) - printf("%02x ", *value); - printf("\n"); + g_string_append_printf(s, "%02x ", *value); + + rl_printf("%s\n", s->str); } att_data_list_free(list); - - rl_forced_update_display(); - -done: - g_free(char_data); + g_string_free(s, TRUE); } static void cmd_exit(int argcp, char **argvp) @@ -354,6 +366,8 @@ static void cmd_connect(int argcp, char **argvp) { + GError *gerr = NULL; + if (conn_state != STATE_DISCONNECTED) return; @@ -369,16 +383,19 @@ } if (opt_dst == NULL) { - printf("Remote Bluetooth address required\n"); + error("Remote Bluetooth address required\n"); return; } + rl_printf("Attempting to connect to %s\n", opt_dst); set_state(STATE_CONNECTING); iochannel = gatt_connect(opt_src, opt_dst, opt_dst_type, opt_sec_level, - opt_psm, opt_mtu, connect_cb); - if (iochannel == NULL) + opt_psm, opt_mtu, connect_cb, &gerr); + if (iochannel == NULL) { set_state(STATE_DISCONNECTED); - else + error("%s\n", gerr->message); + g_error_free(gerr); + } else g_io_add_watch(iochannel, G_IO_HUP, channel_watcher, NULL); } @@ -392,7 +409,7 @@ bt_uuid_t uuid; if (conn_state != STATE_CONNECTED) { - printf("Command failed: disconnected\n"); + failed("Disconnected\n"); return; } @@ -402,7 +419,7 @@ } if (bt_string_to_uuid(&uuid, argvp[1]) < 0) { - printf("Invalid UUID\n"); + error("Invalid UUID\n"); return; } @@ -422,20 +439,50 @@ return dst; } +static void cmd_included(int argcp, char **argvp) +{ + int start = 0x0001; + int end = 0xffff; + + if (conn_state != STATE_CONNECTED) { + failed("Disconnected\n"); + return; + } + + if (argcp > 1) { + start = strtohandle(argvp[1]); + if (start < 0) { + error("Invalid start handle: %s\n", argvp[1]); + return; + } + end = start; + } + + if (argcp > 2) { + end = strtohandle(argvp[2]); + if (end < 0) { + error("Invalid end handle: %s\n", argvp[2]); + return; + } + } + + gatt_find_included(attrib, start, end, included_cb, NULL); +} + static void cmd_char(int argcp, char **argvp) { int start = 0x0001; int end = 0xffff; if (conn_state != STATE_CONNECTED) { - printf("Command failed: disconnected\n"); + failed("Disconnected\n"); return; } if (argcp > 1) { start = strtohandle(argvp[1]); if (start < 0) { - printf("Invalid start handle: %s\n", argvp[1]); + error("Invalid start handle: %s\n", argvp[1]); return; } } @@ -443,7 +490,7 @@ if (argcp > 2) { end = strtohandle(argvp[2]); if (end < 0) { - printf("Invalid end handle: %s\n", argvp[2]); + error("Invalid end handle: %s\n", argvp[2]); return; } } @@ -452,7 +499,7 @@ bt_uuid_t uuid; if (bt_string_to_uuid(&uuid, argvp[3]) < 0) { - printf("Invalid UUID\n"); + error("Invalid UUID\n"); return; } @@ -465,94 +512,80 @@ static void cmd_char_desc(int argcp, char **argvp) { - int start = 0x0001; - int end = 0xffff; - if (conn_state != STATE_CONNECTED) { - printf("Command failed: disconnected\n"); + failed("Disconnected\n"); return; } if (argcp > 1) { start = strtohandle(argvp[1]); if (start < 0) { - printf("Invalid start handle: %s\n", argvp[1]); + error("Invalid start handle: %s\n", argvp[1]); return; } - } + } else + start = 0x0001; if (argcp > 2) { end = strtohandle(argvp[2]); if (end < 0) { - printf("Invalid end handle: %s\n", argvp[2]); + error("Invalid end handle: %s\n", argvp[2]); return; } - } + } else + end = 0xffff; - gatt_find_info(attrib, start, end, char_desc_cb, NULL); + gatt_discover_desc(attrib, start, end, NULL, char_desc_cb, NULL); } static void cmd_read_hnd(int argcp, char **argvp) { int handle; - int offset = 0; if (conn_state != STATE_CONNECTED) { - printf("Command failed: disconnected\n"); + failed("Disconnected\n"); return; } if (argcp < 2) { - printf("Missing argument: handle\n"); + error("Missing argument: handle\n"); return; } handle = strtohandle(argvp[1]); if (handle < 0) { - printf("Invalid handle: %s\n", argvp[1]); + error("Invalid handle: %s\n", argvp[1]); return; } - if (argcp > 2) { - char *e; - - errno = 0; - offset = strtol(argvp[2], &e, 0); - if (errno != 0 || *e != '\0') { - printf("Invalid offset: %s\n", argvp[2]); - return; - } - } - - gatt_read_char(attrib, handle, offset, char_read_cb, attrib); + gatt_read_char(attrib, handle, char_read_cb, attrib); } static void cmd_read_uuid(int argcp, char **argvp) { - struct characteristic_data *char_data; int start = 0x0001; int end = 0xffff; bt_uuid_t uuid; if (conn_state != STATE_CONNECTED) { - printf("Command failed: disconnected\n"); + failed("Disconnected\n"); return; } if (argcp < 2) { - printf("Missing argument: UUID\n"); + error("Missing argument: UUID\n"); return; } if (bt_string_to_uuid(&uuid, argvp[1]) < 0) { - printf("Invalid UUID\n"); + error("Invalid UUID\n"); return; } if (argcp > 2) { start = strtohandle(argvp[2]); if (start < 0) { - printf("Invalid start handle: %s\n", argvp[1]); + error("Invalid start handle: %s\n", argvp[1]); return; } } @@ -560,36 +593,30 @@ if (argcp > 3) { end = strtohandle(argvp[3]); if (end < 0) { - printf("Invalid end handle: %s\n", argvp[2]); + error("Invalid end handle: %s\n", argvp[2]); return; } } - char_data = g_new(struct characteristic_data, 1); - char_data->orig_start = start; - char_data->start = start; - char_data->end = end; - char_data->uuid = uuid; - - gatt_read_char_by_uuid(attrib, start, end, &char_data->uuid, - char_read_by_uuid_cb, char_data); + gatt_read_char_by_uuid(attrib, start, end, &uuid, char_read_by_uuid_cb, + NULL); } static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen, gpointer user_data) { if (status != 0) { - printf("Characteristic Write Request failed: " + error("Characteristic Write Request failed: " "%s\n", att_ecode2str(status)); return; } - if (!dec_write_resp(pdu, plen)) { - printf("Protocol error\n"); + if (!dec_write_resp(pdu, plen) && !dec_exec_write_resp(pdu, plen)) { + error("Protocol error\n"); return; } - printf("Characteristic value was written successfully\n"); + rl_printf("Characteristic value was written successfully\n"); } static void cmd_char_write(int argcp, char **argvp) @@ -599,24 +626,24 @@ int handle; if (conn_state != STATE_CONNECTED) { - printf("Command failed: disconnected\n"); + failed("Disconnected\n"); return; } if (argcp < 3) { - printf("Usage: %s \n", argvp[0]); + rl_printf("Usage: %s \n", argvp[0]); return; } handle = strtohandle(argvp[1]); if (handle <= 0) { - printf("A valid handle is required\n"); + error("A valid handle is required\n"); return; } plen = gatt_attr_data_from_string(argvp[2], &value); if (plen == 0) { - g_printerr("Invalid value\n"); + error("Invalid value\n"); return; } @@ -624,7 +651,7 @@ gatt_write_char(attrib, handle, value, plen, char_write_req_cb, NULL); else - gatt_write_char(attrib, handle, value, plen, NULL, NULL); + gatt_write_cmd(attrib, handle, value, plen, NULL, NULL); g_free(value); } @@ -635,7 +662,7 @@ BtIOSecLevel sec_level; if (argcp < 2) { - printf("sec-level: %s\n", opt_sec_level); + rl_printf("sec-level: %s\n", opt_sec_level); return; } @@ -646,7 +673,7 @@ else if (strcasecmp(argvp[1], "low") == 0) sec_level = BT_IO_SEC_LOW; else { - printf("Allowed values: low | medium | high\n"); + rl_printf("Allowed values: low | medium | high\n"); return; } @@ -657,16 +684,15 @@ return; if (opt_psm) { - printf("It must be reconnected to this change take effect\n"); + rl_printf("Change will take effect on reconnection\n"); return; } - bt_io_set(iochannel, BT_IO_L2CAP, &gerr, + bt_io_set(iochannel, &gerr, BT_IO_OPT_SEC_LEVEL, sec_level, BT_IO_OPT_INVALID); - if (gerr) { - printf("Error: %s\n", gerr->message); + error("%s\n", gerr->message); g_error_free(gerr); } } @@ -677,52 +703,50 @@ uint16_t mtu; if (status != 0) { - printf("Exchange MTU Request failed: %s\n", - att_ecode2str(status)); + error("Exchange MTU Request failed: %s\n", + att_ecode2str(status)); return; } if (!dec_mtu_resp(pdu, plen, &mtu)) { - printf("Protocol error\n"); + error("Protocol error\n"); return; } mtu = MIN(mtu, opt_mtu); /* Set new value for MTU in client */ if (g_attrib_set_mtu(attrib, mtu)) - printf("MTU was exchanged successfully: %d\n", mtu); + rl_printf("MTU was exchanged successfully: %d\n", mtu); else - printf("Error exchanging MTU\n"); + error("Error exchanging MTU\n"); } static void cmd_mtu(int argcp, char **argvp) { if (conn_state != STATE_CONNECTED) { - printf("Command failed: not connected.\n"); + failed("Disconnected\n"); return; } if (opt_psm) { - printf("Command failed: operation is only available for LE" - " transport.\n"); + failed("Operation is only available for LE transport.\n"); return; } if (argcp < 2) { - printf("Usage: mtu \n"); + rl_printf("Usage: mtu \n"); return; } if (opt_mtu) { - printf("Command failed: MTU exchange can only occur once per" - " connection.\n"); + failed("MTU exchange can only occur once per connection.\n"); return; } errno = 0; opt_mtu = strtoll(argvp[1], NULL, 0); if (errno != 0 || opt_mtu < ATT_DEFAULT_LE_MTU) { - printf("Invalid value. Minimum MTU size is %d\n", + error("Invalid value. Minimum MTU size is %d\n", ATT_DEFAULT_LE_MTU); return; } @@ -748,11 +772,13 @@ "Disconnect from a remote device" }, { "primary", cmd_primary, "[UUID]", "Primary Service Discovery" }, + { "included", cmd_included, "[start hnd [end hnd]]", + "Find Included Services" }, { "characteristics", cmd_char, "[start hnd [end hnd [UUID]]]", "Characteristics Discovery" }, { "char-desc", cmd_char_desc, "[start hnd] [end hnd]", "Characteristics Descriptor Discovery" }, - { "char-read-hnd", cmd_read_hnd, " [offset]", + { "char-read-hnd", cmd_read_hnd, "", "Characteristics Value/Descriptor Read by handle" }, { "char-read-uuid", cmd_read_uuid, " [start hnd] [end hnd]", "Characteristics Value/Descriptor Read by UUID" }, @@ -772,18 +798,18 @@ int i; for (i = 0; commands[i].cmd; i++) - printf("%-15s %-30s %s\n", commands[i].cmd, + rl_printf("%-15s %-30s %s\n", commands[i].cmd, commands[i].params, commands[i].desc); } static void parse_line(char *line_read) { - gchar **argvp; + char **argvp; int argcp; int i; if (line_read == NULL) { - printf("\n"); + rl_printf("\n"); cmd_exit(0, NULL); return; } @@ -791,11 +817,12 @@ line_read = g_strstrip(line_read); if (*line_read == '\0') - return; + goto done; add_history(line_read); - g_shell_parse_argv(line_read, &argcp, &argvp, NULL); + if (g_shell_parse_argv(line_read, &argcp, &argvp, NULL) == FALSE) + goto done; for (i = 0; commands[i].cmd; i++) if (strcasecmp(commands[i].cmd, argvp[0]) == 0) @@ -804,9 +831,12 @@ if (commands[i].cmd) commands[i].func(argcp, argvp); else - printf("%s: command not found\n", argvp[0]); + error("%s: command not found\n", argvp[0]); g_strfreev(argvp); + +done: + free(line_read); } static gboolean prompt_read(GIOChannel *chan, GIOCondition cond, @@ -849,11 +879,104 @@ return NULL; } -int interactive(const gchar *src, const gchar *dst, - const gchar *dst_type, int psm) +static guint setup_standard_input(void) +{ + GIOChannel *channel; + guint source; + + channel = g_io_channel_unix_new(fileno(stdin)); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + prompt_read, NULL); + + g_io_channel_unref(channel); + + return source; +} + +static gboolean signal_handler(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + static unsigned int __terminated = 0; + struct signalfd_siginfo si; + ssize_t result; + int fd; + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + g_main_loop_quit(event_loop); + return FALSE; + } + + fd = g_io_channel_unix_get_fd(channel); + + result = read(fd, &si, sizeof(si)); + if (result != sizeof(si)) + return FALSE; + + switch (si.ssi_signo) { + case SIGINT: + rl_replace_line("", 0); + rl_crlf(); + rl_on_new_line(); + rl_redisplay(); + break; + case SIGTERM: + if (__terminated == 0) { + rl_replace_line("", 0); + rl_crlf(); + g_main_loop_quit(event_loop); + } + + __terminated = 1; + break; + } + + return TRUE; +} + +static guint setup_signalfd(void) +{ + GIOChannel *channel; + guint source; + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { + perror("Failed to set signal mask"); + return 0; + } + + fd = signalfd(-1, &mask, 0); + if (fd < 0) { + perror("Failed to create signal descriptor"); + return 0; + } + + channel = g_io_channel_unix_new(fd); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +int interactive(const char *src, const char *dst, + const char *dst_type, int psm) { - GIOChannel *pchan; - gint events; + guint input; + guint signal; opt_sec_level = g_strdup("low"); @@ -866,19 +989,19 @@ event_loop = g_main_loop_new(NULL, FALSE); - pchan = g_io_channel_unix_new(fileno(stdin)); - g_io_channel_set_close_on_unref(pchan, TRUE); - events = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; - g_io_add_watch(pchan, events, prompt_read, NULL); + input = setup_standard_input(); + signal = setup_signalfd(); rl_attempted_completion_function = commands_completion; + rl_erase_empty_line = 1; rl_callback_handler_install(get_prompt(), parse_line); g_main_loop_run(event_loop); rl_callback_handler_remove(); cmd_disconnect(0, NULL); - g_io_channel_unref(pchan); + g_source_remove(input); + g_source_remove(signal); g_main_loop_unref(event_loop); g_string_free(prompt, TRUE); diff -Nru bluez-4.101/attrib/utils.c bluez-5.23/attrib/utils.c --- bluez-4.101/attrib/utils.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/attrib/utils.c 2014-03-25 20:53:41.000000000 +0000 @@ -21,36 +21,35 @@ * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include #include #include -#include -#include +#include "lib/uuid.h" +#include "btio/btio.h" #include "att.h" #include "gattrib.h" #include "gatt.h" -#include "btio.h" #include "gatttool.h" -GIOChannel *gatt_connect(const gchar *src, const gchar *dst, - const gchar *dst_type, const gchar *sec_level, - int psm, int mtu, BtIOConnect connect_cb) +GIOChannel *gatt_connect(const char *src, const char *dst, + const char *dst_type, const char *sec_level, + int psm, int mtu, BtIOConnect connect_cb, + GError **gerr) { GIOChannel *chan; bdaddr_t sba, dba; uint8_t dest_type; - GError *err = NULL; + GError *tmp_err = NULL; BtIOSecLevel sec; - /* Remote device */ - if (dst == NULL) { - g_printerr("Remote Bluetooth address required\n"); - return NULL; - } str2ba(dst, &dba); /* Local adapter */ @@ -76,15 +75,16 @@ sec = BT_IO_SEC_LOW; if (psm == 0) - chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err, + chan = bt_io_connect(connect_cb, NULL, NULL, &tmp_err, BT_IO_OPT_SOURCE_BDADDR, &sba, + BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC, BT_IO_OPT_DEST_BDADDR, &dba, BT_IO_OPT_DEST_TYPE, dest_type, BT_IO_OPT_CID, ATT_CID, BT_IO_OPT_SEC_LEVEL, sec, BT_IO_OPT_INVALID); else - chan = bt_io_connect(BT_IO_L2CAP, connect_cb, NULL, NULL, &err, + chan = bt_io_connect(connect_cb, NULL, NULL, &tmp_err, BT_IO_OPT_SOURCE_BDADDR, &sba, BT_IO_OPT_DEST_BDADDR, &dba, BT_IO_OPT_PSM, psm, @@ -92,9 +92,8 @@ BT_IO_OPT_SEC_LEVEL, sec, BT_IO_OPT_INVALID); - if (err) { - g_printerr("%s\n", err->message); - g_error_free(err); + if (tmp_err) { + g_propagate_error(gerr, tmp_err); return NULL; } diff -Nru bluez-4.101/audio/a2dp.c bluez-5.23/audio/a2dp.c --- bluez-4.101/audio/a2dp.c 2012-06-22 16:36:49.000000000 +0000 +++ bluez-5.23/audio/a2dp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2436 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved. - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include -#include - -#include -#include -#include - -#include "log.h" -#include "device.h" -#include "manager.h" -#include "avdtp.h" -#include "sink.h" -#include "source.h" -#include "unix.h" -#include "a2dp.h" -#include "sdpd.h" - -/* The duration that streams without users are allowed to stay in - * STREAMING state. */ -#define SUSPEND_TIMEOUT 5 -#define RECONFIGURE_TIMEOUT 500 - -#ifndef MIN -# define MIN(x, y) ((x) < (y) ? (x) : (y)) -#endif - -#ifndef MAX -# define MAX(x, y) ((x) > (y) ? (x) : (y)) -#endif - -struct a2dp_sep { - struct a2dp_server *server; - struct a2dp_endpoint *endpoint; - uint8_t type; - uint8_t codec; - struct avdtp_local_sep *lsep; - struct avdtp *session; - struct avdtp_stream *stream; - guint suspend_timer; - gboolean delay_reporting; - gboolean locked; - gboolean suspending; - gboolean starting; - void *user_data; - GDestroyNotify destroy; -}; - -struct a2dp_setup_cb { - struct a2dp_setup *setup; - a2dp_select_cb_t select_cb; - a2dp_config_cb_t config_cb; - a2dp_stream_cb_t resume_cb; - a2dp_stream_cb_t suspend_cb; - guint source_id; - void *user_data; - unsigned int id; -}; - -struct a2dp_setup { - struct audio_device *dev; - struct avdtp *session; - struct a2dp_sep *sep; - struct avdtp_remote_sep *rsep; - struct avdtp_stream *stream; - struct avdtp_error *err; - avdtp_set_configuration_cb setconf_cb; - GSList *caps; - gboolean reconfigure; - gboolean start; - GSList *cb; - int ref; -}; - -static DBusConnection *connection = NULL; - -struct a2dp_server { - bdaddr_t src; - GSList *sinks; - GSList *sources; - uint32_t source_record_id; - uint32_t sink_record_id; - uint16_t version; - gboolean sink_enabled; - gboolean source_enabled; -}; - -static GSList *servers = NULL; -static GSList *setups = NULL; -static unsigned int cb_id = 0; - -static struct a2dp_setup *setup_ref(struct a2dp_setup *setup) -{ - setup->ref++; - - DBG("%p: ref=%d", setup, setup->ref); - - return setup; -} - -static struct audio_device *a2dp_get_dev(struct avdtp *session) -{ - bdaddr_t src, dst; - - avdtp_get_peers(session, &src, &dst); - - return manager_find_device(NULL, &src, &dst, NULL, FALSE); -} - -static struct a2dp_setup *setup_new(struct avdtp *session) -{ - struct audio_device *dev; - struct a2dp_setup *setup; - - dev = a2dp_get_dev(session); - if (!dev) { - error("Unable to create setup"); - return NULL; - } - - setup = g_new0(struct a2dp_setup, 1); - setup->session = avdtp_ref(session); - setup->dev = a2dp_get_dev(session); - setups = g_slist_append(setups, setup); - - return setup; -} - -static void setup_free(struct a2dp_setup *s) -{ - DBG("%p", s); - - setups = g_slist_remove(setups, s); - if (s->session) - avdtp_unref(s->session); - g_slist_free_full(s->cb, g_free); - g_slist_free_full(s->caps, g_free); - g_free(s); -} - -static void setup_unref(struct a2dp_setup *setup) -{ - setup->ref--; - - DBG("%p: ref=%d", setup, setup->ref); - - if (setup->ref > 0) - return; - - setup_free(setup); -} - -static struct a2dp_setup_cb *setup_cb_new(struct a2dp_setup *setup) -{ - struct a2dp_setup_cb *cb; - - cb = g_new0(struct a2dp_setup_cb, 1); - cb->setup = setup; - cb->id = ++cb_id; - - setup->cb = g_slist_append(setup->cb, cb); - return cb; -} - -static void setup_cb_free(struct a2dp_setup_cb *cb) -{ - struct a2dp_setup *setup = cb->setup; - - if (cb->source_id) - g_source_remove(cb->source_id); - - setup->cb = g_slist_remove(setup->cb, cb); - setup_unref(cb->setup); - g_free(cb); -} - -static void finalize_setup_errno(struct a2dp_setup *s, int err, - GSourceFunc cb1, ...) -{ - GSourceFunc finalize; - va_list args; - struct avdtp_error avdtp_err; - - if (err < 0) { - avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err); - s->err = &avdtp_err; - } - - va_start(args, cb1); - finalize = cb1; - setup_ref(s); - while (finalize != NULL) { - finalize(s); - finalize = va_arg(args, GSourceFunc); - } - setup_unref(s); - va_end(args); -} - -static gboolean finalize_config(gpointer data) -{ - struct a2dp_setup *s = data; - GSList *l; - struct avdtp_stream *stream = s->err ? NULL : s->stream; - - for (l = s->cb; l != NULL; ) { - struct a2dp_setup_cb *cb = l->data; - - l = l->next; - - if (!cb->config_cb) - continue; - - cb->config_cb(s->session, s->sep, stream, s->err, - cb->user_data); - setup_cb_free(cb); - } - - return FALSE; -} - -static gboolean finalize_resume(gpointer data) -{ - struct a2dp_setup *s = data; - GSList *l; - - for (l = s->cb; l != NULL; ) { - struct a2dp_setup_cb *cb = l->data; - - l = l->next; - - if (!cb->resume_cb) - continue; - - cb->resume_cb(s->session, s->err, cb->user_data); - setup_cb_free(cb); - } - - return FALSE; -} - -static gboolean finalize_suspend(gpointer data) -{ - struct a2dp_setup *s = data; - GSList *l; - - for (l = s->cb; l != NULL; ) { - struct a2dp_setup_cb *cb = l->data; - - l = l->next; - - if (!cb->suspend_cb) - continue; - - cb->suspend_cb(s->session, s->err, cb->user_data); - setup_cb_free(cb); - } - - return FALSE; -} - -static void finalize_select(struct a2dp_setup *s) -{ - GSList *l; - - for (l = s->cb; l != NULL; ) { - struct a2dp_setup_cb *cb = l->data; - - l = l->next; - - if (!cb->select_cb) - continue; - - cb->select_cb(s->session, s->sep, s->caps, cb->user_data); - setup_cb_free(cb); - } -} - -static struct a2dp_setup *find_setup_by_session(struct avdtp *session) -{ - GSList *l; - - for (l = setups; l != NULL; l = l->next) { - struct a2dp_setup *setup = l->data; - - if (setup->session == session) - return setup; - } - - return NULL; -} - -static struct a2dp_setup *a2dp_setup_get(struct avdtp *session) -{ - struct a2dp_setup *setup; - - setup = find_setup_by_session(session); - if (!setup) { - setup = setup_new(session); - if (!setup) - return NULL; - } - - return setup_ref(setup); -} - -static struct a2dp_setup *find_setup_by_dev(struct audio_device *dev) -{ - GSList *l; - - for (l = setups; l != NULL; l = l->next) { - struct a2dp_setup *setup = l->data; - - if (setup->dev == dev) - return setup; - } - - return NULL; -} - -static void stream_state_changed(struct avdtp_stream *stream, - avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, - void *user_data) -{ - struct a2dp_sep *sep = user_data; - - if (new_state != AVDTP_STATE_IDLE) - return; - - if (sep->suspend_timer) { - g_source_remove(sep->suspend_timer); - sep->suspend_timer = 0; - } - - if (sep->session) { - avdtp_unref(sep->session); - sep->session = NULL; - } - - sep->stream = NULL; - - if (sep->endpoint && sep->endpoint->clear_configuration) - sep->endpoint->clear_configuration(sep, sep->user_data); -} - -static gboolean auto_config(gpointer data) -{ - struct a2dp_setup *setup = data; - struct avdtp_error *err = NULL; - - /* Check if configuration was aborted */ - if (setup->sep->stream == NULL) - return FALSE; - - if (setup->err != NULL) { - err = setup->err; - goto done; - } - - avdtp_stream_add_cb(setup->session, setup->stream, - stream_state_changed, setup->sep); - - if (setup->sep->type == AVDTP_SEP_TYPE_SOURCE) - sink_new_stream(setup->dev, setup->session, setup->stream); - else - source_new_stream(setup->dev, setup->session, setup->stream); - -done: - if (setup->setconf_cb) - setup->setconf_cb(setup->session, setup->stream, setup->err); - - finalize_config(setup); - - if (err) - g_free(err); - - setup_unref(setup); - - return FALSE; -} - -static gboolean sbc_setconf_ind(struct avdtp *session, - struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - GSList *caps, - avdtp_set_configuration_cb cb, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Set_Configuration_Ind", sep); - else - DBG("Source %p: Set_Configuration_Ind", sep); - - setup = a2dp_setup_get(session); - if (!setup) - return FALSE; - - a2dp_sep->stream = stream; - setup->sep = a2dp_sep; - setup->stream = stream; - setup->setconf_cb = cb; - - /* Check valid settings */ - for (; caps != NULL; caps = g_slist_next(caps)) { - struct avdtp_service_capability *cap = caps->data; - struct avdtp_media_codec_capability *codec_cap; - struct sbc_codec_cap *sbc_cap; - - if (cap->category == AVDTP_DELAY_REPORTING && - !a2dp_sep->delay_reporting) { - setup->err = g_new(struct avdtp_error, 1); - avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING, - AVDTP_UNSUPPORTED_CONFIGURATION); - goto done; - } - - if (cap->category != AVDTP_MEDIA_CODEC) - continue; - - if (cap->length < sizeof(struct sbc_codec_cap)) - continue; - - codec_cap = (void *) cap->data; - - if (codec_cap->media_codec_type != A2DP_CODEC_SBC) - continue; - - sbc_cap = (void *) codec_cap; - - if (sbc_cap->min_bitpool < MIN_BITPOOL || - sbc_cap->max_bitpool > MAX_BITPOOL) { - setup->err = g_new(struct avdtp_error, 1); - avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC, - AVDTP_UNSUPPORTED_CONFIGURATION); - goto done; - } - } - -done: - g_idle_add(auto_config, setup); - return TRUE; -} - -static gboolean sbc_getcap_ind(struct avdtp *session, struct avdtp_local_sep *sep, - gboolean get_all, GSList **caps, uint8_t *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct avdtp_service_capability *media_transport, *media_codec; - struct sbc_codec_cap sbc_cap; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Get_Capability_Ind", sep); - else - DBG("Source %p: Get_Capability_Ind", sep); - - *caps = NULL; - - media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, - NULL, 0); - - *caps = g_slist_append(*caps, media_transport); - - memset(&sbc_cap, 0, sizeof(struct sbc_codec_cap)); - - sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; - - sbc_cap.frequency = ( SBC_SAMPLING_FREQ_48000 | - SBC_SAMPLING_FREQ_44100 | - SBC_SAMPLING_FREQ_32000 | - SBC_SAMPLING_FREQ_16000 ); - - sbc_cap.channel_mode = ( SBC_CHANNEL_MODE_JOINT_STEREO | - SBC_CHANNEL_MODE_STEREO | - SBC_CHANNEL_MODE_DUAL_CHANNEL | - SBC_CHANNEL_MODE_MONO ); - - sbc_cap.block_length = ( SBC_BLOCK_LENGTH_16 | - SBC_BLOCK_LENGTH_12 | - SBC_BLOCK_LENGTH_8 | - SBC_BLOCK_LENGTH_4 ); - - sbc_cap.subbands = ( SBC_SUBBANDS_8 | SBC_SUBBANDS_4 ); - - sbc_cap.allocation_method = ( SBC_ALLOCATION_LOUDNESS | - SBC_ALLOCATION_SNR ); - - sbc_cap.min_bitpool = MIN_BITPOOL; - sbc_cap.max_bitpool = MAX_BITPOOL; - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, - sizeof(sbc_cap)); - - *caps = g_slist_append(*caps, media_codec); - - if (get_all) { - struct avdtp_service_capability *delay_reporting; - delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, - NULL, 0); - *caps = g_slist_append(*caps, delay_reporting); - } - - return TRUE; -} - -static gboolean mpeg_setconf_ind(struct avdtp *session, - struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - GSList *caps, - avdtp_set_configuration_cb cb, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Set_Configuration_Ind", sep); - else - DBG("Source %p: Set_Configuration_Ind", sep); - - setup = a2dp_setup_get(session); - if (!setup) - return FALSE; - - a2dp_sep->stream = stream; - setup->sep = a2dp_sep; - setup->stream = stream; - setup->setconf_cb = cb; - - for (; caps != NULL; caps = g_slist_next(caps)) { - struct avdtp_service_capability *cap = caps->data; - - if (cap->category == AVDTP_DELAY_REPORTING && - !a2dp_sep->delay_reporting) { - setup->err = g_new(struct avdtp_error, 1); - avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING, - AVDTP_UNSUPPORTED_CONFIGURATION); - goto done; - } - } - -done: - g_idle_add(auto_config, setup); - return TRUE; -} - -static gboolean mpeg_getcap_ind(struct avdtp *session, - struct avdtp_local_sep *sep, - gboolean get_all, - GSList **caps, uint8_t *err, void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct avdtp_service_capability *media_transport, *media_codec; - struct mpeg_codec_cap mpeg_cap; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Get_Capability_Ind", sep); - else - DBG("Source %p: Get_Capability_Ind", sep); - - *caps = NULL; - - media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, - NULL, 0); - - *caps = g_slist_append(*caps, media_transport); - - memset(&mpeg_cap, 0, sizeof(struct mpeg_codec_cap)); - - mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12; - - mpeg_cap.frequency = ( MPEG_SAMPLING_FREQ_48000 | - MPEG_SAMPLING_FREQ_44100 | - MPEG_SAMPLING_FREQ_32000 | - MPEG_SAMPLING_FREQ_24000 | - MPEG_SAMPLING_FREQ_22050 | - MPEG_SAMPLING_FREQ_16000 ); - - mpeg_cap.channel_mode = ( MPEG_CHANNEL_MODE_JOINT_STEREO | - MPEG_CHANNEL_MODE_STEREO | - MPEG_CHANNEL_MODE_DUAL_CHANNEL | - MPEG_CHANNEL_MODE_MONO ); - - mpeg_cap.layer = ( MPEG_LAYER_MP3 | MPEG_LAYER_MP2 | MPEG_LAYER_MP1 ); - - mpeg_cap.bitrate = 0xFFFF; - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap, - sizeof(mpeg_cap)); - - *caps = g_slist_append(*caps, media_codec); - - if (get_all) { - struct avdtp_service_capability *delay_reporting; - delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, - NULL, 0); - *caps = g_slist_append(*caps, delay_reporting); - } - - return TRUE; -} - - -static void endpoint_setconf_cb(struct a2dp_setup *setup, gboolean ret) -{ - if (ret == FALSE) { - setup->err = g_new(struct avdtp_error, 1); - avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC, - AVDTP_UNSUPPORTED_CONFIGURATION); - } - - auto_config(setup); -} - -static gboolean endpoint_setconf_ind(struct avdtp *session, - struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - GSList *caps, - avdtp_set_configuration_cb cb, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Set_Configuration_Ind", sep); - else - DBG("Source %p: Set_Configuration_Ind", sep); - - setup = a2dp_setup_get(session); - if (!session) - return FALSE; - - a2dp_sep->stream = stream; - setup->sep = a2dp_sep; - setup->stream = stream; - setup->setconf_cb = cb; - - for (; caps != NULL; caps = g_slist_next(caps)) { - struct avdtp_service_capability *cap = caps->data; - struct avdtp_media_codec_capability *codec; - gboolean ret; - - if (cap->category == AVDTP_DELAY_REPORTING && - !a2dp_sep->delay_reporting) { - setup->err = g_new(struct avdtp_error, 1); - avdtp_error_init(setup->err, AVDTP_DELAY_REPORTING, - AVDTP_UNSUPPORTED_CONFIGURATION); - goto done; - } - - if (cap->category != AVDTP_MEDIA_CODEC) - continue; - - codec = (struct avdtp_media_codec_capability *) cap->data; - - if (codec->media_codec_type != a2dp_sep->codec) { - setup->err = g_new(struct avdtp_error, 1); - avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC, - AVDTP_UNSUPPORTED_CONFIGURATION); - goto done; - } - - ret = a2dp_sep->endpoint->set_configuration(a2dp_sep, - setup->dev, codec->data, - cap->length - sizeof(*codec), - setup, - endpoint_setconf_cb, - a2dp_sep->user_data); - if (ret == 0) - return TRUE; - - setup->err = g_new(struct avdtp_error, 1); - avdtp_error_init(setup->err, AVDTP_MEDIA_CODEC, - AVDTP_UNSUPPORTED_CONFIGURATION); - break; - } - -done: - g_idle_add(auto_config, setup); - return TRUE; -} - -static gboolean endpoint_getcap_ind(struct avdtp *session, - struct avdtp_local_sep *sep, - gboolean get_all, GSList **caps, - uint8_t *err, void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct avdtp_service_capability *media_transport, *media_codec; - struct avdtp_media_codec_capability *codec_caps; - uint8_t *capabilities; - size_t length; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Get_Capability_Ind", sep); - else - DBG("Source %p: Get_Capability_Ind", sep); - - *caps = NULL; - - media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, - NULL, 0); - - *caps = g_slist_append(*caps, media_transport); - - length = a2dp_sep->endpoint->get_capabilities(a2dp_sep, &capabilities, - a2dp_sep->user_data); - - codec_caps = g_malloc0(sizeof(*codec_caps) + length); - codec_caps->media_type = AVDTP_MEDIA_TYPE_AUDIO; - codec_caps->media_codec_type = a2dp_sep->codec; - memcpy(codec_caps->data, capabilities, length); - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, codec_caps, - sizeof(*codec_caps) + length); - - *caps = g_slist_append(*caps, media_codec); - g_free(codec_caps); - - if (get_all) { - struct avdtp_service_capability *delay_reporting; - delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, - NULL, 0); - *caps = g_slist_append(*caps, delay_reporting); - } - - return TRUE; -} - -static void endpoint_open_cb(struct a2dp_setup *setup, gboolean ret) -{ - int err; - - if (ret == FALSE) { - setup->stream = NULL; - finalize_setup_errno(setup, -EPERM, finalize_config, NULL); - return; - } - - err = avdtp_open(setup->session, setup->stream); - if (err == 0) - return; - - error("Error on avdtp_open %s (%d)", strerror(-err), -err); - setup->stream = NULL; - finalize_setup_errno(setup, err, finalize_config, NULL); -} - -static void setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - struct audio_device *dev; - int ret; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Set_Configuration_Cfm", sep); - else - DBG("Source %p: Set_Configuration_Cfm", sep); - - setup = find_setup_by_session(session); - - if (err) { - if (setup) { - setup->err = err; - finalize_config(setup); - } - return; - } - - avdtp_stream_add_cb(session, stream, stream_state_changed, a2dp_sep); - a2dp_sep->stream = stream; - - if (!setup) - return; - - dev = a2dp_get_dev(session); - - /* Notify D-Bus interface of the new stream */ - if (a2dp_sep->type == AVDTP_SEP_TYPE_SOURCE) - sink_new_stream(dev, session, setup->stream); - else - source_new_stream(dev, session, setup->stream); - - /* Notify Endpoint */ - if (a2dp_sep->endpoint) { - struct avdtp_service_capability *service; - struct avdtp_media_codec_capability *codec; - int err; - - service = avdtp_stream_get_codec(stream); - codec = (struct avdtp_media_codec_capability *) service->data; - - err = a2dp_sep->endpoint->set_configuration(a2dp_sep, dev, - codec->data, service->length - - sizeof(*codec), - setup, - endpoint_open_cb, - a2dp_sep->user_data); - if (err == 0) - return; - - setup->stream = NULL; - finalize_setup_errno(setup, -EPERM, finalize_config, NULL); - return; - } - - ret = avdtp_open(session, stream); - if (ret < 0) { - error("Error on avdtp_open %s (%d)", strerror(-ret), -ret); - setup->stream = NULL; - finalize_setup_errno(setup, ret, finalize_config, NULL); - } -} - -static gboolean getconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, - uint8_t *err, void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Get_Configuration_Ind", sep); - else - DBG("Source %p: Get_Configuration_Ind", sep); - return TRUE; -} - -static void getconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Set_Configuration_Cfm", sep); - else - DBG("Source %p: Set_Configuration_Cfm", sep); -} - -static gboolean open_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Open_Ind", sep); - else - DBG("Source %p: Open_Ind", sep); - - setup = find_setup_by_session(session); - if (!setup) - return TRUE; - - if (setup->reconfigure) - setup->reconfigure = FALSE; - - finalize_config(setup); - - return TRUE; -} - -static void open_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Open_Cfm", sep); - else - DBG("Source %p: Open_Cfm", sep); - - setup = find_setup_by_session(session); - if (!setup) - return; - - if (setup->reconfigure) - setup->reconfigure = FALSE; - - if (err) { - setup->stream = NULL; - setup->err = err; - } - - finalize_config(setup); -} - -static gboolean suspend_timeout(struct a2dp_sep *sep) -{ - if (avdtp_suspend(sep->session, sep->stream) == 0) - sep->suspending = TRUE; - - sep->suspend_timer = 0; - - avdtp_unref(sep->session); - sep->session = NULL; - - return FALSE; -} - -static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Start_Ind", sep); - else - DBG("Source %p: Start_Ind", sep); - - if (!a2dp_sep->locked) { - a2dp_sep->session = avdtp_ref(session); - a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT, - (GSourceFunc) suspend_timeout, - a2dp_sep); - } - - if (!a2dp_sep->starting) - return TRUE; - - a2dp_sep->starting = FALSE; - - setup = find_setup_by_session(session); - if (setup) - finalize_resume(setup); - - return TRUE; -} - -static void start_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Start_Cfm", sep); - else - DBG("Source %p: Start_Cfm", sep); - - a2dp_sep->starting = FALSE; - - setup = find_setup_by_session(session); - if (!setup) - return; - - if (err) { - setup->stream = NULL; - setup->err = err; - } - - finalize_resume(setup); -} - -static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - gboolean start; - int start_err; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Suspend_Ind", sep); - else - DBG("Source %p: Suspend_Ind", sep); - - if (a2dp_sep->suspend_timer) { - g_source_remove(a2dp_sep->suspend_timer); - a2dp_sep->suspend_timer = 0; - avdtp_unref(a2dp_sep->session); - a2dp_sep->session = NULL; - } - - if (!a2dp_sep->suspending) - return TRUE; - - a2dp_sep->suspending = FALSE; - - setup = find_setup_by_session(session); - if (!setup) - return TRUE; - - start = setup->start; - setup->start = FALSE; - - finalize_suspend(setup); - - if (!start) - return TRUE; - - start_err = avdtp_start(session, a2dp_sep->stream); - if (start_err < 0 && start_err != -EINPROGRESS) { - error("avdtp_start: %s (%d)", strerror(-start_err), - -start_err); - finalize_setup_errno(setup, start_err, finalize_resume); - } - - return TRUE; -} - -static void suspend_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - gboolean start; - int start_err; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Suspend_Cfm", sep); - else - DBG("Source %p: Suspend_Cfm", sep); - - a2dp_sep->suspending = FALSE; - - setup = find_setup_by_session(session); - if (!setup) - return; - - start = setup->start; - setup->start = FALSE; - - if (err) { - setup->stream = NULL; - setup->err = err; - } - - finalize_suspend(setup); - - if (!start) - return; - - if (err) { - finalize_resume(setup); - return; - } - - start_err = avdtp_start(session, a2dp_sep->stream); - if (start_err < 0 && start_err != -EINPROGRESS) { - error("avdtp_start: %s (%d)", strerror(-start_err), - -start_err); - finalize_setup_errno(setup, start_err, finalize_suspend, NULL); - } -} - -static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Close_Ind", sep); - else - DBG("Source %p: Close_Ind", sep); - - setup = find_setup_by_session(session); - if (!setup) - return TRUE; - - finalize_setup_errno(setup, -ECONNRESET, finalize_suspend, - finalize_resume, NULL); - - return TRUE; -} - -static gboolean a2dp_reconfigure(gpointer data) -{ - struct a2dp_setup *setup = data; - struct a2dp_sep *sep = setup->sep; - int posix_err; - struct avdtp_media_codec_capability *rsep_codec; - struct avdtp_service_capability *cap; - - if (setup->rsep) { - cap = avdtp_get_codec(setup->rsep); - rsep_codec = (struct avdtp_media_codec_capability *) cap->data; - } - - if (!setup->rsep || sep->codec != rsep_codec->media_codec_type) - setup->rsep = avdtp_find_remote_sep(setup->session, sep->lsep); - - posix_err = avdtp_set_configuration(setup->session, setup->rsep, - sep->lsep, - setup->caps, - &setup->stream); - if (posix_err < 0) { - error("avdtp_set_configuration: %s", strerror(-posix_err)); - goto failed; - } - - return FALSE; - -failed: - finalize_setup_errno(setup, posix_err, finalize_config, NULL); - return FALSE; -} - -static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Close_Cfm", sep); - else - DBG("Source %p: Close_Cfm", sep); - - setup = find_setup_by_session(session); - if (!setup) - return; - - if (err) { - setup->stream = NULL; - setup->err = err; - finalize_config(setup); - return; - } - - if (!setup->rsep) - setup->rsep = avdtp_stream_get_remote_sep(stream); - - if (setup->reconfigure) - g_timeout_add(RECONFIGURE_TIMEOUT, a2dp_reconfigure, setup); -} - -static void abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Abort_Ind", sep); - else - DBG("Source %p: Abort_Ind", sep); - - a2dp_sep->stream = NULL; - - setup = find_setup_by_session(session); - if (!setup) - return; - - finalize_setup_errno(setup, -ECONNRESET, finalize_suspend, - finalize_resume, - finalize_config); - - return; -} - -static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: Abort_Cfm", sep); - else - DBG("Source %p: Abort_Cfm", sep); - - setup = find_setup_by_session(session); - if (!setup) - return; - - setup_unref(setup); -} - -static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, - uint8_t *err, void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: ReConfigure_Ind", sep); - else - DBG("Source %p: ReConfigure_Ind", sep); - - return TRUE; -} - -static gboolean delayreport_ind(struct avdtp *session, - struct avdtp_local_sep *sep, - uint8_t rseid, uint16_t delay, - uint8_t *err, void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct audio_device *dev = a2dp_get_dev(session); - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: DelayReport_Ind", sep); - else - DBG("Source %p: DelayReport_Ind", sep); - - unix_delay_report(dev, rseid, delay); - - return TRUE; -} - -static gboolean endpoint_delayreport_ind(struct avdtp *session, - struct avdtp_local_sep *sep, - uint8_t rseid, uint16_t delay, - uint8_t *err, void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: DelayReport_Ind", sep); - else - DBG("Source %p: DelayReport_Ind", sep); - - if (a2dp_sep->endpoint == NULL || - a2dp_sep->endpoint->set_delay == NULL) - return FALSE; - - a2dp_sep->endpoint->set_delay(a2dp_sep, delay, a2dp_sep->user_data); - - return TRUE; -} - -static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - struct a2dp_setup *setup; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: ReConfigure_Cfm", sep); - else - DBG("Source %p: ReConfigure_Cfm", sep); - - setup = find_setup_by_session(session); - if (!setup) - return; - - if (err) { - setup->stream = NULL; - setup->err = err; - } - - finalize_config(setup); -} - -static void delay_report_cfm(struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data) -{ - struct a2dp_sep *a2dp_sep = user_data; - - if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) - DBG("Sink %p: DelayReport_Cfm", sep); - else - DBG("Source %p: DelayReport_Cfm", sep); -} - -static struct avdtp_sep_cfm cfm = { - .set_configuration = setconf_cfm, - .get_configuration = getconf_cfm, - .open = open_cfm, - .start = start_cfm, - .suspend = suspend_cfm, - .close = close_cfm, - .abort = abort_cfm, - .reconfigure = reconf_cfm, - .delay_report = delay_report_cfm, -}; - -static struct avdtp_sep_ind sbc_ind = { - .get_capability = sbc_getcap_ind, - .set_configuration = sbc_setconf_ind, - .get_configuration = getconf_ind, - .open = open_ind, - .start = start_ind, - .suspend = suspend_ind, - .close = close_ind, - .abort = abort_ind, - .reconfigure = reconf_ind, - .delayreport = delayreport_ind, -}; - -static struct avdtp_sep_ind mpeg_ind = { - .get_capability = mpeg_getcap_ind, - .set_configuration = mpeg_setconf_ind, - .get_configuration = getconf_ind, - .open = open_ind, - .start = start_ind, - .suspend = suspend_ind, - .close = close_ind, - .abort = abort_ind, - .reconfigure = reconf_ind, - .delayreport = delayreport_ind, -}; - -static struct avdtp_sep_ind endpoint_ind = { - .get_capability = endpoint_getcap_ind, - .set_configuration = endpoint_setconf_ind, - .get_configuration = getconf_ind, - .open = open_ind, - .start = start_ind, - .suspend = suspend_ind, - .close = close_ind, - .abort = abort_ind, - .reconfigure = reconf_ind, - .delayreport = endpoint_delayreport_ind, -}; - -static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid; - sdp_profile_desc_t profile[1]; - sdp_list_t *aproto, *proto[2]; - sdp_record_t *record; - sdp_data_t *psm, *version, *features; - uint16_t lp = AVDTP_UUID; - uint16_t a2dp_ver = 0x0102, feat = 0x000f; - - record = sdp_record_alloc(); - if (!record) - return NULL; - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(record, root); - - if (type == AVDTP_SEP_TYPE_SOURCE) - sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID); - else - sdp_uuid16_create(&a2dp_uuid, AUDIO_SINK_SVCLASS_ID); - svclass_id = sdp_list_append(0, &a2dp_uuid); - sdp_set_service_classes(record, svclass_id); - - sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); - profile[0].version = a2dp_ver; - pfseq = sdp_list_append(0, &profile[0]); - sdp_set_profile_descs(record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - psm = sdp_data_alloc(SDP_UINT16, &lp); - proto[0] = sdp_list_append(proto[0], psm); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&avdtp_uuid, AVDTP_UUID); - proto[1] = sdp_list_append(0, &avdtp_uuid); - version = sdp_data_alloc(SDP_UINT16, &avdtp_ver); - proto[1] = sdp_list_append(proto[1], version); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(record, aproto); - - features = sdp_data_alloc(SDP_UINT16, &feat); - sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); - - if (type == AVDTP_SEP_TYPE_SOURCE) - sdp_set_info_attr(record, "Audio Source", 0, 0); - else - sdp_set_info_attr(record, "Audio Sink", 0, 0); - - free(psm); - free(version); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); - - return record; -} - -static struct a2dp_server *find_server(GSList *list, const bdaddr_t *src) -{ - - for (; list; list = list->next) { - struct a2dp_server *server = list->data; - - if (bacmp(&server->src, src) == 0) - return server; - } - - return NULL; -} - -int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config) -{ - int sbc_srcs = 0, sbc_sinks = 0; - int mpeg12_srcs = 0, mpeg12_sinks = 0; - gboolean source = TRUE, sink = FALSE, socket = FALSE; - gboolean delay_reporting = FALSE; - char *str; - GError *err = NULL; - int i; - struct a2dp_server *server; - - if (!config) - goto proceed; - - str = g_key_file_get_string(config, "General", "Enable", &err); - - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else { - if (strstr(str, "Sink")) - source = TRUE; - if (strstr(str, "Source")) - sink = TRUE; - if (strstr(str, "Socket")) - socket = TRUE; - g_free(str); - } - - str = g_key_file_get_string(config, "General", "Disable", &err); - - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else { - if (strstr(str, "Sink")) - source = FALSE; - if (strstr(str, "Source")) - sink = FALSE; - if (strstr(str, "Socket")) - socket = FALSE; - g_free(str); - } - - /* Don't register any local sep if Socket is disabled */ - if (socket == FALSE) - goto proceed; - - str = g_key_file_get_string(config, "A2DP", "SBCSources", &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - sbc_srcs = 1; - } else { - sbc_srcs = atoi(str); - g_free(str); - } - - str = g_key_file_get_string(config, "A2DP", "MPEG12Sources", &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else { - mpeg12_srcs = atoi(str); - g_free(str); - } - - str = g_key_file_get_string(config, "A2DP", "SBCSinks", &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - sbc_sinks = 1; - } else { - sbc_sinks = atoi(str); - g_free(str); - } - - str = g_key_file_get_string(config, "A2DP", "MPEG12Sinks", &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else { - mpeg12_sinks = atoi(str); - g_free(str); - } - -proceed: - if (!connection) - connection = dbus_connection_ref(conn); - - server = find_server(servers, src); - if (!server) { - int av_err; - - server = g_new0(struct a2dp_server, 1); - if (!server) - return -ENOMEM; - - av_err = avdtp_init(src, config, &server->version); - if (av_err < 0) { - g_free(server); - return av_err; - } - - bacpy(&server->src, src); - servers = g_slist_append(servers, server); - } - - if (config) - delay_reporting = g_key_file_get_boolean(config, "A2DP", - "DelayReporting", NULL); - - if (delay_reporting) - server->version = 0x0103; - else - server->version = 0x0102; - - server->source_enabled = source; - if (source) { - for (i = 0; i < sbc_srcs; i++) - a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE, - A2DP_CODEC_SBC, delay_reporting, - NULL, NULL, NULL, NULL); - - for (i = 0; i < mpeg12_srcs; i++) - a2dp_add_sep(src, AVDTP_SEP_TYPE_SOURCE, - A2DP_CODEC_MPEG12, delay_reporting, - NULL, NULL, NULL, NULL); - } - server->sink_enabled = sink; - if (sink) { - for (i = 0; i < sbc_sinks; i++) - a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK, - A2DP_CODEC_SBC, delay_reporting, - NULL, NULL, NULL, NULL); - - for (i = 0; i < mpeg12_sinks; i++) - a2dp_add_sep(src, AVDTP_SEP_TYPE_SINK, - A2DP_CODEC_MPEG12, delay_reporting, - NULL, NULL, NULL, NULL); - } - - return 0; -} - -static void a2dp_unregister_sep(struct a2dp_sep *sep) -{ - if (sep->destroy) { - sep->destroy(sep->user_data); - sep->endpoint = NULL; - } - - avdtp_unregister_sep(sep->lsep); - g_free(sep); -} - -void a2dp_unregister(const bdaddr_t *src) -{ - struct a2dp_server *server; - - server = find_server(servers, src); - if (!server) - return; - - g_slist_free_full(server->sinks, (GDestroyNotify) a2dp_unregister_sep); - g_slist_free_full(server->sources, - (GDestroyNotify) a2dp_unregister_sep); - - avdtp_exit(src); - - servers = g_slist_remove(servers, server); - - if (server->source_record_id) - remove_record_from_server(server->source_record_id); - - if (server->sink_record_id) - remove_record_from_server(server->sink_record_id); - - g_free(server); - - if (servers) - return; - - dbus_connection_unref(connection); - connection = NULL; -} - -struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type, - uint8_t codec, gboolean delay_reporting, - struct a2dp_endpoint *endpoint, - void *user_data, GDestroyNotify destroy, - int *err) -{ - struct a2dp_server *server; - struct a2dp_sep *sep; - GSList **l; - uint32_t *record_id; - sdp_record_t *record; - struct avdtp_sep_ind *ind; - - server = find_server(servers, src); - if (server == NULL) { - if (err) - *err = -EPROTONOSUPPORT; - return NULL; - } - - if (type == AVDTP_SEP_TYPE_SINK && !server->sink_enabled) { - if (err) - *err = -EPROTONOSUPPORT; - return NULL; - } - - if (type == AVDTP_SEP_TYPE_SOURCE && !server->source_enabled) { - if (err) - *err = -EPROTONOSUPPORT; - return NULL; - } - - sep = g_new0(struct a2dp_sep, 1); - - if (endpoint) { - ind = &endpoint_ind; - goto proceed; - } - - ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind; - -proceed: - sep->lsep = avdtp_register_sep(&server->src, type, - AVDTP_MEDIA_TYPE_AUDIO, codec, - delay_reporting, ind, &cfm, sep); - if (sep->lsep == NULL) { - g_free(sep); - if (err) - *err = -EINVAL; - return NULL; - } - - sep->server = server; - sep->endpoint = endpoint; - sep->codec = codec; - sep->type = type; - sep->delay_reporting = delay_reporting; - sep->user_data = user_data; - sep->destroy = destroy; - - if (type == AVDTP_SEP_TYPE_SOURCE) { - l = &server->sources; - record_id = &server->source_record_id; - } else { - l = &server->sinks; - record_id = &server->sink_record_id; - } - - if (*record_id != 0) - goto add; - - record = a2dp_record(type, server->version); - if (!record) { - error("Unable to allocate new service record"); - avdtp_unregister_sep(sep->lsep); - g_free(sep); - if (err) - *err = -EINVAL; - return NULL; - } - - if (add_record_to_server(&server->src, record) < 0) { - error("Unable to register A2DP service record");\ - sdp_record_free(record); - avdtp_unregister_sep(sep->lsep); - g_free(sep); - if (err) - *err = -EINVAL; - return NULL; - } - *record_id = record->handle; - -add: - *l = g_slist_append(*l, sep); - - if (err) - *err = 0; - return sep; -} - -void a2dp_remove_sep(struct a2dp_sep *sep) -{ - struct a2dp_server *server = sep->server; - - if (sep->type == AVDTP_SEP_TYPE_SOURCE) { - if (g_slist_find(server->sources, sep) == NULL) - return; - server->sources = g_slist_remove(server->sources, sep); - if (server->sources == NULL && server->source_record_id) { - remove_record_from_server(server->source_record_id); - server->source_record_id = 0; - } - } else { - if (g_slist_find(server->sinks, sep) == NULL) - return; - server->sinks = g_slist_remove(server->sinks, sep); - if (server->sinks == NULL && server->sink_record_id) { - remove_record_from_server(server->sink_record_id); - server->sink_record_id = 0; - } - } - - if (sep->locked) - return; - - a2dp_unregister_sep(sep); -} - -struct a2dp_sep *a2dp_get(struct avdtp *session, - struct avdtp_remote_sep *rsep) -{ - GSList *l; - struct a2dp_server *server; - struct avdtp_service_capability *cap; - struct avdtp_media_codec_capability *codec_cap = NULL; - bdaddr_t src; - - avdtp_get_peers(session, &src, NULL); - server = find_server(servers, &src); - if (!server) - return NULL; - - cap = avdtp_get_codec(rsep); - codec_cap = (void *) cap->data; - - if (avdtp_get_type(rsep) == AVDTP_SEP_TYPE_SINK) - l = server->sources; - else - l = server->sinks; - - for (; l != NULL; l = l->next) { - struct a2dp_sep *sep = l->data; - - if (sep->locked) - continue; - - if (sep->codec != codec_cap->media_codec_type) - continue; - - if (!sep->stream || avdtp_has_stream(session, sep->stream)) - return sep; - } - - return NULL; -} - -static uint8_t default_bitpool(uint8_t freq, uint8_t mode) -{ - switch (freq) { - case SBC_SAMPLING_FREQ_16000: - case SBC_SAMPLING_FREQ_32000: - return 53; - case SBC_SAMPLING_FREQ_44100: - switch (mode) { - case SBC_CHANNEL_MODE_MONO: - case SBC_CHANNEL_MODE_DUAL_CHANNEL: - return 31; - case SBC_CHANNEL_MODE_STEREO: - case SBC_CHANNEL_MODE_JOINT_STEREO: - return 53; - default: - error("Invalid channel mode %u", mode); - return 53; - } - case SBC_SAMPLING_FREQ_48000: - switch (mode) { - case SBC_CHANNEL_MODE_MONO: - case SBC_CHANNEL_MODE_DUAL_CHANNEL: - return 29; - case SBC_CHANNEL_MODE_STEREO: - case SBC_CHANNEL_MODE_JOINT_STEREO: - return 51; - default: - error("Invalid channel mode %u", mode); - return 51; - } - default: - error("Invalid sampling freq %u", freq); - return 53; - } -} - -static gboolean select_sbc_params(struct sbc_codec_cap *cap, - struct sbc_codec_cap *supported) -{ - unsigned int max_bitpool, min_bitpool; - - memset(cap, 0, sizeof(struct sbc_codec_cap)); - - cap->cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - cap->cap.media_codec_type = A2DP_CODEC_SBC; - - if (supported->frequency & SBC_SAMPLING_FREQ_44100) - cap->frequency = SBC_SAMPLING_FREQ_44100; - else if (supported->frequency & SBC_SAMPLING_FREQ_48000) - cap->frequency = SBC_SAMPLING_FREQ_48000; - else if (supported->frequency & SBC_SAMPLING_FREQ_32000) - cap->frequency = SBC_SAMPLING_FREQ_32000; - else if (supported->frequency & SBC_SAMPLING_FREQ_16000) - cap->frequency = SBC_SAMPLING_FREQ_16000; - else { - error("No supported frequencies"); - return FALSE; - } - - if (supported->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO) - cap->channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO; - else if (supported->channel_mode & SBC_CHANNEL_MODE_STEREO) - cap->channel_mode = SBC_CHANNEL_MODE_STEREO; - else if (supported->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL) - cap->channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL; - else if (supported->channel_mode & SBC_CHANNEL_MODE_MONO) - cap->channel_mode = SBC_CHANNEL_MODE_MONO; - else { - error("No supported channel modes"); - return FALSE; - } - - if (supported->block_length & SBC_BLOCK_LENGTH_16) - cap->block_length = SBC_BLOCK_LENGTH_16; - else if (supported->block_length & SBC_BLOCK_LENGTH_12) - cap->block_length = SBC_BLOCK_LENGTH_12; - else if (supported->block_length & SBC_BLOCK_LENGTH_8) - cap->block_length = SBC_BLOCK_LENGTH_8; - else if (supported->block_length & SBC_BLOCK_LENGTH_4) - cap->block_length = SBC_BLOCK_LENGTH_4; - else { - error("No supported block lengths"); - return FALSE; - } - - if (supported->subbands & SBC_SUBBANDS_8) - cap->subbands = SBC_SUBBANDS_8; - else if (supported->subbands & SBC_SUBBANDS_4) - cap->subbands = SBC_SUBBANDS_4; - else { - error("No supported subbands"); - return FALSE; - } - - if (supported->allocation_method & SBC_ALLOCATION_LOUDNESS) - cap->allocation_method = SBC_ALLOCATION_LOUDNESS; - else if (supported->allocation_method & SBC_ALLOCATION_SNR) - cap->allocation_method = SBC_ALLOCATION_SNR; - - min_bitpool = MAX(MIN_BITPOOL, supported->min_bitpool); - max_bitpool = MIN(default_bitpool(cap->frequency, cap->channel_mode), - supported->max_bitpool); - - cap->min_bitpool = min_bitpool; - cap->max_bitpool = max_bitpool; - - return TRUE; -} - -static gboolean select_capabilities(struct avdtp *session, - struct avdtp_remote_sep *rsep, - GSList **caps) -{ - struct avdtp_service_capability *media_transport, *media_codec; - struct sbc_codec_cap sbc_cap; - - media_codec = avdtp_get_codec(rsep); - if (!media_codec) - return FALSE; - - select_sbc_params(&sbc_cap, (struct sbc_codec_cap *) media_codec->data); - - media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, - NULL, 0); - - *caps = g_slist_append(*caps, media_transport); - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, - sizeof(sbc_cap)); - - *caps = g_slist_append(*caps, media_codec); - - if (avdtp_get_delay_reporting(rsep)) { - struct avdtp_service_capability *delay_reporting; - delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, - NULL, 0); - *caps = g_slist_append(*caps, delay_reporting); - } - - return TRUE; -} - -static void select_cb(struct a2dp_setup *setup, void *ret, int size) -{ - struct avdtp_service_capability *media_transport, *media_codec; - struct avdtp_media_codec_capability *cap; - - if (size < 0) { - DBG("Endpoint replied an invalid configuration"); - goto done; - } - - media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, - NULL, 0); - - setup->caps = g_slist_append(setup->caps, media_transport); - - cap = g_malloc0(sizeof(*cap) + size); - cap->media_type = AVDTP_MEDIA_TYPE_AUDIO; - cap->media_codec_type = setup->sep->codec; - memcpy(cap->data, ret, size); - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, cap, - sizeof(*cap) + size); - - setup->caps = g_slist_append(setup->caps, media_codec); - g_free(cap); - -done: - finalize_select(setup); -} - -static gboolean auto_select(gpointer data) -{ - struct a2dp_setup *setup = data; - - finalize_select(setup); - - return FALSE; -} - -static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list, - const char *sender) -{ - for (; list; list = list->next) { - struct a2dp_sep *sep = list->data; - - /* Use sender's endpoint if available */ - if (sender) { - const char *name; - - if (sep->endpoint == NULL) - continue; - - name = sep->endpoint->get_name(sep, sep->user_data); - if (g_strcmp0(sender, name) != 0) - continue; - } - - if (avdtp_find_remote_sep(session, sep->lsep) == NULL) - continue; - - return sep; - } - - return NULL; -} - -static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type, - const char *sender) -{ - struct a2dp_server *server; - struct a2dp_sep *sep; - GSList *l; - bdaddr_t src; - - avdtp_get_peers(session, &src, NULL); - server = find_server(servers, &src); - if (!server) - return NULL; - - l = type == AVDTP_SEP_TYPE_SINK ? server->sources : server->sinks; - - /* Check sender's seps first */ - sep = a2dp_find_sep(session, l, sender); - if (sep != NULL) - return sep; - - return a2dp_find_sep(session, l, NULL); -} - -unsigned int a2dp_select_capabilities(struct avdtp *session, - uint8_t type, const char *sender, - a2dp_select_cb_t cb, - void *user_data) -{ - struct a2dp_setup *setup; - struct a2dp_setup_cb *cb_data; - struct a2dp_sep *sep; - struct avdtp_service_capability *service; - struct avdtp_media_codec_capability *codec; - int err; - - sep = a2dp_select_sep(session, type, sender); - if (!sep) { - error("Unable to select SEP"); - return 0; - } - - setup = a2dp_setup_get(session); - if (!setup) - return 0; - - cb_data = setup_cb_new(setup); - cb_data->select_cb = cb; - cb_data->user_data = user_data; - - setup->sep = sep; - setup->rsep = avdtp_find_remote_sep(session, sep->lsep); - - if (setup->rsep == NULL) { - error("Could not find remote sep"); - goto fail; - } - - /* FIXME: Remove auto select when it is not longer possible to register - endpoint in the configuration file */ - if (sep->endpoint == NULL) { - if (!select_capabilities(session, setup->rsep, - &setup->caps)) { - error("Unable to auto select remote SEP capabilities"); - goto fail; - } - - g_idle_add(auto_select, setup); - - return cb_data->id; - } - - service = avdtp_get_codec(setup->rsep); - codec = (struct avdtp_media_codec_capability *) service->data; - - err = sep->endpoint->select_configuration(sep, codec->data, - service->length - sizeof(*codec), - setup, - select_cb, sep->user_data); - if (err == 0) - return cb_data->id; - -fail: - setup_cb_free(cb_data); - return 0; - -} - -unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep, - a2dp_config_cb_t cb, GSList *caps, - void *user_data) -{ - struct a2dp_setup_cb *cb_data; - GSList *l; - struct a2dp_server *server; - struct a2dp_setup *setup; - struct a2dp_sep *tmp; - struct avdtp_service_capability *cap; - struct avdtp_media_codec_capability *codec_cap = NULL; - int posix_err; - bdaddr_t src; - - avdtp_get_peers(session, &src, NULL); - server = find_server(servers, &src); - if (!server) - return 0; - - for (l = caps; l != NULL; l = l->next) { - cap = l->data; - - if (cap->category != AVDTP_MEDIA_CODEC) - continue; - - codec_cap = (void *) cap->data; - break; - } - - if (!codec_cap) - return 0; - - if (sep->codec != codec_cap->media_codec_type) - return 0; - - DBG("a2dp_config: selected SEP %p", sep->lsep); - - setup = a2dp_setup_get(session); - if (!setup) - return 0; - - cb_data = setup_cb_new(setup); - cb_data->config_cb = cb; - cb_data->user_data = user_data; - - setup->sep = sep; - setup->stream = sep->stream; - - /* Copy given caps if they are different than current caps */ - if (setup->caps != caps) { - g_slist_free_full(setup->caps, g_free); - setup->caps = g_slist_copy(caps); - } - - switch (avdtp_sep_get_state(sep->lsep)) { - case AVDTP_STATE_IDLE: - if (sep->type == AVDTP_SEP_TYPE_SOURCE) - l = server->sources; - else - l = server->sinks; - - for (; l != NULL; l = l->next) { - tmp = l->data; - if (avdtp_has_stream(session, tmp->stream)) - break; - } - - if (l != NULL) { - if (a2dp_sep_get_lock(tmp)) - goto failed; - setup->reconfigure = TRUE; - if (avdtp_close(session, tmp->stream, FALSE) < 0) { - error("avdtp_close failed"); - goto failed; - } - break; - } - - setup->rsep = avdtp_find_remote_sep(session, sep->lsep); - if (setup->rsep == NULL) { - error("No matching ACP and INT SEPs found"); - goto failed; - } - - posix_err = avdtp_set_configuration(session, setup->rsep, - sep->lsep, caps, - &setup->stream); - if (posix_err < 0) { - error("avdtp_set_configuration: %s", - strerror(-posix_err)); - goto failed; - } - break; - case AVDTP_STATE_OPEN: - case AVDTP_STATE_STREAMING: - if (avdtp_stream_has_capabilities(setup->stream, caps)) { - DBG("Configuration match: resuming"); - cb_data->source_id = g_idle_add(finalize_config, - setup); - } else if (!setup->reconfigure) { - setup->reconfigure = TRUE; - if (avdtp_close(session, sep->stream, FALSE) < 0) { - error("avdtp_close failed"); - goto failed; - } - } - break; - default: - error("SEP in bad state for requesting a new stream"); - goto failed; - } - - return cb_data->id; - -failed: - setup_cb_free(cb_data); - return 0; -} - -unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep, - a2dp_stream_cb_t cb, void *user_data) -{ - struct a2dp_setup_cb *cb_data; - struct a2dp_setup *setup; - - setup = a2dp_setup_get(session); - if (!setup) - return 0; - - cb_data = setup_cb_new(setup); - cb_data->resume_cb = cb; - cb_data->user_data = user_data; - - setup->sep = sep; - setup->stream = sep->stream; - - switch (avdtp_sep_get_state(sep->lsep)) { - case AVDTP_STATE_IDLE: - goto failed; - break; - case AVDTP_STATE_OPEN: - if (avdtp_start(session, sep->stream) < 0) { - error("avdtp_start failed"); - goto failed; - } - sep->starting = TRUE; - break; - case AVDTP_STATE_STREAMING: - if (!sep->suspending && sep->suspend_timer) { - g_source_remove(sep->suspend_timer); - sep->suspend_timer = 0; - avdtp_unref(sep->session); - sep->session = NULL; - } - if (sep->suspending) - setup->start = TRUE; - else - cb_data->source_id = g_idle_add(finalize_resume, - setup); - break; - default: - error("SEP in bad state for resume"); - goto failed; - } - - return cb_data->id; - -failed: - setup_cb_free(cb_data); - return 0; -} - -unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep, - a2dp_stream_cb_t cb, void *user_data) -{ - struct a2dp_setup_cb *cb_data; - struct a2dp_setup *setup; - - setup = a2dp_setup_get(session); - if (!setup) - return 0; - - cb_data = setup_cb_new(setup); - cb_data->suspend_cb = cb; - cb_data->user_data = user_data; - - setup->sep = sep; - setup->stream = sep->stream; - - switch (avdtp_sep_get_state(sep->lsep)) { - case AVDTP_STATE_IDLE: - error("a2dp_suspend: no stream to suspend"); - goto failed; - break; - case AVDTP_STATE_OPEN: - cb_data->source_id = g_idle_add(finalize_suspend, setup); - break; - case AVDTP_STATE_STREAMING: - if (avdtp_suspend(session, sep->stream) < 0) { - error("avdtp_suspend failed"); - goto failed; - } - sep->suspending = TRUE; - break; - default: - error("SEP in bad state for suspend"); - goto failed; - } - - return cb_data->id; - -failed: - setup_cb_free(cb_data); - return 0; -} - -gboolean a2dp_cancel(struct audio_device *dev, unsigned int id) -{ - struct a2dp_setup *setup; - GSList *l; - - setup = find_setup_by_dev(dev); - if (!setup) - return FALSE; - - for (l = setup->cb; l != NULL; l = g_slist_next(l)) { - struct a2dp_setup_cb *cb = l->data; - - if (cb->id != id) - continue; - - setup_ref(setup); - setup_cb_free(cb); - - if (!setup->cb) { - DBG("aborting setup %p", setup); - avdtp_abort(setup->session, setup->stream); - return TRUE; - } - - setup_unref(setup); - return TRUE; - } - - return FALSE; -} - -gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session) -{ - if (sep->locked) - return FALSE; - - DBG("SEP %p locked", sep->lsep); - sep->locked = TRUE; - - return TRUE; -} - -gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session) -{ - struct a2dp_server *server = sep->server; - avdtp_state_t state; - GSList *l; - - state = avdtp_sep_get_state(sep->lsep); - - sep->locked = FALSE; - - DBG("SEP %p unlocked", sep->lsep); - - if (sep->type == AVDTP_SEP_TYPE_SOURCE) - l = server->sources; - else - l = server->sinks; - - /* Unregister sep if it was removed */ - if (g_slist_find(l, sep) == NULL) { - a2dp_unregister_sep(sep); - return TRUE; - } - - if (!sep->stream || state == AVDTP_STATE_IDLE) - return TRUE; - - switch (state) { - case AVDTP_STATE_OPEN: - /* Set timer here */ - break; - case AVDTP_STATE_STREAMING: - if (avdtp_suspend(session, sep->stream) == 0) - sep->suspending = TRUE; - break; - default: - break; - } - - return TRUE; -} - -gboolean a2dp_sep_get_lock(struct a2dp_sep *sep) -{ - return sep->locked; -} - -static int stream_cmp(gconstpointer data, gconstpointer user_data) -{ - const struct a2dp_sep *sep = data; - const struct avdtp_stream *stream = user_data; - - return (sep->stream != stream); -} - -struct a2dp_sep *a2dp_get_sep(struct avdtp *session, - struct avdtp_stream *stream) -{ - struct a2dp_server *server; - bdaddr_t src, dst; - GSList *l; - - avdtp_get_peers(session, &src, &dst); - - for (l = servers; l; l = l->next) { - server = l->data; - - if (bacmp(&src, &server->src) == 0) - break; - } - - if (!l) - return NULL; - - l = g_slist_find_custom(server->sources, stream, stream_cmp); - if (l) - return l->data; - - l = g_slist_find_custom(server->sinks, stream, stream_cmp); - if (l) - return l->data; - - return NULL; -} - -struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep) -{ - return sep->stream; -} diff -Nru bluez-4.101/audio/a2dp-codecs.h bluez-5.23/audio/a2dp-codecs.h --- bluez-4.101/audio/a2dp-codecs.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/a2dp-codecs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,116 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define A2DP_CODEC_SBC 0x00 -#define A2DP_CODEC_MPEG12 0x01 -#define A2DP_CODEC_MPEG24 0x02 -#define A2DP_CODEC_ATRAC 0x03 - -#define SBC_SAMPLING_FREQ_16000 (1 << 3) -#define SBC_SAMPLING_FREQ_32000 (1 << 2) -#define SBC_SAMPLING_FREQ_44100 (1 << 1) -#define SBC_SAMPLING_FREQ_48000 1 - -#define SBC_CHANNEL_MODE_MONO (1 << 3) -#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) -#define SBC_CHANNEL_MODE_STEREO (1 << 1) -#define SBC_CHANNEL_MODE_JOINT_STEREO 1 - -#define SBC_BLOCK_LENGTH_4 (1 << 3) -#define SBC_BLOCK_LENGTH_8 (1 << 2) -#define SBC_BLOCK_LENGTH_12 (1 << 1) -#define SBC_BLOCK_LENGTH_16 1 - -#define SBC_SUBBANDS_4 (1 << 1) -#define SBC_SUBBANDS_8 1 - -#define SBC_ALLOCATION_SNR (1 << 1) -#define SBC_ALLOCATION_LOUDNESS 1 - -#define MPEG_CHANNEL_MODE_MONO (1 << 3) -#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) -#define MPEG_CHANNEL_MODE_STEREO (1 << 1) -#define MPEG_CHANNEL_MODE_JOINT_STEREO 1 - -#define MPEG_LAYER_MP1 (1 << 2) -#define MPEG_LAYER_MP2 (1 << 1) -#define MPEG_LAYER_MP3 1 - -#define MPEG_SAMPLING_FREQ_16000 (1 << 5) -#define MPEG_SAMPLING_FREQ_22050 (1 << 4) -#define MPEG_SAMPLING_FREQ_24000 (1 << 3) -#define MPEG_SAMPLING_FREQ_32000 (1 << 2) -#define MPEG_SAMPLING_FREQ_44100 (1 << 1) -#define MPEG_SAMPLING_FREQ_48000 1 - -#define MAX_BITPOOL 64 -#define MIN_BITPOOL 2 - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -typedef struct { - uint8_t channel_mode:4; - uint8_t frequency:4; - uint8_t allocation_method:2; - uint8_t subbands:2; - uint8_t block_length:4; - uint8_t min_bitpool; - uint8_t max_bitpool; -} __attribute__ ((packed)) a2dp_sbc_t; - -typedef struct { - uint8_t channel_mode:4; - uint8_t crc:1; - uint8_t layer:3; - uint8_t frequency:6; - uint8_t mpf:1; - uint8_t rfa:1; - uint16_t bitrate; -} __attribute__ ((packed)) a2dp_mpeg_t; - -#elif __BYTE_ORDER == __BIG_ENDIAN - -typedef struct { - uint8_t frequency:4; - uint8_t channel_mode:4; - uint8_t block_length:4; - uint8_t subbands:2; - uint8_t allocation_method:2; - uint8_t min_bitpool; - uint8_t max_bitpool; -} __attribute__ ((packed)) a2dp_sbc_t; - -typedef struct { - uint8_t layer:3; - uint8_t crc:1; - uint8_t channel_mode:4; - uint8_t rfa:1; - uint8_t mpf:1; - uint8_t frequency:6; - uint16_t bitrate; -} __attribute__ ((packed)) a2dp_mpeg_t; - -#else -#error "Unknown byte order" -#endif diff -Nru bluez-4.101/audio/a2dp.h bluez-5.23/audio/a2dp.h --- bluez-4.101/audio/a2dp.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/a2dp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,194 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved. - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define A2DP_CODEC_SBC 0x00 -#define A2DP_CODEC_MPEG12 0x01 -#define A2DP_CODEC_MPEG24 0x02 -#define A2DP_CODEC_ATRAC 0x03 - -#define SBC_SAMPLING_FREQ_16000 (1 << 3) -#define SBC_SAMPLING_FREQ_32000 (1 << 2) -#define SBC_SAMPLING_FREQ_44100 (1 << 1) -#define SBC_SAMPLING_FREQ_48000 1 - -#define SBC_CHANNEL_MODE_MONO (1 << 3) -#define SBC_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) -#define SBC_CHANNEL_MODE_STEREO (1 << 1) -#define SBC_CHANNEL_MODE_JOINT_STEREO 1 - -#define SBC_BLOCK_LENGTH_4 (1 << 3) -#define SBC_BLOCK_LENGTH_8 (1 << 2) -#define SBC_BLOCK_LENGTH_12 (1 << 1) -#define SBC_BLOCK_LENGTH_16 1 - -#define SBC_SUBBANDS_4 (1 << 1) -#define SBC_SUBBANDS_8 1 - -#define SBC_ALLOCATION_SNR (1 << 1) -#define SBC_ALLOCATION_LOUDNESS 1 - -#define MPEG_CHANNEL_MODE_MONO (1 << 3) -#define MPEG_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) -#define MPEG_CHANNEL_MODE_STEREO (1 << 1) -#define MPEG_CHANNEL_MODE_JOINT_STEREO 1 - -#define MPEG_LAYER_MP1 (1 << 2) -#define MPEG_LAYER_MP2 (1 << 1) -#define MPEG_LAYER_MP3 1 - -#define MPEG_SAMPLING_FREQ_16000 (1 << 5) -#define MPEG_SAMPLING_FREQ_22050 (1 << 4) -#define MPEG_SAMPLING_FREQ_24000 (1 << 3) -#define MPEG_SAMPLING_FREQ_32000 (1 << 2) -#define MPEG_SAMPLING_FREQ_44100 (1 << 1) -#define MPEG_SAMPLING_FREQ_48000 1 - -#define MAX_BITPOOL 64 -#define MIN_BITPOOL 2 - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct sbc_codec_cap { - struct avdtp_media_codec_capability cap; - uint8_t channel_mode:4; - uint8_t frequency:4; - uint8_t allocation_method:2; - uint8_t subbands:2; - uint8_t block_length:4; - uint8_t min_bitpool; - uint8_t max_bitpool; -} __attribute__ ((packed)); - -struct mpeg_codec_cap { - struct avdtp_media_codec_capability cap; - uint8_t channel_mode:4; - uint8_t crc:1; - uint8_t layer:3; - uint8_t frequency:6; - uint8_t mpf:1; - uint8_t rfa:1; - uint16_t bitrate; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct sbc_codec_cap { - struct avdtp_media_codec_capability cap; - uint8_t frequency:4; - uint8_t channel_mode:4; - uint8_t block_length:4; - uint8_t subbands:2; - uint8_t allocation_method:2; - uint8_t min_bitpool; - uint8_t max_bitpool; -} __attribute__ ((packed)); - -struct mpeg_codec_cap { - struct avdtp_media_codec_capability cap; - uint8_t layer:3; - uint8_t crc:1; - uint8_t channel_mode:4; - uint8_t rfa:1; - uint8_t mpf:1; - uint8_t frequency:6; - uint16_t bitrate; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif - -struct a2dp_sep; -struct a2dp_setup; - -typedef void (*a2dp_endpoint_select_t) (struct a2dp_setup *setup, void *ret, - int size); -typedef void (*a2dp_endpoint_config_t) (struct a2dp_setup *setup, gboolean ret); - -struct a2dp_endpoint { - const char *(*get_name) (struct a2dp_sep *sep, void *user_data); - size_t (*get_capabilities) (struct a2dp_sep *sep, - uint8_t **capabilities, - void *user_data); - int (*select_configuration) (struct a2dp_sep *sep, - uint8_t *capabilities, - size_t length, - struct a2dp_setup *setup, - a2dp_endpoint_select_t cb, - void *user_data); - int (*set_configuration) (struct a2dp_sep *sep, - struct audio_device *dev, - uint8_t *configuration, - size_t length, - struct a2dp_setup *setup, - a2dp_endpoint_config_t cb, - void *user_data); - void (*clear_configuration) (struct a2dp_sep *sep, void *user_data); - void (*set_delay) (struct a2dp_sep *sep, uint16_t delay, - void *user_data); -}; - -typedef void (*a2dp_select_cb_t) (struct avdtp *session, - struct a2dp_sep *sep, GSList *caps, - void *user_data); -typedef void (*a2dp_config_cb_t) (struct avdtp *session, struct a2dp_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, - void *user_data); -typedef void (*a2dp_stream_cb_t) (struct avdtp *session, - struct avdtp_error *err, - void *user_data); - -int a2dp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config); -void a2dp_unregister(const bdaddr_t *src); - -struct a2dp_sep *a2dp_add_sep(const bdaddr_t *src, uint8_t type, - uint8_t codec, gboolean delay_reporting, - struct a2dp_endpoint *endpoint, - void *user_data, GDestroyNotify destroy, - int *err); -void a2dp_remove_sep(struct a2dp_sep *sep); - -struct a2dp_sep *a2dp_get(struct avdtp *session, struct avdtp_remote_sep *sep); - -unsigned int a2dp_select_capabilities(struct avdtp *session, - uint8_t type, const char *sender, - a2dp_select_cb_t cb, - void *user_data); -unsigned int a2dp_config(struct avdtp *session, struct a2dp_sep *sep, - a2dp_config_cb_t cb, GSList *caps, - void *user_data); -unsigned int a2dp_resume(struct avdtp *session, struct a2dp_sep *sep, - a2dp_stream_cb_t cb, void *user_data); -unsigned int a2dp_suspend(struct avdtp *session, struct a2dp_sep *sep, - a2dp_stream_cb_t cb, void *user_data); -gboolean a2dp_cancel(struct audio_device *dev, unsigned int id); - -gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session); -gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session); -gboolean a2dp_sep_get_lock(struct a2dp_sep *sep); -struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep); -struct a2dp_sep *a2dp_get_sep(struct avdtp *session, - struct avdtp_stream *stream); diff -Nru bluez-4.101/audio/audio.conf bluez-5.23/audio/audio.conf --- bluez-4.101/audio/audio.conf 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/audio.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -# Configuration file for the audio service - -# This section contains options which are not specific to any -# particular interface -[General] - -# Switch to master role for incoming connections (defaults to true) -#Master=true - -# If we want to disable support for specific services -# Defaults to supporting all implemented services -#Disable=Gateway,Source,Socket - -# SCO routing. Either PCM or HCI (in which case audio is routed to/from ALSA) -# Defaults to HCI -#SCORouting=PCM - -# Automatically connect both A2DP and HFP/HSP profiles for incoming -# connections. Some headsets that support both profiles will only connect the -# other one automatically so the default setting of true is usually a good -# idea. -#AutoConnect=true - -# Headset interface specific options (i.e. options which affect how the audio -# service interacts with remote headset devices) -[Headset] - -# Set to true to support HFP, false means only HSP is supported -# Defaults to true -HFP=true - -# Maximum number of connected HSP/HFP devices per adapter. Defaults to 1 -MaxConnected=1 - -# Set to true to enable use of fast connectable mode (faster page scanning) -# for HFP when incoming call starts. Default settings are restored after -# call is answered or rejected. Page scan interval is much shorter and page -# scan type changed to interlaced. Such allows faster connection initiated -# by a headset. -FastConnectable=false - -# Just an example of potential config options for the other interfaces -#[A2DP] -#SBCSources=1 -#MPEG12Sources=0 diff -Nru bluez-4.101/audio/avctp.c bluez-5.23/audio/avctp.c --- bluez-4.101/audio/avctp.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/avctp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1115 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * Copyright (C) 2011 Texas Instruments, Inc. - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "adapter.h" -#include "../src/device.h" - -#include "log.h" -#include "error.h" -#include "uinput.h" -#include "btio.h" -#include "manager.h" -#include "device.h" -#include "avctp.h" -#include "avrcp.h" - -#define QUIRK_NO_RELEASE 1 << 0 - -/* Message types */ -#define AVCTP_COMMAND 0 -#define AVCTP_RESPONSE 1 - -/* Packet types */ -#define AVCTP_PACKET_SINGLE 0 -#define AVCTP_PACKET_START 1 -#define AVCTP_PACKET_CONTINUE 2 -#define AVCTP_PACKET_END 3 - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct avctp_header { - uint8_t ipid:1; - uint8_t cr:1; - uint8_t packet_type:2; - uint8_t transaction:4; - uint16_t pid; -} __attribute__ ((packed)); -#define AVCTP_HEADER_LENGTH 3 - -struct avc_header { - uint8_t code:4; - uint8_t _hdr0:4; - uint8_t subunit_id:3; - uint8_t subunit_type:5; - uint8_t opcode; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct avctp_header { - uint8_t transaction:4; - uint8_t packet_type:2; - uint8_t cr:1; - uint8_t ipid:1; - uint16_t pid; -} __attribute__ ((packed)); -#define AVCTP_HEADER_LENGTH 3 - -struct avc_header { - uint8_t _hdr0:4; - uint8_t code:4; - uint8_t subunit_type:5; - uint8_t subunit_id:3; - uint8_t opcode; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif - -struct avctp_state_callback { - avctp_state_cb cb; - void *user_data; - unsigned int id; -}; - -struct avctp_server { - bdaddr_t src; - GIOChannel *io; - GSList *sessions; -}; - -struct avctp_rsp_handler { - uint8_t id; - avctp_rsp_cb func; - void *user_data; -}; - -struct avctp { - struct avctp_server *server; - bdaddr_t dst; - - avctp_state_t state; - - int uinput; - - GIOChannel *io; - guint io_id; - - uint16_t mtu; - - uint8_t key_quirks[256]; - GSList *handlers; -}; - -struct avctp_pdu_handler { - uint8_t opcode; - avctp_pdu_cb cb; - void *user_data; - unsigned int id; -}; - -static struct { - const char *name; - uint8_t avc; - uint16_t uinput; -} key_map[] = { - { "PLAY", PLAY_OP, KEY_PLAYCD }, - { "STOP", STAVC_OP_OP, KEY_STOPCD }, - { "PAUSE", PAUSE_OP, KEY_PAUSECD }, - { "FORWARD", FORWARD_OP, KEY_NEXTSONG }, - { "BACKWARD", BACKWARD_OP, KEY_PREVIOUSSONG }, - { "REWIND", REWIND_OP, KEY_REWIND }, - { "FAST FORWARD", FAST_FORWARD_OP, KEY_FASTFORWARD }, - { NULL } -}; - -static GSList *callbacks = NULL; -static GSList *servers = NULL; -static GSList *handlers = NULL; -static uint8_t id = 0; - -static void auth_cb(DBusError *derr, void *user_data); - -static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) -{ - struct uinput_event event; - - memset(&event, 0, sizeof(event)); - event.type = type; - event.code = code; - event.value = value; - - return write(fd, &event, sizeof(event)); -} - -static void send_key(int fd, uint16_t key, int pressed) -{ - if (fd < 0) - return; - - send_event(fd, EV_KEY, key, pressed); - send_event(fd, EV_SYN, SYN_REPORT, 0); -} - -static size_t handle_panel_passthrough(struct avctp *session, - uint8_t transaction, uint8_t *code, - uint8_t *subunit, uint8_t *operands, - size_t operand_count, void *user_data) -{ - const char *status; - int pressed, i; - - if (*code != AVC_CTYPE_CONTROL || *subunit != AVC_SUBUNIT_PANEL) { - *code = AVC_CTYPE_REJECTED; - return 0; - } - - if (operand_count == 0) - goto done; - - if (operands[0] & 0x80) { - status = "released"; - pressed = 0; - } else { - status = "pressed"; - pressed = 1; - } - - for (i = 0; key_map[i].name != NULL; i++) { - uint8_t key_quirks; - - if ((operands[0] & 0x7F) != key_map[i].avc) - continue; - - DBG("AV/C: %s %s", key_map[i].name, status); - - key_quirks = session->key_quirks[key_map[i].avc]; - - if (key_quirks & QUIRK_NO_RELEASE) { - if (!pressed) { - DBG("AV/C: Ignoring release"); - break; - } - - DBG("AV/C: treating key press as press + release"); - send_key(session->uinput, key_map[i].uinput, 1); - send_key(session->uinput, key_map[i].uinput, 0); - break; - } - - send_key(session->uinput, key_map[i].uinput, pressed); - break; - } - - if (key_map[i].name == NULL) { - DBG("AV/C: unknown button 0x%02X %s", - operands[0] & 0x7F, status); - *code = AVC_CTYPE_NOT_IMPLEMENTED; - return 0; - } - -done: - *code = AVC_CTYPE_ACCEPTED; - return operand_count; -} - -static size_t handle_unit_info(struct avctp *session, - uint8_t transaction, uint8_t *code, - uint8_t *subunit, uint8_t *operands, - size_t operand_count, void *user_data) -{ - if (*code != AVC_CTYPE_STATUS) { - *code = AVC_CTYPE_REJECTED; - return 0; - } - - *code = AVC_CTYPE_STABLE; - - /* The first operand should be 0x07 for the UNITINFO response. - * Neither AVRCP (section 22.1, page 117) nor AVC Digital - * Interface Command Set (section 9.2.1, page 45) specs - * explain this value but both use it */ - if (operand_count >= 1) - operands[0] = 0x07; - if (operand_count >= 2) - operands[1] = AVC_SUBUNIT_PANEL << 3; - - DBG("reply to AVC_OP_UNITINFO"); - - return operand_count; -} - -static size_t handle_subunit_info(struct avctp *session, - uint8_t transaction, uint8_t *code, - uint8_t *subunit, uint8_t *operands, - size_t operand_count, void *user_data) -{ - if (*code != AVC_CTYPE_STATUS) { - *code = AVC_CTYPE_REJECTED; - return 0; - } - - *code = AVC_CTYPE_STABLE; - - /* The first operand should be 0x07 for the UNITINFO response. - * Neither AVRCP (section 22.1, page 117) nor AVC Digital - * Interface Command Set (section 9.2.1, page 45) specs - * explain this value but both use it */ - if (operand_count >= 2) - operands[1] = AVC_SUBUNIT_PANEL << 3; - - DBG("reply to AVC_OP_SUBUNITINFO"); - - return operand_count; -} - -static struct avctp_pdu_handler *find_handler(GSList *list, uint8_t opcode) -{ - for (; list; list = list->next) { - struct avctp_pdu_handler *handler = list->data; - - if (handler->opcode == opcode) - return handler; - } - - return NULL; -} - -static void avctp_disconnected(struct avctp *session) -{ - struct avctp_server *server; - - if (!session) - return; - - if (session->io) { - g_io_channel_shutdown(session->io, TRUE, NULL); - g_io_channel_unref(session->io); - session->io = NULL; - } - - if (session->io_id) { - g_source_remove(session->io_id); - session->io_id = 0; - - if (session->state == AVCTP_STATE_CONNECTING) { - struct audio_device *dev; - - dev = manager_get_device(&session->server->src, - &session->dst, FALSE); - audio_device_cancel_authorization(dev, auth_cb, - session); - } - } - - if (session->uinput >= 0) { - char address[18]; - - ba2str(&session->dst, address); - DBG("AVCTP: closing uinput for %s", address); - - ioctl(session->uinput, UI_DEV_DESTROY); - close(session->uinput); - session->uinput = -1; - } - - server = session->server; - server->sessions = g_slist_remove(server->sessions, session); - g_slist_free_full(session->handlers, g_free); - g_free(session); -} - -static void avctp_set_state(struct avctp *session, avctp_state_t new_state) -{ - GSList *l; - struct audio_device *dev; - avctp_state_t old_state = session->state; - - dev = manager_get_device(&session->server->src, &session->dst, FALSE); - if (dev == NULL) { - error("avdtp_set_state(): no matching audio device"); - return; - } - - session->state = new_state; - - for (l = callbacks; l != NULL; l = l->next) { - struct avctp_state_callback *cb = l->data; - cb->cb(dev, old_state, new_state, cb->user_data); - } - - switch (new_state) { - case AVCTP_STATE_DISCONNECTED: - DBG("AVCTP Disconnected"); - - avctp_disconnected(session); - - if (old_state != AVCTP_STATE_CONNECTED) - break; - - if (!audio_device_is_active(dev, NULL)) - audio_device_set_authorized(dev, FALSE); - - break; - case AVCTP_STATE_CONNECTING: - DBG("AVCTP Connecting"); - break; - case AVCTP_STATE_CONNECTED: - DBG("AVCTP Connected"); - break; - default: - error("Invalid AVCTP state %d", new_state); - return; - } -} - -static void handle_response(struct avctp *session, struct avctp_header *avctp, - struct avc_header *avc, uint8_t *operands, - size_t operand_count) -{ - GSList *l; - - for (l = session->handlers; l; l = l->next) { - struct avctp_rsp_handler *handler = l->data; - - if (handler->id != avctp->transaction) - continue; - - if (handler->func && handler->func(session, avc->code, - avc->subunit_type, - operands, operand_count, - handler->user_data)) - return; - - session->handlers = g_slist_remove(session->handlers, handler); - g_free(handler); - - return; - } -} - -static gboolean session_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) -{ - struct avctp *session = data; - uint8_t buf[1024], *operands, code, subunit; - struct avctp_header *avctp; - struct avc_header *avc; - int ret, packet_size, operand_count, sock; - struct avctp_pdu_handler *handler; - - if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) - goto failed; - - sock = g_io_channel_unix_get_fd(session->io); - - ret = read(sock, buf, sizeof(buf)); - if (ret <= 0) - goto failed; - - DBG("Got %d bytes of data for AVCTP session %p", ret, session); - - if ((unsigned int) ret < sizeof(struct avctp_header)) { - error("Too small AVCTP packet"); - goto failed; - } - - avctp = (struct avctp_header *) buf; - - DBG("AVCTP transaction %u, packet type %u, C/R %u, IPID %u, " - "PID 0x%04X", - avctp->transaction, avctp->packet_type, - avctp->cr, avctp->ipid, ntohs(avctp->pid)); - - ret -= sizeof(struct avctp_header); - if ((unsigned int) ret < sizeof(struct avc_header)) { - error("Too small AVCTP packet"); - goto failed; - } - - avc = (struct avc_header *) (buf + sizeof(struct avctp_header)); - - ret -= sizeof(struct avc_header); - - operands = buf + sizeof(struct avctp_header) + sizeof(struct avc_header); - operand_count = ret; - - DBG("AV/C %s 0x%01X, subunit_type 0x%02X, subunit_id 0x%01X, " - "opcode 0x%02X, %d operands", - avctp->cr ? "response" : "command", - avc->code, avc->subunit_type, avc->subunit_id, - avc->opcode, operand_count); - - if (avctp->cr == AVCTP_RESPONSE) { - handle_response(session, avctp, avc, operands, operand_count); - return TRUE; - } - - packet_size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH; - avctp->cr = AVCTP_RESPONSE; - - if (avctp->packet_type != AVCTP_PACKET_SINGLE) { - avc->code = AVC_CTYPE_NOT_IMPLEMENTED; - goto done; - } - - if (avctp->pid != htons(AV_REMOTE_SVCLASS_ID)) { - avctp->ipid = 1; - avc->code = AVC_CTYPE_REJECTED; - goto done; - } - - handler = find_handler(handlers, avc->opcode); - if (!handler) { - DBG("handler not found for 0x%02x", avc->opcode); - packet_size += avrcp_handle_vendor_reject(&code, operands); - avc->code = code; - goto done; - } - - code = avc->code; - subunit = avc->subunit_type; - - packet_size += handler->cb(session, avctp->transaction, &code, - &subunit, operands, operand_count, - handler->user_data); - - avc->code = code; - avc->subunit_type = subunit; - -done: - ret = write(sock, buf, packet_size); - if (ret != packet_size) - goto failed; - - return TRUE; - -failed: - DBG("AVCTP session %p got disconnected", session); - avctp_set_state(session, AVCTP_STATE_DISCONNECTED); - return FALSE; -} - -static int uinput_create(char *name) -{ - struct uinput_dev dev; - int fd, err, i; - - fd = open("/dev/uinput", O_RDWR); - if (fd < 0) { - fd = open("/dev/input/uinput", O_RDWR); - if (fd < 0) { - fd = open("/dev/misc/uinput", O_RDWR); - if (fd < 0) { - err = -errno; - error("Can't open input device: %s (%d)", - strerror(-err), -err); - return err; - } - } - } - - memset(&dev, 0, sizeof(dev)); - if (name) - strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1); - - dev.id.bustype = BUS_BLUETOOTH; - dev.id.vendor = 0x0000; - dev.id.product = 0x0000; - dev.id.version = 0x0000; - - if (write(fd, &dev, sizeof(dev)) < 0) { - err = -errno; - error("Can't write device information: %s (%d)", - strerror(-err), -err); - close(fd); - return err; - } - - ioctl(fd, UI_SET_EVBIT, EV_KEY); - ioctl(fd, UI_SET_EVBIT, EV_REL); - ioctl(fd, UI_SET_EVBIT, EV_REP); - ioctl(fd, UI_SET_EVBIT, EV_SYN); - - for (i = 0; key_map[i].name != NULL; i++) - ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput); - - if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) { - err = -errno; - error("Can't create uinput device: %s (%d)", - strerror(-err), -err); - close(fd); - return err; - } - - return fd; -} - -static void init_uinput(struct avctp *session) -{ - struct audio_device *dev; - char address[18], name[248 + 1]; - - dev = manager_get_device(&session->server->src, &session->dst, FALSE); - - device_get_name(dev->btd_dev, name, sizeof(name)); - if (g_str_equal(name, "Nokia CK-20W")) { - session->key_quirks[FORWARD_OP] |= QUIRK_NO_RELEASE; - session->key_quirks[BACKWARD_OP] |= QUIRK_NO_RELEASE; - session->key_quirks[PLAY_OP] |= QUIRK_NO_RELEASE; - session->key_quirks[PAUSE_OP] |= QUIRK_NO_RELEASE; - } - - ba2str(&session->dst, address); - - session->uinput = uinput_create(address); - if (session->uinput < 0) - error("AVRCP: failed to init uinput for %s", address); - else - DBG("AVRCP: uinput initialized for %s", address); -} - -static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data) -{ - struct avctp *session = data; - char address[18]; - uint16_t imtu; - GError *gerr = NULL; - - if (err) { - avctp_set_state(session, AVCTP_STATE_DISCONNECTED); - error("%s", err->message); - return; - } - - bt_io_get(chan, BT_IO_L2CAP, &gerr, - BT_IO_OPT_DEST, &address, - BT_IO_OPT_IMTU, &imtu, - BT_IO_OPT_INVALID); - if (gerr) { - avctp_set_state(session, AVCTP_STATE_DISCONNECTED); - error("%s", gerr->message); - g_error_free(gerr); - return; - } - - DBG("AVCTP: connected to %s", address); - - if (!session->io) - session->io = g_io_channel_ref(chan); - - init_uinput(session); - - avctp_set_state(session, AVCTP_STATE_CONNECTED); - session->mtu = imtu; - session->io_id = g_io_add_watch(chan, - G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) session_cb, session); -} - -static void auth_cb(DBusError *derr, void *user_data) -{ - struct avctp *session = user_data; - GError *err = NULL; - - if (session->io_id) { - g_source_remove(session->io_id); - session->io_id = 0; - } - - if (derr && dbus_error_is_set(derr)) { - error("Access denied: %s", derr->message); - avctp_set_state(session, AVCTP_STATE_DISCONNECTED); - return; - } - - if (!bt_io_accept(session->io, avctp_connect_cb, session, - NULL, &err)) { - error("bt_io_accept: %s", err->message); - g_error_free(err); - avctp_set_state(session, AVCTP_STATE_DISCONNECTED); - } -} - -static struct avctp_server *find_server(GSList *list, const bdaddr_t *src) -{ - for (; list; list = list->next) { - struct avctp_server *server = list->data; - - if (bacmp(&server->src, src) == 0) - return server; - } - - return NULL; -} - -static struct avctp *find_session(GSList *list, const bdaddr_t *dst) -{ - for (; list != NULL; list = g_slist_next(list)) { - struct avctp *s = list->data; - - if (bacmp(dst, &s->dst)) - continue; - - return s; - } - - return NULL; -} - -static struct avctp *avctp_get_internal(const bdaddr_t *src, - const bdaddr_t *dst) -{ - struct avctp_server *server; - struct avctp *session; - - assert(src != NULL); - assert(dst != NULL); - - server = find_server(servers, src); - if (server == NULL) - return NULL; - - session = find_session(server->sessions, dst); - if (session) - return session; - - session = g_new0(struct avctp, 1); - - session->server = server; - bacpy(&session->dst, dst); - session->state = AVCTP_STATE_DISCONNECTED; - - server->sessions = g_slist_append(server->sessions, session); - - return session; -} - -static void avctp_confirm_cb(GIOChannel *chan, gpointer data) -{ - struct avctp *session; - struct audio_device *dev; - char address[18]; - bdaddr_t src, dst; - GError *err = NULL; - - bt_io_get(chan, BT_IO_L2CAP, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_DEST_BDADDR, &dst, - BT_IO_OPT_DEST, address, - BT_IO_OPT_INVALID); - if (err) { - error("%s", err->message); - g_error_free(err); - g_io_channel_shutdown(chan, TRUE, NULL); - return; - } - - DBG("AVCTP: incoming connect from %s", address); - - session = avctp_get_internal(&src, &dst); - if (!session) - goto drop; - - dev = manager_get_device(&src, &dst, FALSE); - if (!dev) { - dev = manager_get_device(&src, &dst, TRUE); - if (!dev) { - error("Unable to get audio device object for %s", - address); - goto drop; - } - } - - if (dev->control == NULL) { - btd_device_add_uuid(dev->btd_dev, AVRCP_REMOTE_UUID); - if (dev->control == NULL) - goto drop; - } - - if (session->io) { - error("Refusing unexpected connect from %s", address); - goto drop; - } - - avctp_set_state(session, AVCTP_STATE_CONNECTING); - session->io = g_io_channel_ref(chan); - - if (audio_device_request_authorization(dev, AVRCP_TARGET_UUID, - auth_cb, session) < 0) - goto drop; - - session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - session_cb, session); - return; - -drop: - if (!session || !session->io) - g_io_channel_shutdown(chan, TRUE, NULL); - if (session) - avctp_set_state(session, AVCTP_STATE_DISCONNECTED); -} - -static GIOChannel *avctp_server_socket(const bdaddr_t *src, gboolean master) -{ - GError *err = NULL; - GIOChannel *io; - - io = bt_io_listen(BT_IO_L2CAP, NULL, avctp_confirm_cb, NULL, - NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, src, - BT_IO_OPT_PSM, AVCTP_PSM, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_MASTER, master, - BT_IO_OPT_INVALID); - if (!io) { - error("%s", err->message); - g_error_free(err); - } - - return io; -} - -static unsigned int passthrough_id = 0; -static unsigned int unit_id = 0; -static unsigned int subunit_id = 0; - -int avctp_register(const bdaddr_t *src, gboolean master) -{ - struct avctp_server *server; - - server = g_new0(struct avctp_server, 1); - if (!server) - return -ENOMEM; - - server->io = avctp_server_socket(src, master); - if (!server->io) { - g_free(server); - return -1; - } - - bacpy(&server->src, src); - - servers = g_slist_append(servers, server); - - if (!passthrough_id) - passthrough_id = avctp_register_pdu_handler(AVC_OP_PASSTHROUGH, - handle_panel_passthrough, NULL); - - if (!unit_id) - unit_id = avctp_register_pdu_handler(AVC_OP_UNITINFO, handle_unit_info, - NULL); - - if (!subunit_id) - subunit_id = avctp_register_pdu_handler(AVC_OP_SUBUNITINFO, - handle_subunit_info, NULL); - - return 0; -} - -void avctp_unregister(const bdaddr_t *src) -{ - struct avctp_server *server; - - server = find_server(servers, src); - if (!server) - return; - - while (server->sessions) - avctp_disconnected(server->sessions->data); - - servers = g_slist_remove(servers, server); - - g_io_channel_shutdown(server->io, TRUE, NULL); - g_io_channel_unref(server->io); - g_free(server); - - if (servers) - return; - - if (passthrough_id) { - avctp_unregister_pdu_handler(passthrough_id); - passthrough_id = 0; - } - - if (unit_id) { - avctp_unregister_pdu_handler(unit_id); - passthrough_id = 0; - } - - if (subunit_id) { - avctp_unregister_pdu_handler(subunit_id); - subunit_id = 0; - } -} - -int avctp_send_passthrough(struct avctp *session, uint8_t op) -{ - unsigned char buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + 2]; - struct avctp_header *avctp = (void *) buf; - struct avc_header *avc = (void *) &buf[AVCTP_HEADER_LENGTH]; - uint8_t *operands = &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH]; - int sk; - - if (session->state != AVCTP_STATE_CONNECTED) - return -ENOTCONN; - - memset(buf, 0, sizeof(buf)); - - avctp->transaction = id++; - avctp->packet_type = AVCTP_PACKET_SINGLE; - avctp->cr = AVCTP_COMMAND; - avctp->pid = htons(AV_REMOTE_SVCLASS_ID); - - avc->code = AVC_CTYPE_CONTROL; - avc->subunit_type = AVC_SUBUNIT_PANEL; - avc->opcode = AVC_OP_PASSTHROUGH; - - operands[0] = op & 0x7f; - operands[1] = 0; - - sk = g_io_channel_unix_get_fd(session->io); - - if (write(sk, buf, sizeof(buf)) < 0) - return -errno; - - /* Button release */ - avctp->transaction = id++; - operands[0] |= 0x80; - - if (write(sk, buf, sizeof(buf)) < 0) - return -errno; - - return 0; -} - -static int avctp_send(struct avctp *session, uint8_t transaction, uint8_t cr, - uint8_t code, uint8_t subunit, uint8_t opcode, - uint8_t *operands, size_t operand_count) -{ - uint8_t *buf; - struct avctp_header *avctp; - struct avc_header *avc; - uint8_t *pdu; - int sk, err = 0; - uint16_t size; - - if (session->state != AVCTP_STATE_CONNECTED) - return -ENOTCONN; - - sk = g_io_channel_unix_get_fd(session->io); - size = AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH + operand_count; - buf = g_malloc0(size); - - avctp = (void *) buf; - avc = (void *) &buf[AVCTP_HEADER_LENGTH]; - pdu = (void *) &buf[AVCTP_HEADER_LENGTH + AVC_HEADER_LENGTH]; - - avctp->transaction = transaction; - avctp->packet_type = AVCTP_PACKET_SINGLE; - avctp->cr = cr; - avctp->pid = htons(AV_REMOTE_SVCLASS_ID); - - avc->code = code; - avc->subunit_type = subunit; - avc->opcode = opcode; - - memcpy(pdu, operands, operand_count); - - if (write(sk, buf, size) < 0) - err = -errno; - - g_free(buf); - return err; -} - -int avctp_send_vendordep(struct avctp *session, uint8_t transaction, - uint8_t code, uint8_t subunit, - uint8_t *operands, size_t operand_count) -{ - return avctp_send(session, transaction, AVCTP_RESPONSE, code, subunit, - AVC_OP_VENDORDEP, operands, operand_count); -} - -int avctp_send_vendordep_req(struct avctp *session, uint8_t code, - uint8_t subunit, uint8_t *operands, - size_t operand_count, - avctp_rsp_cb func, void *user_data) -{ - struct avctp_rsp_handler *handler; - int err; - - err = avctp_send(session, id, AVCTP_COMMAND, code, subunit, - AVC_OP_VENDORDEP, operands, operand_count); - if (err < 0) - return err; - - handler = g_new0(struct avctp_rsp_handler, 1); - handler->id = id; - handler->func = func; - handler->user_data = user_data; - - session->handlers = g_slist_prepend(session->handlers, handler); - - id++; - - return 0; -} - -unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data) -{ - struct avctp_state_callback *state_cb; - static unsigned int id = 0; - - state_cb = g_new(struct avctp_state_callback, 1); - state_cb->cb = cb; - state_cb->user_data = user_data; - state_cb->id = ++id; - - callbacks = g_slist_append(callbacks, state_cb); - - return state_cb->id; -} - -gboolean avctp_remove_state_cb(unsigned int id) -{ - GSList *l; - - for (l = callbacks; l != NULL; l = l->next) { - struct avctp_state_callback *cb = l->data; - if (cb && cb->id == id) { - callbacks = g_slist_remove(callbacks, cb); - g_free(cb); - return TRUE; - } - } - - return FALSE; -} - -unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb, - void *user_data) -{ - struct avctp_pdu_handler *handler; - static unsigned int id = 0; - - handler = find_handler(handlers, opcode); - if (handler) - return 0; - - handler = g_new(struct avctp_pdu_handler, 1); - handler->opcode = opcode; - handler->cb = cb; - handler->user_data = user_data; - handler->id = ++id; - - handlers = g_slist_append(handlers, handler); - - return handler->id; -} - -gboolean avctp_unregister_pdu_handler(unsigned int id) -{ - GSList *l; - - for (l = handlers; l != NULL; l = l->next) { - struct avctp_pdu_handler *handler = l->data; - - if (handler->id == id) { - handlers = g_slist_remove(handlers, handler); - g_free(handler); - return TRUE; - } - } - - return FALSE; -} - -struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst) -{ - struct avctp *session; - GError *err = NULL; - GIOChannel *io; - - session = avctp_get_internal(src, dst); - if (!session) - return NULL; - - if (session->state > AVCTP_STATE_DISCONNECTED) - return session; - - avctp_set_state(session, AVCTP_STATE_CONNECTING); - - io = bt_io_connect(BT_IO_L2CAP, avctp_connect_cb, session, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &session->server->src, - BT_IO_OPT_DEST_BDADDR, &session->dst, - BT_IO_OPT_PSM, AVCTP_PSM, - BT_IO_OPT_INVALID); - if (err) { - avctp_set_state(session, AVCTP_STATE_DISCONNECTED); - error("%s", err->message); - g_error_free(err); - return NULL; - } - - session->io = io; - - return session; -} - -void avctp_disconnect(struct avctp *session) -{ - if (!session->io) - return; - - avctp_set_state(session, AVCTP_STATE_DISCONNECTED); -} - -struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst) -{ - return avctp_get_internal(src, dst); -} diff -Nru bluez-4.101/audio/avctp.h bluez-5.23/audio/avctp.h --- bluez-4.101/audio/avctp.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/avctp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,106 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define AVCTP_PSM 23 - -#define AVC_MTU 512 -#define AVC_HEADER_LENGTH 3 - -/* ctype entries */ -#define AVC_CTYPE_CONTROL 0x0 -#define AVC_CTYPE_STATUS 0x1 -#define AVC_CTYPE_NOTIFY 0x3 -#define AVC_CTYPE_NOT_IMPLEMENTED 0x8 -#define AVC_CTYPE_ACCEPTED 0x9 -#define AVC_CTYPE_REJECTED 0xA -#define AVC_CTYPE_STABLE 0xC -#define AVC_CTYPE_CHANGED 0xD -#define AVC_CTYPE_INTERIM 0xF - -/* opcodes */ -#define AVC_OP_VENDORDEP 0x00 -#define AVC_OP_UNITINFO 0x30 -#define AVC_OP_SUBUNITINFO 0x31 -#define AVC_OP_PASSTHROUGH 0x7c - -/* subunits of interest */ -#define AVC_SUBUNIT_PANEL 0x09 - -/* operands in passthrough commands */ -#define VOL_UP_OP 0x41 -#define VOL_DOWN_OP 0x42 -#define MUTE_OP 0x43 -#define PLAY_OP 0x44 -#define STAVC_OP_OP 0x45 -#define PAUSE_OP 0x46 -#define RECORD_OP 0x47 -#define REWIND_OP 0x48 -#define FAST_FORWARD_OP 0x49 -#define EJECT_OP 0x4a -#define FORWARD_OP 0x4b -#define BACKWARD_OP 0x4c - -struct avctp; - -typedef enum { - AVCTP_STATE_DISCONNECTED = 0, - AVCTP_STATE_CONNECTING, - AVCTP_STATE_CONNECTED -} avctp_state_t; - -typedef void (*avctp_state_cb) (struct audio_device *dev, - avctp_state_t old_state, - avctp_state_t new_state, - void *user_data); - -typedef size_t (*avctp_pdu_cb) (struct avctp *session, uint8_t transaction, - uint8_t *code, uint8_t *subunit, - uint8_t *operands, size_t operand_count, - void *user_data); -typedef gboolean (*avctp_rsp_cb) (struct avctp *session, uint8_t code, - uint8_t subunit, uint8_t *operands, - size_t operand_count, void *user_data); - -unsigned int avctp_add_state_cb(avctp_state_cb cb, void *user_data); -gboolean avctp_remove_state_cb(unsigned int id); - -int avctp_register(const bdaddr_t *src, gboolean master); -void avctp_unregister(const bdaddr_t *src); - -struct avctp *avctp_connect(const bdaddr_t *src, const bdaddr_t *dst); -struct avctp *avctp_get(const bdaddr_t *src, const bdaddr_t *dst); -void avctp_disconnect(struct avctp *session); - -unsigned int avctp_register_pdu_handler(uint8_t opcode, avctp_pdu_cb cb, - void *user_data); -gboolean avctp_unregister_pdu_handler(unsigned int id); - -int avctp_send_passthrough(struct avctp *session, uint8_t op); -int avctp_send_vendordep(struct avctp *session, uint8_t transaction, - uint8_t code, uint8_t subunit, - uint8_t *operands, size_t operand_count); -int avctp_send_vendordep_req(struct avctp *session, uint8_t code, - uint8_t subunit, uint8_t *operands, - size_t operand_count, - avctp_rsp_cb func, void *user_data); diff -Nru bluez-4.101/audio/avdtp.c bluez-5.23/audio/avdtp.c --- bluez-4.101/audio/avdtp.c 2012-06-22 16:36:49.000000000 +0000 +++ bluez-5.23/audio/avdtp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,4055 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "log.h" - -#include "../src/adapter.h" -#include "../src/manager.h" -#include "../src/device.h" - -#include "device.h" -#include "manager.h" -#include "control.h" -#include "avdtp.h" -#include "btio.h" -#include "sink.h" -#include "source.h" - -#define AVDTP_PSM 25 - -#define MAX_SEID 0x3E - -#ifndef MAX -# define MAX(x, y) ((x) > (y) ? (x) : (y)) -#endif - -#define AVDTP_DISCOVER 0x01 -#define AVDTP_GET_CAPABILITIES 0x02 -#define AVDTP_SET_CONFIGURATION 0x03 -#define AVDTP_GET_CONFIGURATION 0x04 -#define AVDTP_RECONFIGURE 0x05 -#define AVDTP_OPEN 0x06 -#define AVDTP_START 0x07 -#define AVDTP_CLOSE 0x08 -#define AVDTP_SUSPEND 0x09 -#define AVDTP_ABORT 0x0A -#define AVDTP_SECURITY_CONTROL 0x0B -#define AVDTP_GET_ALL_CAPABILITIES 0x0C -#define AVDTP_DELAY_REPORT 0x0D - -#define AVDTP_PKT_TYPE_SINGLE 0x00 -#define AVDTP_PKT_TYPE_START 0x01 -#define AVDTP_PKT_TYPE_CONTINUE 0x02 -#define AVDTP_PKT_TYPE_END 0x03 - -#define AVDTP_MSG_TYPE_COMMAND 0x00 -#define AVDTP_MSG_TYPE_GEN_REJECT 0x01 -#define AVDTP_MSG_TYPE_ACCEPT 0x02 -#define AVDTP_MSG_TYPE_REJECT 0x03 - -#define REQ_TIMEOUT 6 -#define ABORT_TIMEOUT 2 -#define DISCONNECT_TIMEOUT 1 -#define STREAM_TIMEOUT 20 - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct avdtp_common_header { - uint8_t message_type:2; - uint8_t packet_type:2; - uint8_t transaction:4; -} __attribute__ ((packed)); - -struct avdtp_single_header { - uint8_t message_type:2; - uint8_t packet_type:2; - uint8_t transaction:4; - uint8_t signal_id:6; - uint8_t rfa0:2; -} __attribute__ ((packed)); - -struct avdtp_start_header { - uint8_t message_type:2; - uint8_t packet_type:2; - uint8_t transaction:4; - uint8_t no_of_packets; - uint8_t signal_id:6; - uint8_t rfa0:2; -} __attribute__ ((packed)); - -struct avdtp_continue_header { - uint8_t message_type:2; - uint8_t packet_type:2; - uint8_t transaction:4; -} __attribute__ ((packed)); - -struct seid_info { - uint8_t rfa0:1; - uint8_t inuse:1; - uint8_t seid:6; - uint8_t rfa2:3; - uint8_t type:1; - uint8_t media_type:4; -} __attribute__ ((packed)); - -struct seid { - uint8_t rfa0:2; - uint8_t seid:6; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct avdtp_common_header { - uint8_t transaction:4; - uint8_t packet_type:2; - uint8_t message_type:2; -} __attribute__ ((packed)); - -struct avdtp_single_header { - uint8_t transaction:4; - uint8_t packet_type:2; - uint8_t message_type:2; - uint8_t rfa0:2; - uint8_t signal_id:6; -} __attribute__ ((packed)); - -struct avdtp_start_header { - uint8_t transaction:4; - uint8_t packet_type:2; - uint8_t message_type:2; - uint8_t no_of_packets; - uint8_t rfa0:2; - uint8_t signal_id:6; -} __attribute__ ((packed)); - -struct avdtp_continue_header { - uint8_t transaction:4; - uint8_t packet_type:2; - uint8_t message_type:2; -} __attribute__ ((packed)); - -struct seid_info { - uint8_t seid:6; - uint8_t inuse:1; - uint8_t rfa0:1; - uint8_t media_type:4; - uint8_t type:1; - uint8_t rfa2:3; -} __attribute__ ((packed)); - -struct seid { - uint8_t seid:6; - uint8_t rfa0:2; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif - -/* packets */ - -struct discover_resp { - struct seid_info seps[0]; -} __attribute__ ((packed)); - -struct getcap_resp { - uint8_t caps[0]; -} __attribute__ ((packed)); - -struct start_req { - struct seid first_seid; - struct seid other_seids[0]; -} __attribute__ ((packed)); - -struct suspend_req { - struct seid first_seid; - struct seid other_seids[0]; -} __attribute__ ((packed)); - -struct seid_rej { - uint8_t error; -} __attribute__ ((packed)); - -struct conf_rej { - uint8_t category; - uint8_t error; -} __attribute__ ((packed)); - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct seid_req { - uint8_t rfa0:2; - uint8_t acp_seid:6; -} __attribute__ ((packed)); - -struct setconf_req { - uint8_t rfa0:2; - uint8_t acp_seid:6; - uint8_t rfa1:2; - uint8_t int_seid:6; - - uint8_t caps[0]; -} __attribute__ ((packed)); - -struct stream_rej { - uint8_t rfa0:2; - uint8_t acp_seid:6; - uint8_t error; -} __attribute__ ((packed)); - -struct reconf_req { - uint8_t rfa0:2; - uint8_t acp_seid:6; - - uint8_t serv_cap; - uint8_t serv_cap_len; - - uint8_t caps[0]; -} __attribute__ ((packed)); - -struct delay_req { - uint8_t rfa0:2; - uint8_t acp_seid:6; - uint16_t delay; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct seid_req { - uint8_t acp_seid:6; - uint8_t rfa0:2; -} __attribute__ ((packed)); - -struct setconf_req { - uint8_t acp_seid:6; - uint8_t rfa0:2; - uint8_t int_seid:6; - uint8_t rfa1:2; - - uint8_t caps[0]; -} __attribute__ ((packed)); - -struct stream_rej { - uint8_t acp_seid:6; - uint8_t rfa0:2; - uint8_t error; -} __attribute__ ((packed)); - -struct reconf_req { - uint8_t acp_seid:6; - uint8_t rfa0:2; - - uint8_t serv_cap; - uint8_t serv_cap_len; - - uint8_t caps[0]; -} __attribute__ ((packed)); - -struct delay_req { - uint8_t acp_seid:6; - uint8_t rfa0:2; - uint16_t delay; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif - -struct in_buf { - gboolean active; - int no_of_packets; - uint8_t transaction; - uint8_t message_type; - uint8_t signal_id; - uint8_t buf[1024]; - uint8_t data_size; -}; - -struct pending_req { - uint8_t transaction; - uint8_t signal_id; - void *data; - size_t data_size; - struct avdtp_stream *stream; /* Set if the request targeted a stream */ - guint timeout; - gboolean collided; -}; - -struct avdtp_remote_sep { - uint8_t seid; - uint8_t type; - uint8_t media_type; - struct avdtp_service_capability *codec; - gboolean delay_reporting; - GSList *caps; /* of type struct avdtp_service_capability */ - struct avdtp_stream *stream; -}; - -struct avdtp_server { - bdaddr_t src; - uint16_t version; - GIOChannel *io; - GSList *seps; - GSList *sessions; -}; - -struct avdtp_local_sep { - avdtp_state_t state; - struct avdtp_stream *stream; - struct seid_info info; - uint8_t codec; - gboolean delay_reporting; - GSList *caps; - struct avdtp_sep_ind *ind; - struct avdtp_sep_cfm *cfm; - void *user_data; - struct avdtp_server *server; -}; - -struct stream_callback { - avdtp_stream_state_cb cb; - void *user_data; - unsigned int id; -}; - -struct avdtp_state_callback { - avdtp_session_state_cb cb; - void *user_data; - unsigned int id; -}; - -struct avdtp_stream { - GIOChannel *io; - uint16_t imtu; - uint16_t omtu; - struct avdtp *session; - struct avdtp_local_sep *lsep; - uint8_t rseid; - GSList *caps; - GSList *callbacks; - struct avdtp_service_capability *codec; - guint io_id; /* Transport GSource ID */ - guint timer; /* Waiting for other side to close or open - * the transport channel */ - gboolean open_acp; /* If we are in ACT role for Open */ - gboolean close_int; /* If we are in INT role for Close */ - gboolean abort_int; /* If we are in INT role for Abort */ - guint idle_timer; - gboolean delay_reporting; - uint16_t delay; /* AVDTP 1.3 Delay Reporting feature */ - gboolean starting; /* only valid while sep state == OPEN */ -}; - -/* Structure describing an AVDTP connection between two devices */ - -struct avdtp { - int ref; - int free_lock; - - uint16_t version; - - struct avdtp_server *server; - bdaddr_t dst; - - avdtp_session_state_t state; - - /* True if the session should be automatically disconnected */ - gboolean auto_dc; - - /* True if the entire device is being disconnected */ - gboolean device_disconnect; - - GIOChannel *io; - guint io_id; - - GSList *seps; /* Elements of type struct avdtp_remote_sep * */ - - GSList *streams; /* Elements of type struct avdtp_stream * */ - - GSList *req_queue; /* Elements of type struct pending_req * */ - GSList *prio_queue; /* Same as req_queue but is processed before it */ - - struct avdtp_stream *pending_open; - - uint16_t imtu; - uint16_t omtu; - - struct in_buf in; - - char *buf; - - avdtp_discover_cb_t discov_cb; - void *user_data; - - struct pending_req *req; - - guint dc_timer; - - /* Attempt stream setup instead of disconnecting */ - gboolean stream_setup; - - DBusPendingCall *pending_auth; -}; - -static GSList *servers = NULL; - -static GSList *avdtp_callbacks = NULL; - -static gboolean auto_connect = TRUE; - -static int send_request(struct avdtp *session, gboolean priority, - struct avdtp_stream *stream, uint8_t signal_id, - void *buffer, size_t size); -static gboolean avdtp_parse_resp(struct avdtp *session, - struct avdtp_stream *stream, - uint8_t transaction, uint8_t signal_id, - void *buf, int size); -static gboolean avdtp_parse_rej(struct avdtp *session, - struct avdtp_stream *stream, - uint8_t transaction, uint8_t signal_id, - void *buf, int size); -static int process_queue(struct avdtp *session); -static void connection_lost(struct avdtp *session, int err); -static void avdtp_sep_set_state(struct avdtp *session, - struct avdtp_local_sep *sep, - avdtp_state_t state); -static void auth_cb(DBusError *derr, void *user_data); - -static struct avdtp_server *find_server(GSList *list, const bdaddr_t *src) -{ - for (; list; list = list->next) { - struct avdtp_server *server = list->data; - - if (bacmp(&server->src, src) == 0) - return server; - } - - return NULL; -} - -static const char *avdtp_statestr(avdtp_state_t state) -{ - switch (state) { - case AVDTP_STATE_IDLE: - return "IDLE"; - case AVDTP_STATE_CONFIGURED: - return "CONFIGURED"; - case AVDTP_STATE_OPEN: - return "OPEN"; - case AVDTP_STATE_STREAMING: - return "STREAMING"; - case AVDTP_STATE_CLOSING: - return "CLOSING"; - case AVDTP_STATE_ABORTING: - return "ABORTING"; - default: - return ""; - } -} - -static gboolean try_send(int sk, void *data, size_t len) -{ - int err; - - do { - err = send(sk, data, len, 0); - } while (err < 0 && errno == EINTR); - - if (err < 0) { - error("send: %s (%d)", strerror(errno), errno); - return FALSE; - } else if ((size_t) err != len) { - error("try_send: complete buffer not sent (%d/%zu bytes)", - err, len); - return FALSE; - } - - return TRUE; -} - -static gboolean avdtp_send(struct avdtp *session, uint8_t transaction, - uint8_t message_type, uint8_t signal_id, - void *data, size_t len) -{ - unsigned int cont_fragments, sent; - struct avdtp_start_header start; - struct avdtp_continue_header cont; - int sock; - - if (session->io == NULL) { - error("avdtp_send: session is closed"); - return FALSE; - } - - sock = g_io_channel_unix_get_fd(session->io); - - /* Single packet - no fragmentation */ - if (sizeof(struct avdtp_single_header) + len <= session->omtu) { - struct avdtp_single_header single; - - memset(&single, 0, sizeof(single)); - - single.transaction = transaction; - single.packet_type = AVDTP_PKT_TYPE_SINGLE; - single.message_type = message_type; - single.signal_id = signal_id; - - memcpy(session->buf, &single, sizeof(single)); - memcpy(session->buf + sizeof(single), data, len); - - return try_send(sock, session->buf, sizeof(single) + len); - } - - /* Check if there is enough space to start packet */ - if (session->omtu < sizeof(start)) { - error("No enough space to fragment packet"); - return FALSE; - } - - /* Count the number of needed fragments */ - cont_fragments = (len - (session->omtu - sizeof(start))) / - (session->omtu - sizeof(cont)) + 1; - - DBG("%zu bytes split into %d fragments", len, cont_fragments + 1); - - /* Send the start packet */ - memset(&start, 0, sizeof(start)); - start.transaction = transaction; - start.packet_type = AVDTP_PKT_TYPE_START; - start.message_type = message_type; - start.no_of_packets = cont_fragments + 1; - start.signal_id = signal_id; - - memcpy(session->buf, &start, sizeof(start)); - memcpy(session->buf + sizeof(start), data, - session->omtu - sizeof(start)); - - if (!try_send(sock, session->buf, session->omtu)) - return FALSE; - - DBG("first packet with %zu bytes sent", session->omtu - sizeof(start)); - - sent = session->omtu - sizeof(start); - - /* Send the continue fragments and the end packet */ - while (sent < len) { - int left, to_copy; - - left = len - sent; - if (left + sizeof(cont) > session->omtu) { - cont.packet_type = AVDTP_PKT_TYPE_CONTINUE; - to_copy = session->omtu - sizeof(cont); - DBG("sending continue with %d bytes", to_copy); - } else { - cont.packet_type = AVDTP_PKT_TYPE_END; - to_copy = left; - DBG("sending end with %d bytes", to_copy); - } - - cont.transaction = transaction; - cont.message_type = message_type; - - memcpy(session->buf, &cont, sizeof(cont)); - memcpy(session->buf + sizeof(cont), data + sent, to_copy); - - if (!try_send(sock, session->buf, to_copy + sizeof(cont))) - return FALSE; - - sent += to_copy; - } - - return TRUE; -} - -static void pending_req_free(struct pending_req *req) -{ - if (req->timeout) - g_source_remove(req->timeout); - g_free(req->data); - g_free(req); -} - -static void close_stream(struct avdtp_stream *stream) -{ - int sock; - - if (stream->io == NULL) - return; - - sock = g_io_channel_unix_get_fd(stream->io); - - shutdown(sock, SHUT_RDWR); - - g_io_channel_shutdown(stream->io, FALSE, NULL); - - g_io_channel_unref(stream->io); - stream->io = NULL; -} - -static gboolean stream_close_timeout(gpointer user_data) -{ - struct avdtp_stream *stream = user_data; - - DBG("Timed out waiting for peer to close the transport channel"); - - stream->timer = 0; - - close_stream(stream); - - return FALSE; -} - -static gboolean stream_open_timeout(gpointer user_data) -{ - struct avdtp_stream *stream = user_data; - - DBG("Timed out waiting for peer to open the transport channel"); - - stream->timer = 0; - - stream->session->pending_open = NULL; - - avdtp_abort(stream->session, stream); - - return FALSE; -} - -static gboolean disconnect_timeout(gpointer user_data) -{ - struct avdtp *session = user_data; - struct audio_device *dev; - gboolean stream_setup; - - session->dc_timer = 0; - stream_setup = session->stream_setup; - session->stream_setup = FALSE; - - dev = manager_get_device(&session->server->src, &session->dst, FALSE); - - if (dev && dev->sink && stream_setup) - sink_setup_stream(dev->sink, session); - else if (dev && dev->source && stream_setup) - source_setup_stream(dev->source, session); - else - connection_lost(session, ETIMEDOUT); - - return FALSE; -} - -static void remove_disconnect_timer(struct avdtp *session) -{ - g_source_remove(session->dc_timer); - session->dc_timer = 0; - session->stream_setup = FALSE; -} - -static void set_disconnect_timer(struct avdtp *session) -{ - if (session->dc_timer) - remove_disconnect_timer(session); - - if (session->device_disconnect) { - session->dc_timer = g_idle_add(disconnect_timeout, session); - return; - } - - session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT, - disconnect_timeout, - session); -} - -void avdtp_error_init(struct avdtp_error *err, uint8_t category, int id) -{ - err->category = category; - - if (category == AVDTP_ERRNO) - err->err.posix_errno = id; - else - err->err.error_code = id; -} - -uint8_t avdtp_error_category(struct avdtp_error *err) -{ - return err->category; -} - -int avdtp_error_error_code(struct avdtp_error *err) -{ - assert(err->category != AVDTP_ERRNO); - return err->err.error_code; -} - -int avdtp_error_posix_errno(struct avdtp_error *err) -{ - assert(err->category == AVDTP_ERRNO); - return err->err.posix_errno; -} - -static struct avdtp_stream *find_stream_by_rseid(struct avdtp *session, - uint8_t rseid) -{ - GSList *l; - - for (l = session->streams; l != NULL; l = g_slist_next(l)) { - struct avdtp_stream *stream = l->data; - - if (stream->rseid == rseid) - return stream; - } - - return NULL; -} - -static struct avdtp_remote_sep *find_remote_sep(GSList *seps, uint8_t seid) -{ - GSList *l; - - for (l = seps; l != NULL; l = g_slist_next(l)) { - struct avdtp_remote_sep *sep = l->data; - - if (sep->seid == seid) - return sep; - } - - return NULL; -} - -static void avdtp_set_state(struct avdtp *session, - avdtp_session_state_t new_state) -{ - GSList *l; - struct audio_device *dev; - bdaddr_t src, dst; - avdtp_session_state_t old_state = session->state; - - session->state = new_state; - - avdtp_get_peers(session, &src, &dst); - dev = manager_get_device(&src, &dst, FALSE); - if (dev == NULL) { - error("avdtp_set_state(): no matching audio device"); - return; - } - - for (l = avdtp_callbacks; l != NULL; l = l->next) { - struct avdtp_state_callback *cb = l->data; - cb->cb(dev, session, old_state, new_state, cb->user_data); - } -} - -static void stream_free(struct avdtp_stream *stream) -{ - struct avdtp_remote_sep *rsep; - - stream->lsep->info.inuse = 0; - stream->lsep->stream = NULL; - - rsep = find_remote_sep(stream->session->seps, stream->rseid); - if (rsep) - rsep->stream = NULL; - - if (stream->timer) - g_source_remove(stream->timer); - - if (stream->io) - close_stream(stream); - - if (stream->io_id) - g_source_remove(stream->io_id); - - g_slist_free_full(stream->callbacks, g_free); - g_slist_free_full(stream->caps, g_free); - - g_free(stream); -} - -static gboolean stream_timeout(gpointer user_data) -{ - struct avdtp_stream *stream = user_data; - struct avdtp *session = stream->session; - - if (avdtp_close(session, stream, FALSE) < 0) - error("stream_timeout: closing AVDTP stream failed"); - - stream->idle_timer = 0; - - return FALSE; -} - -static gboolean transport_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) -{ - struct avdtp_stream *stream = data; - struct avdtp_local_sep *sep = stream->lsep; - - if (stream->close_int && sep->cfm && sep->cfm->close) - sep->cfm->close(stream->session, sep, stream, NULL, - sep->user_data); - - if (!(cond & G_IO_NVAL)) - close_stream(stream); - - stream->io_id = 0; - - if (!stream->abort_int) - avdtp_sep_set_state(stream->session, sep, AVDTP_STATE_IDLE); - - return FALSE; -} - -static int get_send_buffer_size(int sk) -{ - int size; - socklen_t optlen = sizeof(size); - - if (getsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, &optlen) < 0) { - int err = -errno; - error("getsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err), - -err); - return err; - } - - /* - * Doubled value is returned by getsockopt since kernel uses that - * space for its own purposes (see man 7 socket, bookkeeping overhead - * for SO_SNDBUF). - */ - return size / 2; -} - -static int set_send_buffer_size(int sk, int size) -{ - socklen_t optlen = sizeof(size); - - if (setsockopt(sk, SOL_SOCKET, SO_SNDBUF, &size, optlen) < 0) { - int err = -errno; - error("setsockopt(SO_SNDBUF) failed: %s (%d)", strerror(-err), - -err); - return err; - } - - return 0; -} - -static void handle_transport_connect(struct avdtp *session, GIOChannel *io, - uint16_t imtu, uint16_t omtu) -{ - struct avdtp_stream *stream = session->pending_open; - struct avdtp_local_sep *sep = stream->lsep; - int sk, buf_size, min_buf_size; - GError *err = NULL; - - session->pending_open = NULL; - - if (stream->timer) { - g_source_remove(stream->timer); - stream->timer = 0; - } - - if (io == NULL) { - if (!stream->open_acp && sep->cfm && sep->cfm->open) { - struct avdtp_error err; - avdtp_error_init(&err, AVDTP_ERRNO, EIO); - sep->cfm->open(session, sep, NULL, &err, - sep->user_data); - } - return; - } - - if (stream->io == NULL) - stream->io = g_io_channel_ref(io); - - stream->omtu = omtu; - stream->imtu = imtu; - - /* Apply special settings only if local SEP is of type SRC */ - if (sep->info.type != AVDTP_SEP_TYPE_SOURCE) - goto proceed; - - bt_io_set(stream->io, BT_IO_L2CAP, &err, - BT_IO_OPT_FLUSHABLE, TRUE, - BT_IO_OPT_INVALID); - if (err != NULL) { - error("Enabling flushable packets failed: %s", err->message); - g_error_free(err); - } else - DBG("Flushable packets enabled"); - - sk = g_io_channel_unix_get_fd(stream->io); - buf_size = get_send_buffer_size(sk); - if (buf_size < 0) - goto proceed; - - DBG("sk %d, omtu %d, send buffer size %d", sk, omtu, buf_size); - min_buf_size = omtu * 2; - if (buf_size < min_buf_size) { - DBG("send buffer size to be increassed to %d", - min_buf_size); - set_send_buffer_size(sk, min_buf_size); - } - -proceed: - if (!stream->open_acp && sep->cfm && sep->cfm->open) - sep->cfm->open(session, sep, stream, NULL, sep->user_data); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); - - stream->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) transport_cb, stream); -} - -static int pending_req_cmp(gconstpointer a, gconstpointer b) -{ - const struct pending_req *req = a; - const struct avdtp_stream *stream = b; - - if (req->stream == stream) - return 0; - - return -1; -} - -static void cleanup_queue(struct avdtp *session, struct avdtp_stream *stream) -{ - GSList *l; - struct pending_req *req; - - while ((l = g_slist_find_custom(session->prio_queue, stream, - pending_req_cmp))) { - req = l->data; - pending_req_free(req); - session->prio_queue = g_slist_remove(session->prio_queue, req); - } - - while ((l = g_slist_find_custom(session->req_queue, stream, - pending_req_cmp))) { - req = l->data; - pending_req_free(req); - session->req_queue = g_slist_remove(session->req_queue, req); - } -} - -static void handle_unanswered_req(struct avdtp *session, - struct avdtp_stream *stream) -{ - struct pending_req *req; - struct avdtp_local_sep *lsep; - struct avdtp_error err; - - if (session->req->signal_id == AVDTP_ABORT) { - /* Avoid freeing the Abort request here */ - DBG("handle_unanswered_req: Abort req, returning"); - session->req->stream = NULL; - return; - } - - req = session->req; - session->req = NULL; - - avdtp_error_init(&err, AVDTP_ERRNO, EIO); - - lsep = stream->lsep; - - switch (req->signal_id) { - case AVDTP_RECONFIGURE: - error("No reply to Reconfigure request"); - if (lsep && lsep->cfm && lsep->cfm->reconfigure) - lsep->cfm->reconfigure(session, lsep, stream, &err, - lsep->user_data); - break; - case AVDTP_OPEN: - error("No reply to Open request"); - if (lsep && lsep->cfm && lsep->cfm->open) - lsep->cfm->open(session, lsep, stream, &err, - lsep->user_data); - break; - case AVDTP_START: - error("No reply to Start request"); - if (lsep && lsep->cfm && lsep->cfm->start) - lsep->cfm->start(session, lsep, stream, &err, - lsep->user_data); - break; - case AVDTP_SUSPEND: - error("No reply to Suspend request"); - if (lsep && lsep->cfm && lsep->cfm->suspend) - lsep->cfm->suspend(session, lsep, stream, &err, - lsep->user_data); - break; - case AVDTP_CLOSE: - error("No reply to Close request"); - if (lsep && lsep->cfm && lsep->cfm->close) - lsep->cfm->close(session, lsep, stream, &err, - lsep->user_data); - break; - case AVDTP_SET_CONFIGURATION: - error("No reply to SetConfiguration request"); - if (lsep && lsep->cfm && lsep->cfm->set_configuration) - lsep->cfm->set_configuration(session, lsep, stream, - &err, lsep->user_data); - } - - pending_req_free(req); -} - -static void avdtp_sep_set_state(struct avdtp *session, - struct avdtp_local_sep *sep, - avdtp_state_t state) -{ - struct avdtp_stream *stream = sep->stream; - avdtp_state_t old_state; - struct avdtp_error err, *err_ptr = NULL; - GSList *l; - - if (!stream) { - error("Error changing sep state: stream not available"); - return; - } - - if (sep->state == state) { - avdtp_error_init(&err, AVDTP_ERRNO, EIO); - DBG("stream state change failed: %s", avdtp_strerror(&err)); - err_ptr = &err; - } else { - err_ptr = NULL; - DBG("stream state changed: %s -> %s", - avdtp_statestr(sep->state), - avdtp_statestr(state)); - } - - old_state = sep->state; - sep->state = state; - - switch (state) { - case AVDTP_STATE_CONFIGURED: - if (sep->info.type == AVDTP_SEP_TYPE_SINK) - avdtp_delay_report(session, stream, stream->delay); - break; - case AVDTP_STATE_OPEN: - stream->starting = FALSE; - if ((old_state > AVDTP_STATE_OPEN && session->auto_dc) || - stream->open_acp) - stream->idle_timer = g_timeout_add_seconds(STREAM_TIMEOUT, - stream_timeout, - stream); - break; - case AVDTP_STATE_STREAMING: - if (stream->idle_timer) { - g_source_remove(stream->idle_timer); - stream->idle_timer = 0; - } - stream->open_acp = FALSE; - break; - case AVDTP_STATE_CLOSING: - case AVDTP_STATE_ABORTING: - if (stream->idle_timer) { - g_source_remove(stream->idle_timer); - stream->idle_timer = 0; - } - break; - case AVDTP_STATE_IDLE: - if (stream->idle_timer) { - g_source_remove(stream->idle_timer); - stream->idle_timer = 0; - } - if (session->pending_open == stream) - handle_transport_connect(session, NULL, 0, 0); - if (session->req && session->req->stream == stream) - handle_unanswered_req(session, stream); - /* Remove pending commands for this stream from the queue */ - cleanup_queue(session, stream); - break; - default: - break; - } - - l = stream->callbacks; - while (l != NULL) { - struct stream_callback *cb = l->data; - l = g_slist_next(l); - cb->cb(stream, old_state, state, err_ptr, cb->user_data); - } - - if (state == AVDTP_STATE_IDLE && - g_slist_find(session->streams, stream)) { - session->streams = g_slist_remove(session->streams, stream); - stream_free(stream); - } -} - -static void finalize_discovery(struct avdtp *session, int err) -{ - struct avdtp_error avdtp_err; - - avdtp_error_init(&avdtp_err, AVDTP_ERRNO, err); - - if (!session->discov_cb) - return; - - session->discov_cb(session, session->seps, - err ? &avdtp_err : NULL, - session->user_data); - - session->discov_cb = NULL; - session->user_data = NULL; -} - -static void release_stream(struct avdtp_stream *stream, struct avdtp *session) -{ - struct avdtp_local_sep *sep = stream->lsep; - - if (sep->cfm && sep->cfm->abort && - (sep->state != AVDTP_STATE_ABORTING || - stream->abort_int)) - sep->cfm->abort(session, sep, stream, NULL, sep->user_data); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); -} - -static int avdtp_cancel_authorization(struct avdtp *session) -{ - struct audio_device *dev; - - if (session->state != AVDTP_SESSION_STATE_CONNECTING) - return 0; - - dev = manager_get_device(&session->server->src, &session->dst, FALSE); - if (dev == NULL) - return -ENODEV; - - return audio_device_cancel_authorization(dev, auth_cb, session); -} - -static void connection_lost(struct avdtp *session, int err) -{ - char address[18]; - - ba2str(&session->dst, address); - DBG("Disconnected from %s", address); - - if (err != EACCES) - avdtp_cancel_authorization(session); - - session->free_lock = 1; - - finalize_discovery(session, err); - - g_slist_foreach(session->streams, (GFunc) release_stream, session); - session->streams = NULL; - - session->free_lock = 0; - - if (session->io) { - g_io_channel_shutdown(session->io, FALSE, NULL); - g_io_channel_unref(session->io); - session->io = NULL; - } - - avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED); - - if (session->io_id) { - g_source_remove(session->io_id); - session->io_id = 0; - } - - if (session->dc_timer) - remove_disconnect_timer(session); - - session->auto_dc = TRUE; - - if (session->ref != 1) - error("connection_lost: ref count not 1 after all callbacks"); - else - avdtp_unref(session); -} - -void avdtp_unref(struct avdtp *session) -{ - struct avdtp_server *server; - - if (!session) - return; - - session->ref--; - - DBG("%p: ref=%d", session, session->ref); - - if (session->ref == 1) { - if (session->state == AVDTP_SESSION_STATE_CONNECTING && - session->io) { - avdtp_cancel_authorization(session); - g_io_channel_shutdown(session->io, TRUE, NULL); - g_io_channel_unref(session->io); - session->io = NULL; - avdtp_set_state(session, - AVDTP_SESSION_STATE_DISCONNECTED); - } - - if (session->io) - set_disconnect_timer(session); - else if (!session->free_lock) /* Drop the local ref if we - aren't connected */ - session->ref--; - } - - if (session->ref > 0) - return; - - server = session->server; - - DBG("%p: freeing session and removing from list", session); - - if (session->dc_timer) - remove_disconnect_timer(session); - - server->sessions = g_slist_remove(server->sessions, session); - - if (session->req) - pending_req_free(session->req); - - g_slist_free_full(session->seps, g_free); - - g_free(session->buf); - - g_free(session); -} - -struct avdtp *avdtp_ref(struct avdtp *session) -{ - session->ref++; - DBG("%p: ref=%d", session, session->ref); - if (session->dc_timer) - remove_disconnect_timer(session); - return session; -} - -static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *server, - uint8_t seid) -{ - GSList *l; - - for (l = server->seps; l != NULL; l = g_slist_next(l)) { - struct avdtp_local_sep *sep = l->data; - - if (sep->info.seid == seid) - return sep; - } - - return NULL; -} - -struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, - struct avdtp_local_sep *lsep) -{ - GSList *l; - - if (lsep->info.inuse) - return NULL; - - for (l = session->seps; l != NULL; l = g_slist_next(l)) { - struct avdtp_remote_sep *sep = l->data; - struct avdtp_service_capability *cap; - struct avdtp_media_codec_capability *codec_data; - - /* Type must be different: source <-> sink */ - if (sep->type == lsep->info.type) - continue; - - if (sep->media_type != lsep->info.media_type) - continue; - - if (!sep->codec) - continue; - - cap = sep->codec; - codec_data = (void *) cap->data; - - if (codec_data->media_codec_type != lsep->codec) - continue; - - if (sep->stream == NULL) - return sep; - } - - return NULL; -} - -static GSList *caps_to_list(uint8_t *data, int size, - struct avdtp_service_capability **codec, - gboolean *delay_reporting) -{ - GSList *caps; - int processed; - - if (delay_reporting) - *delay_reporting = FALSE; - - for (processed = 0, caps = NULL; processed + 2 <= size;) { - struct avdtp_service_capability *cap; - uint8_t length, category; - - category = data[0]; - length = data[1]; - - if (processed + 2 + length > size) { - error("Invalid capability data in getcap resp"); - break; - } - - cap = g_malloc(sizeof(struct avdtp_service_capability) + - length); - memcpy(cap, data, 2 + length); - - processed += 2 + length; - data += 2 + length; - - caps = g_slist_append(caps, cap); - - if (category == AVDTP_MEDIA_CODEC && - length >= - sizeof(struct avdtp_media_codec_capability)) - *codec = cap; - else if (category == AVDTP_DELAY_REPORTING && delay_reporting) - *delay_reporting = TRUE; - } - - return caps; -} - -static gboolean avdtp_unknown_cmd(struct avdtp *session, uint8_t transaction, - uint8_t signal_id) -{ - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_GEN_REJECT, - signal_id, NULL, 0); -} - -static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction, - void *buf, int size) -{ - GSList *l; - unsigned int rsp_size, sep_count, i; - struct seid_info *seps; - gboolean ret; - - sep_count = g_slist_length(session->server->seps); - - if (sep_count == 0) { - uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND; - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_DISCOVER, &err, sizeof(err)); - } - - rsp_size = sep_count * sizeof(struct seid_info); - - seps = g_new0(struct seid_info, sep_count); - - for (l = session->server->seps, i = 0; l != NULL; l = l->next, i++) { - struct avdtp_local_sep *sep = l->data; - - memcpy(&seps[i], &sep->info, sizeof(struct seid_info)); - } - - ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_DISCOVER, seps, rsp_size); - g_free(seps); - - return ret; -} - -static gboolean avdtp_getcap_cmd(struct avdtp *session, uint8_t transaction, - struct seid_req *req, unsigned int size, - gboolean get_all) -{ - GSList *l, *caps; - struct avdtp_local_sep *sep = NULL; - unsigned int rsp_size; - uint8_t err, buf[1024], *ptr = buf; - uint8_t cmd; - - cmd = get_all ? AVDTP_GET_ALL_CAPABILITIES : AVDTP_GET_CAPABILITIES; - - if (size < sizeof(struct seid_req)) { - err = AVDTP_BAD_LENGTH; - goto failed; - } - - sep = find_local_sep_by_seid(session->server, req->acp_seid); - if (!sep) { - err = AVDTP_BAD_ACP_SEID; - goto failed; - } - - if (get_all && session->server->version < 0x0103) - return avdtp_unknown_cmd(session, transaction, cmd); - - if (!sep->ind->get_capability(session, sep, get_all, &caps, - &err, sep->user_data)) - goto failed; - - for (l = caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { - struct avdtp_service_capability *cap = l->data; - - if (rsp_size + cap->length + 2 > sizeof(buf)) - break; - - memcpy(ptr, cap, cap->length + 2); - rsp_size += cap->length + 2; - ptr += cap->length + 2; - - g_free(cap); - } - - g_slist_free(caps); - - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, cmd, - buf, rsp_size); - -failed: - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, cmd, - &err, sizeof(err)); -} - -static void setconf_cb(struct avdtp *session, struct avdtp_stream *stream, - struct avdtp_error *err) -{ - struct conf_rej rej; - struct avdtp_local_sep *sep; - - if (err != NULL) { - rej.error = AVDTP_UNSUPPORTED_CONFIGURATION; - rej.category = err->err.error_code; - avdtp_send(session, session->in.transaction, - AVDTP_MSG_TYPE_REJECT, AVDTP_SET_CONFIGURATION, - &rej, sizeof(rej)); - return; - } - - if (!avdtp_send(session, session->in.transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_SET_CONFIGURATION, NULL, 0)) { - stream_free(stream); - return; - } - - sep = stream->lsep; - sep->stream = stream; - sep->info.inuse = 1; - session->streams = g_slist_append(session->streams, stream); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); -} - -static gboolean avdtp_setconf_cmd(struct avdtp *session, uint8_t transaction, - struct setconf_req *req, unsigned int size) -{ - struct conf_rej rej; - struct avdtp_local_sep *sep; - struct avdtp_stream *stream; - uint8_t err, category = 0x00; - struct audio_device *dev; - bdaddr_t src, dst; - GSList *l; - - if (size < sizeof(struct setconf_req)) { - error("Too short getcap request"); - return FALSE; - } - - sep = find_local_sep_by_seid(session->server, req->acp_seid); - if (!sep) { - err = AVDTP_BAD_ACP_SEID; - goto failed; - } - - if (sep->stream) { - err = AVDTP_SEP_IN_USE; - goto failed; - } - - avdtp_get_peers(session, &src, &dst); - dev = manager_get_device(&src, &dst, FALSE); - if (!dev) { - error("Unable to get a audio device object"); - err = AVDTP_BAD_STATE; - goto failed; - } - - switch (sep->info.type) { - case AVDTP_SEP_TYPE_SOURCE: - if (!dev->sink) { - btd_device_add_uuid(dev->btd_dev, A2DP_SINK_UUID); - if (!dev->sink) { - error("Unable to get a audio sink object"); - err = AVDTP_BAD_STATE; - goto failed; - } - } - break; - case AVDTP_SEP_TYPE_SINK: - if (!dev->source) { - btd_device_add_uuid(dev->btd_dev, A2DP_SOURCE_UUID); - if (!dev->source) { - error("Unable to get a audio source object"); - err = AVDTP_BAD_STATE; - goto failed; - } - } - break; - } - - stream = g_new0(struct avdtp_stream, 1); - stream->session = session; - stream->lsep = sep; - stream->rseid = req->int_seid; - stream->caps = caps_to_list(req->caps, - size - sizeof(struct setconf_req), - &stream->codec, - &stream->delay_reporting); - - /* Verify that the Media Transport capability's length = 0. Reject otherwise */ - for (l = stream->caps; l != NULL; l = g_slist_next(l)) { - struct avdtp_service_capability *cap = l->data; - - if (cap->category == AVDTP_MEDIA_TRANSPORT && cap->length != 0) { - err = AVDTP_BAD_MEDIA_TRANSPORT_FORMAT; - goto failed_stream; - } - } - - if (stream->delay_reporting && session->version < 0x0103) - session->version = 0x0103; - - if (sep->ind && sep->ind->set_configuration) { - if (!sep->ind->set_configuration(session, sep, stream, - stream->caps, - setconf_cb, - sep->user_data)) { - err = AVDTP_UNSUPPORTED_CONFIGURATION; - category = 0x00; - goto failed_stream; - } - } else { - if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_SET_CONFIGURATION, NULL, 0)) { - stream_free(stream); - return FALSE; - } - - sep->stream = stream; - sep->info.inuse = 1; - session->streams = g_slist_append(session->streams, stream); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); - } - - return TRUE; - -failed_stream: - stream_free(stream); -failed: - rej.error = err; - rej.category = category; - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_SET_CONFIGURATION, &rej, sizeof(rej)); -} - -static gboolean avdtp_getconf_cmd(struct avdtp *session, uint8_t transaction, - struct seid_req *req, int size) -{ - GSList *l; - struct avdtp_local_sep *sep = NULL; - int rsp_size; - uint8_t err; - uint8_t buf[1024]; - uint8_t *ptr = buf; - - if (size < (int) sizeof(struct seid_req)) { - error("Too short getconf request"); - return FALSE; - } - - memset(buf, 0, sizeof(buf)); - - sep = find_local_sep_by_seid(session->server, req->acp_seid); - if (!sep) { - err = AVDTP_BAD_ACP_SEID; - goto failed; - } - if (!sep->stream || !sep->stream->caps) { - err = AVDTP_UNSUPPORTED_CONFIGURATION; - goto failed; - } - - for (l = sep->stream->caps, rsp_size = 0; l != NULL; l = g_slist_next(l)) { - struct avdtp_service_capability *cap = l->data; - - if (rsp_size + cap->length + 2 > (int) sizeof(buf)) - break; - - memcpy(ptr, cap, cap->length + 2); - rsp_size += cap->length + 2; - ptr += cap->length + 2; - } - - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_GET_CONFIGURATION, buf, rsp_size); - -failed: - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_GET_CONFIGURATION, &err, sizeof(err)); -} - -static gboolean avdtp_reconf_cmd(struct avdtp *session, uint8_t transaction, - struct seid_req *req, int size) -{ - struct conf_rej rej; - - rej.error = AVDTP_NOT_SUPPORTED_COMMAND; - rej.category = 0x00; - - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_RECONFIGURE, &rej, sizeof(rej)); -} - -static void check_seid_collision(struct pending_req *req, uint8_t id) -{ - struct seid_req *seid = req->data; - - if (seid->acp_seid == id) - req->collided = TRUE; -} - -static void check_start_collision(struct pending_req *req, uint8_t id) -{ - struct start_req *start = req->data; - struct seid *seid = &start->first_seid; - int count = 1 + req->data_size - sizeof(struct start_req); - int i; - - for (i = 0; i < count; i++, seid++) { - if (seid->seid == id) { - req->collided = TRUE; - return; - } - } -} - -static void check_suspend_collision(struct pending_req *req, uint8_t id) -{ - struct suspend_req *suspend = req->data; - struct seid *seid = &suspend->first_seid; - int count = 1 + req->data_size - sizeof(struct suspend_req); - int i; - - for (i = 0; i < count; i++, seid++) { - if (seid->seid == id) { - req->collided = TRUE; - return; - } - } -} - -static void avdtp_check_collision(struct avdtp *session, uint8_t cmd, - struct avdtp_stream *stream) -{ - struct pending_req *req = session->req; - - if (req == NULL || (req->signal_id != cmd && cmd != AVDTP_ABORT)) - return; - - if (cmd == AVDTP_ABORT) - cmd = req->signal_id; - - switch (cmd) { - case AVDTP_OPEN: - case AVDTP_CLOSE: - check_seid_collision(req, stream->rseid); - break; - case AVDTP_START: - check_start_collision(req, stream->rseid); - break; - case AVDTP_SUSPEND: - check_suspend_collision(req, stream->rseid); - break; - } -} - -static gboolean avdtp_open_cmd(struct avdtp *session, uint8_t transaction, - struct seid_req *req, unsigned int size) -{ - struct avdtp_local_sep *sep; - struct avdtp_stream *stream; - uint8_t err; - - if (size < sizeof(struct seid_req)) { - error("Too short abort request"); - return FALSE; - } - - sep = find_local_sep_by_seid(session->server, req->acp_seid); - if (!sep) { - err = AVDTP_BAD_ACP_SEID; - goto failed; - } - - if (sep->state != AVDTP_STATE_CONFIGURED) { - err = AVDTP_BAD_STATE; - goto failed; - } - - stream = sep->stream; - - if (sep->ind && sep->ind->open) { - if (!sep->ind->open(session, sep, stream, &err, - sep->user_data)) - goto failed; - } - - avdtp_check_collision(session, AVDTP_OPEN, stream); - - if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_OPEN, NULL, 0)) - return FALSE; - - stream->open_acp = TRUE; - session->pending_open = stream; - stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, - stream_open_timeout, - stream); - - return TRUE; - -failed: - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_OPEN, &err, sizeof(err)); -} - -static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction, - struct start_req *req, unsigned int size) -{ - struct avdtp_local_sep *sep; - struct avdtp_stream *stream; - struct stream_rej rej; - struct seid *seid; - uint8_t err, failed_seid; - int seid_count, i; - - if (size < sizeof(struct start_req)) { - error("Too short start request"); - return FALSE; - } - - seid_count = 1 + size - sizeof(struct start_req); - - seid = &req->first_seid; - - for (i = 0; i < seid_count; i++, seid++) { - failed_seid = seid->seid; - - sep = find_local_sep_by_seid(session->server, - req->first_seid.seid); - if (!sep || !sep->stream) { - err = AVDTP_BAD_ACP_SEID; - goto failed; - } - - stream = sep->stream; - - /* Also reject start cmd if state is not open */ - if (sep->state != AVDTP_STATE_OPEN) { - err = AVDTP_BAD_STATE; - goto failed; - } - stream->starting = TRUE; - - if (sep->ind && sep->ind->start) { - if (!sep->ind->start(session, sep, stream, &err, - sep->user_data)) - goto failed; - } - - avdtp_check_collision(session, AVDTP_START, stream); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); - } - - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_START, NULL, 0); - -failed: - DBG("Rejecting (%d)", err); - memset(&rej, 0, sizeof(rej)); - rej.acp_seid = failed_seid; - rej.error = err; - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_START, &rej, sizeof(rej)); -} - -static gboolean avdtp_close_cmd(struct avdtp *session, uint8_t transaction, - struct seid_req *req, unsigned int size) -{ - struct avdtp_local_sep *sep; - struct avdtp_stream *stream; - uint8_t err; - - if (size < sizeof(struct seid_req)) { - error("Too short close request"); - return FALSE; - } - - sep = find_local_sep_by_seid(session->server, req->acp_seid); - if (!sep || !sep->stream) { - err = AVDTP_BAD_ACP_SEID; - goto failed; - } - - if (sep->state != AVDTP_STATE_OPEN && - sep->state != AVDTP_STATE_STREAMING) { - err = AVDTP_BAD_STATE; - goto failed; - } - - stream = sep->stream; - - if (sep->ind && sep->ind->close) { - if (!sep->ind->close(session, sep, stream, &err, - sep->user_data)) - goto failed; - } - - avdtp_check_collision(session, AVDTP_CLOSE, stream); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); - - if (!avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_CLOSE, NULL, 0)) - return FALSE; - - stream->timer = g_timeout_add_seconds(REQ_TIMEOUT, - stream_close_timeout, - stream); - - return TRUE; - -failed: - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_CLOSE, &err, sizeof(err)); -} - -static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction, - struct suspend_req *req, unsigned int size) -{ - struct avdtp_local_sep *sep; - struct avdtp_stream *stream; - struct stream_rej rej; - struct seid *seid; - uint8_t err, failed_seid; - int seid_count, i; - - if (size < sizeof(struct suspend_req)) { - error("Too short suspend request"); - return FALSE; - } - - seid_count = 1 + size - sizeof(struct suspend_req); - - seid = &req->first_seid; - - for (i = 0; i < seid_count; i++, seid++) { - failed_seid = seid->seid; - - sep = find_local_sep_by_seid(session->server, - req->first_seid.seid); - if (!sep || !sep->stream) { - err = AVDTP_BAD_ACP_SEID; - goto failed; - } - - stream = sep->stream; - - if (sep->state != AVDTP_STATE_STREAMING) { - err = AVDTP_BAD_STATE; - goto failed; - } - - if (sep->ind && sep->ind->suspend) { - if (!sep->ind->suspend(session, sep, stream, &err, - sep->user_data)) - goto failed; - } - - avdtp_check_collision(session, AVDTP_SUSPEND, stream); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); - } - - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_SUSPEND, NULL, 0); - -failed: - memset(&rej, 0, sizeof(rej)); - rej.acp_seid = failed_seid; - rej.error = err; - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_SUSPEND, &rej, sizeof(rej)); -} - -static gboolean avdtp_abort_cmd(struct avdtp *session, uint8_t transaction, - struct seid_req *req, unsigned int size) -{ - struct avdtp_local_sep *sep; - uint8_t err; - gboolean ret; - - if (size < sizeof(struct seid_req)) { - error("Too short abort request"); - return FALSE; - } - - sep = find_local_sep_by_seid(session->server, req->acp_seid); - if (!sep || !sep->stream) - return TRUE; - - if (sep->ind && sep->ind->abort) - sep->ind->abort(session, sep, sep->stream, &err, - sep->user_data); - - avdtp_check_collision(session, AVDTP_ABORT, sep->stream); - - ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_ABORT, NULL, 0); - if (ret) - avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); - - return ret; -} - -static gboolean avdtp_secctl_cmd(struct avdtp *session, uint8_t transaction, - struct seid_req *req, int size) -{ - return avdtp_unknown_cmd(session, transaction, AVDTP_SECURITY_CONTROL); -} - -static gboolean avdtp_delayreport_cmd(struct avdtp *session, - uint8_t transaction, - struct delay_req *req, - unsigned int size) -{ - struct avdtp_local_sep *sep; - struct avdtp_stream *stream; - uint8_t err; - - if (size < sizeof(struct delay_req)) { - error("Too short delay report request"); - return FALSE; - } - - sep = find_local_sep_by_seid(session->server, req->acp_seid); - if (!sep || !sep->stream) { - err = AVDTP_BAD_ACP_SEID; - goto failed; - } - - stream = sep->stream; - - if (sep->state != AVDTP_STATE_CONFIGURED && - sep->state != AVDTP_STATE_STREAMING) { - err = AVDTP_BAD_STATE; - goto failed; - } - - stream->delay = ntohs(req->delay); - - if (sep->ind && sep->ind->delayreport) { - if (!sep->ind->delayreport(session, sep, stream->rseid, - stream->delay, &err, - sep->user_data)) - goto failed; - } - - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT, - AVDTP_DELAY_REPORT, NULL, 0); - -failed: - return avdtp_send(session, transaction, AVDTP_MSG_TYPE_REJECT, - AVDTP_DELAY_REPORT, &err, sizeof(err)); -} - -static gboolean avdtp_parse_cmd(struct avdtp *session, uint8_t transaction, - uint8_t signal_id, void *buf, int size) -{ - switch (signal_id) { - case AVDTP_DISCOVER: - DBG("Received DISCOVER_CMD"); - return avdtp_discover_cmd(session, transaction, buf, size); - case AVDTP_GET_CAPABILITIES: - DBG("Received GET_CAPABILITIES_CMD"); - return avdtp_getcap_cmd(session, transaction, buf, size, - FALSE); - case AVDTP_GET_ALL_CAPABILITIES: - DBG("Received GET_ALL_CAPABILITIES_CMD"); - return avdtp_getcap_cmd(session, transaction, buf, size, TRUE); - case AVDTP_SET_CONFIGURATION: - DBG("Received SET_CONFIGURATION_CMD"); - return avdtp_setconf_cmd(session, transaction, buf, size); - case AVDTP_GET_CONFIGURATION: - DBG("Received GET_CONFIGURATION_CMD"); - return avdtp_getconf_cmd(session, transaction, buf, size); - case AVDTP_RECONFIGURE: - DBG("Received RECONFIGURE_CMD"); - return avdtp_reconf_cmd(session, transaction, buf, size); - case AVDTP_OPEN: - DBG("Received OPEN_CMD"); - return avdtp_open_cmd(session, transaction, buf, size); - case AVDTP_START: - DBG("Received START_CMD"); - return avdtp_start_cmd(session, transaction, buf, size); - case AVDTP_CLOSE: - DBG("Received CLOSE_CMD"); - return avdtp_close_cmd(session, transaction, buf, size); - case AVDTP_SUSPEND: - DBG("Received SUSPEND_CMD"); - return avdtp_suspend_cmd(session, transaction, buf, size); - case AVDTP_ABORT: - DBG("Received ABORT_CMD"); - return avdtp_abort_cmd(session, transaction, buf, size); - case AVDTP_SECURITY_CONTROL: - DBG("Received SECURITY_CONTROL_CMD"); - return avdtp_secctl_cmd(session, transaction, buf, size); - case AVDTP_DELAY_REPORT: - DBG("Received DELAY_REPORT_CMD"); - return avdtp_delayreport_cmd(session, transaction, buf, size); - default: - DBG("Received unknown request id %u", signal_id); - return avdtp_unknown_cmd(session, transaction, signal_id); - } -} - -enum avdtp_parse_result { PARSE_ERROR, PARSE_FRAGMENT, PARSE_SUCCESS }; - -static enum avdtp_parse_result avdtp_parse_data(struct avdtp *session, - void *buf, size_t size) -{ - struct avdtp_common_header *header = buf; - struct avdtp_single_header *single = (void *) session->buf; - struct avdtp_start_header *start = (void *) session->buf; - void *payload; - gsize payload_size; - - switch (header->packet_type) { - case AVDTP_PKT_TYPE_SINGLE: - if (size < sizeof(*single)) { - error("Received too small single packet (%zu bytes)", size); - return PARSE_ERROR; - } - if (session->in.active) { - error("SINGLE: Invalid AVDTP packet fragmentation"); - return PARSE_ERROR; - } - - payload = session->buf + sizeof(*single); - payload_size = size - sizeof(*single); - - session->in.active = TRUE; - session->in.data_size = 0; - session->in.no_of_packets = 1; - session->in.transaction = header->transaction; - session->in.message_type = header->message_type; - session->in.signal_id = single->signal_id; - - break; - case AVDTP_PKT_TYPE_START: - if (size < sizeof(*start)) { - error("Received too small start packet (%zu bytes)", size); - return PARSE_ERROR; - } - if (session->in.active) { - error("START: Invalid AVDTP packet fragmentation"); - return PARSE_ERROR; - } - - session->in.active = TRUE; - session->in.data_size = 0; - session->in.transaction = header->transaction; - session->in.message_type = header->message_type; - session->in.no_of_packets = start->no_of_packets; - session->in.signal_id = start->signal_id; - - payload = session->buf + sizeof(*start); - payload_size = size - sizeof(*start); - - break; - case AVDTP_PKT_TYPE_CONTINUE: - if (size < sizeof(struct avdtp_continue_header)) { - error("Received too small continue packet (%zu bytes)", - size); - return PARSE_ERROR; - } - if (!session->in.active) { - error("CONTINUE: Invalid AVDTP packet fragmentation"); - return PARSE_ERROR; - } - if (session->in.transaction != header->transaction) { - error("Continue transaction id doesn't match"); - return PARSE_ERROR; - } - if (session->in.no_of_packets <= 1) { - error("Too few continue packets"); - return PARSE_ERROR; - } - - payload = session->buf + sizeof(struct avdtp_continue_header); - payload_size = size - sizeof(struct avdtp_continue_header); - - break; - case AVDTP_PKT_TYPE_END: - if (size < sizeof(struct avdtp_continue_header)) { - error("Received too small end packet (%zu bytes)", size); - return PARSE_ERROR; - } - if (!session->in.active) { - error("END: Invalid AVDTP packet fragmentation"); - return PARSE_ERROR; - } - if (session->in.transaction != header->transaction) { - error("End transaction id doesn't match"); - return PARSE_ERROR; - } - if (session->in.no_of_packets > 1) { - error("Got an end packet too early"); - return PARSE_ERROR; - } - - payload = session->buf + sizeof(struct avdtp_continue_header); - payload_size = size - sizeof(struct avdtp_continue_header); - - break; - default: - error("Invalid AVDTP packet type 0x%02X", header->packet_type); - return PARSE_ERROR; - } - - if (session->in.data_size + payload_size > - sizeof(session->in.buf)) { - error("Not enough incoming buffer space!"); - return PARSE_ERROR; - } - - memcpy(session->in.buf + session->in.data_size, payload, payload_size); - session->in.data_size += payload_size; - - if (session->in.no_of_packets > 1) { - session->in.no_of_packets--; - DBG("Received AVDTP fragment. %d to go", - session->in.no_of_packets); - return PARSE_FRAGMENT; - } - - session->in.active = FALSE; - - return PARSE_SUCCESS; -} - -static gboolean session_cb(GIOChannel *chan, GIOCondition cond, - gpointer data) -{ - struct avdtp *session = data; - struct avdtp_common_header *header; - ssize_t size; - int fd; - - DBG(""); - - if (cond & G_IO_NVAL) - return FALSE; - - header = (void *) session->buf; - - if (cond & (G_IO_HUP | G_IO_ERR)) - goto failed; - - fd = g_io_channel_unix_get_fd(chan); - size = read(fd, session->buf, session->imtu); - if (size < 0) { - error("IO Channel read error"); - goto failed; - } - - if ((size_t) size < sizeof(struct avdtp_common_header)) { - error("Received too small packet (%zu bytes)", size); - goto failed; - } - - switch (avdtp_parse_data(session, session->buf, size)) { - case PARSE_ERROR: - goto failed; - case PARSE_FRAGMENT: - return TRUE; - case PARSE_SUCCESS: - break; - } - - if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) { - if (!avdtp_parse_cmd(session, session->in.transaction, - session->in.signal_id, - session->in.buf, - session->in.data_size)) { - error("Unable to handle command. Disconnecting"); - goto failed; - } - - if (session->ref == 1 && !session->streams && !session->req) - set_disconnect_timer(session); - - if (session->streams && session->dc_timer) - remove_disconnect_timer(session); - - if (session->req && session->req->collided) { - DBG("Collision detected"); - goto next; - } - - return TRUE; - } - - if (session->req == NULL) { - error("No pending request, ignoring message"); - return TRUE; - } - - if (header->transaction != session->req->transaction) { - error("Transaction label doesn't match"); - return TRUE; - } - - if (session->in.signal_id != session->req->signal_id) { - error("Response signal doesn't match"); - return TRUE; - } - - g_source_remove(session->req->timeout); - session->req->timeout = 0; - - switch (header->message_type) { - case AVDTP_MSG_TYPE_ACCEPT: - if (!avdtp_parse_resp(session, session->req->stream, - session->in.transaction, - session->in.signal_id, - session->in.buf, - session->in.data_size)) { - error("Unable to parse accept response"); - goto failed; - } - break; - case AVDTP_MSG_TYPE_REJECT: - if (!avdtp_parse_rej(session, session->req->stream, - session->in.transaction, - session->in.signal_id, - session->in.buf, - session->in.data_size)) { - error("Unable to parse reject response"); - goto failed; - } - break; - case AVDTP_MSG_TYPE_GEN_REJECT: - error("Received a General Reject message"); - break; - default: - error("Unknown message type 0x%02X", header->message_type); - break; - } - -next: - pending_req_free(session->req); - session->req = NULL; - - process_queue(session); - - return TRUE; - -failed: - connection_lost(session, EIO); - - return FALSE; -} - -static struct avdtp *find_session(GSList *list, const bdaddr_t *dst) -{ - for (; list != NULL; list = g_slist_next(list)) { - struct avdtp *s = list->data; - - if (bacmp(dst, &s->dst)) - continue; - - return s; - } - - return NULL; -} - -static uint16_t get_version(struct avdtp *session) -{ - struct btd_adapter *adapter; - struct btd_device *device; - const sdp_record_t *rec; - sdp_list_t *protos; - sdp_data_t *proto_desc; - char addr[18]; - uint16_t ver = 0x0100; - - adapter = manager_find_adapter(&session->server->src); - if (!adapter) - return ver; - - ba2str(&session->dst, addr); - device = adapter_find_device(adapter, addr); - if (!device) - return ver; - - rec = btd_device_get_record(device, A2DP_SINK_UUID); - if (!rec) - rec = btd_device_get_record(device, A2DP_SOURCE_UUID); - - if (!rec) - return ver; - - if (sdp_get_access_protos(rec, &protos) < 0) - return ver; - - proto_desc = sdp_get_proto_desc(protos, AVDTP_UUID); - if (proto_desc && proto_desc->dtd == SDP_UINT16) - ver = proto_desc->val.uint16; - - sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); - sdp_list_free(protos, NULL); - - return ver; -} - -static struct avdtp *avdtp_get_internal(const bdaddr_t *src, const bdaddr_t *dst) -{ - struct avdtp_server *server; - struct avdtp *session; - - assert(src != NULL); - assert(dst != NULL); - - server = find_server(servers, src); - if (server == NULL) - return NULL; - - session = find_session(server->sessions, dst); - if (session) { - if (session->pending_auth) - return NULL; - else - return session; - } - - session = g_new0(struct avdtp, 1); - - session->server = server; - bacpy(&session->dst, dst); - session->ref = 1; - /* We don't use avdtp_set_state() here since this isn't a state change - * but just setting of the initial state */ - session->state = AVDTP_SESSION_STATE_DISCONNECTED; - session->auto_dc = TRUE; - - session->version = get_version(session); - - server->sessions = g_slist_append(server->sessions, session); - - return session; -} - -struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst) -{ - struct avdtp *session; - - session = avdtp_get_internal(src, dst); - - if (!session) - return NULL; - - return avdtp_ref(session); -} - -static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) -{ - struct avdtp *session = user_data; - char address[18]; - GError *gerr = NULL; - - if (err) { - error("%s", err->message); - goto failed; - } - - if (!session->io) - session->io = g_io_channel_ref(chan); - - bt_io_get(chan, BT_IO_L2CAP, &gerr, - BT_IO_OPT_OMTU, &session->omtu, - BT_IO_OPT_IMTU, &session->imtu, - BT_IO_OPT_INVALID); - if (gerr) { - error("%s", gerr->message); - g_error_free(gerr); - goto failed; - } - - ba2str(&session->dst, address); - DBG("AVDTP: connected %s channel to %s", - session->pending_open ? "transport" : "signaling", - address); - - if (session->state == AVDTP_SESSION_STATE_CONNECTING) { - DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu); - - session->buf = g_malloc0(MAX(session->imtu, session->omtu)); - avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED); - - if (session->io_id) - g_source_remove(session->io_id); - - /* This watch should be low priority since otherwise the - * connect callback might be dispatched before the session - * callback if the kernel wakes us up at the same time for - * them. This could happen if a headset is very quick in - * sending the Start command after connecting the stream - * transport channel. - */ - session->io_id = g_io_add_watch_full(chan, - G_PRIORITY_LOW, - G_IO_IN | G_IO_ERR | G_IO_HUP - | G_IO_NVAL, - (GIOFunc) session_cb, session, - NULL); - - if (session->stream_setup) { - set_disconnect_timer(session); - avdtp_set_auto_disconnect(session, FALSE); - } - } else if (session->pending_open) - handle_transport_connect(session, chan, session->imtu, - session->omtu); - else - goto failed; - - process_queue(session); - - return; - -failed: - if (session->pending_open) { - struct avdtp_stream *stream = session->pending_open; - - handle_transport_connect(session, NULL, 0, 0); - - if (avdtp_abort(session, stream) < 0) - avdtp_sep_set_state(session, stream->lsep, - AVDTP_STATE_IDLE); - } else - connection_lost(session, EIO); -} - -static void auth_cb(DBusError *derr, void *user_data) -{ - struct avdtp *session = user_data; - GError *err = NULL; - - if (derr && dbus_error_is_set(derr)) { - error("Access denied: %s", derr->message); - connection_lost(session, EACCES); - return; - } - - if (!bt_io_accept(session->io, avdtp_connect_cb, session, NULL, - &err)) { - error("bt_io_accept: %s", err->message); - connection_lost(session, EACCES); - g_error_free(err); - return; - } - - /* This is so that avdtp_connect_cb will know to do the right thing - * with respect to the disconnect timer */ - session->stream_setup = TRUE; -} - -static void avdtp_confirm_cb(GIOChannel *chan, gpointer data) -{ - struct avdtp *session; - struct audio_device *dev; - char address[18]; - bdaddr_t src, dst; - int perr; - GError *err = NULL; - - bt_io_get(chan, BT_IO_L2CAP, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_DEST_BDADDR, &dst, - BT_IO_OPT_DEST, address, - BT_IO_OPT_INVALID); - if (err) { - error("%s", err->message); - g_error_free(err); - goto drop; - } - - DBG("AVDTP: incoming connect from %s", address); - - session = avdtp_get_internal(&src, &dst); - if (!session) - goto drop; - - /* This state (ie, session is already *connecting*) happens when the - * device initiates a connect (really a config'd L2CAP channel) even - * though there is a connect we initiated in progress. In sink.c & - * source.c, this state is referred to as XCASE connect:connect. - * Abort the device's channel in favor of our own. - */ - if (session->state == AVDTP_SESSION_STATE_CONNECTING) { - DBG("connect already in progress (XCASE connect:connect)"); - goto drop; - } - - if (session->pending_open && session->pending_open->open_acp) { - if (!bt_io_accept(chan, avdtp_connect_cb, session, NULL, NULL)) - goto drop; - return; - } - - if (session->io) { - error("Refusing unexpected connect from %s", address); - goto drop; - } - - dev = manager_get_device(&src, &dst, FALSE); - if (!dev) { - dev = manager_get_device(&src, &dst, TRUE); - if (!dev) { - error("Unable to get audio device object for %s", - address); - goto drop; - } - btd_device_add_uuid(dev->btd_dev, ADVANCED_AUDIO_UUID); - } - - session->io = g_io_channel_ref(chan); - avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING); - - session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) session_cb, session); - - perr = audio_device_request_authorization(dev, ADVANCED_AUDIO_UUID, - auth_cb, session); - if (perr < 0) { - avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED); - avdtp_unref(session); - goto drop; - } - - dev->auto_connect = auto_connect; - - return; - -drop: - g_io_channel_shutdown(chan, TRUE, NULL); -} - -static GIOChannel *l2cap_connect(struct avdtp *session) -{ - GError *err = NULL; - GIOChannel *io; - - io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session, - NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &session->server->src, - BT_IO_OPT_DEST_BDADDR, &session->dst, - BT_IO_OPT_PSM, AVDTP_PSM, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_INVALID); - if (!io) { - error("%s", err->message); - g_error_free(err); - return NULL; - } - - return io; -} - -static void queue_request(struct avdtp *session, struct pending_req *req, - gboolean priority) -{ - if (priority) - session->prio_queue = g_slist_append(session->prio_queue, req); - else - session->req_queue = g_slist_append(session->req_queue, req); -} - -static uint8_t req_get_seid(struct pending_req *req) -{ - if (req->signal_id == AVDTP_DISCOVER) - return 0; - - return ((struct seid_req *) (req->data))->acp_seid; -} - -static int cancel_request(struct avdtp *session, int err) -{ - struct pending_req *req; - struct seid_req sreq; - struct avdtp_local_sep *lsep; - struct avdtp_stream *stream; - uint8_t seid; - struct avdtp_error averr; - - req = session->req; - session->req = NULL; - - avdtp_error_init(&averr, AVDTP_ERRNO, err); - - seid = req_get_seid(req); - if (seid) - stream = find_stream_by_rseid(session, seid); - else - stream = NULL; - - if (stream) { - stream->abort_int = TRUE; - lsep = stream->lsep; - } else - lsep = NULL; - - switch (req->signal_id) { - case AVDTP_RECONFIGURE: - error("Reconfigure: %s (%d)", strerror(err), err); - if (lsep && lsep->cfm && lsep->cfm->reconfigure) - lsep->cfm->reconfigure(session, lsep, stream, &averr, - lsep->user_data); - break; - case AVDTP_OPEN: - error("Open: %s (%d)", strerror(err), err); - if (lsep && lsep->cfm && lsep->cfm->open) - lsep->cfm->open(session, lsep, stream, &averr, - lsep->user_data); - break; - case AVDTP_START: - error("Start: %s (%d)", strerror(err), err); - if (lsep && lsep->cfm && lsep->cfm->start) { - lsep->cfm->start(session, lsep, stream, &averr, - lsep->user_data); - if (stream) - stream->starting = FALSE; - } - break; - case AVDTP_SUSPEND: - error("Suspend: %s (%d)", strerror(err), err); - if (lsep && lsep->cfm && lsep->cfm->suspend) - lsep->cfm->suspend(session, lsep, stream, &averr, - lsep->user_data); - break; - case AVDTP_CLOSE: - error("Close: %s (%d)", strerror(err), err); - if (lsep && lsep->cfm && lsep->cfm->close) { - lsep->cfm->close(session, lsep, stream, &averr, - lsep->user_data); - if (stream) - stream->close_int = FALSE; - } - break; - case AVDTP_SET_CONFIGURATION: - error("SetConfiguration: %s (%d)", strerror(err), err); - if (lsep && lsep->cfm && lsep->cfm->set_configuration) - lsep->cfm->set_configuration(session, lsep, stream, - &averr, lsep->user_data); - goto failed; - case AVDTP_DISCOVER: - error("Discover: %s (%d)", strerror(err), err); - goto failed; - case AVDTP_GET_CAPABILITIES: - error("GetCapabilities: %s (%d)", strerror(err), err); - goto failed; - case AVDTP_ABORT: - error("Abort: %s (%d)", strerror(err), err); - goto failed; - } - - if (!stream) - goto failed; - - memset(&sreq, 0, sizeof(sreq)); - sreq.acp_seid = seid; - - err = send_request(session, TRUE, stream, AVDTP_ABORT, &sreq, - sizeof(sreq)); - if (err < 0) { - error("Unable to send abort request"); - goto failed; - } - - goto done; - -failed: - connection_lost(session, err); -done: - pending_req_free(req); - return err; -} - -static gboolean request_timeout(gpointer user_data) -{ - struct avdtp *session = user_data; - - cancel_request(session, ETIMEDOUT); - - return FALSE; -} - -static int send_req(struct avdtp *session, gboolean priority, - struct pending_req *req) -{ - static int transaction = 0; - int err; - - if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) { - session->io = l2cap_connect(session); - if (!session->io) { - err = -EIO; - goto failed; - } - avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING); - } - - if (session->state < AVDTP_SESSION_STATE_CONNECTED || - session->req != NULL) { - queue_request(session, req, priority); - return 0; - } - - req->transaction = transaction++; - transaction %= 16; - - /* FIXME: Should we retry to send if the buffer - was not totally sent or in case of EINTR? */ - if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND, - req->signal_id, req->data, req->data_size)) { - err = -EIO; - goto failed; - } - - session->req = req; - - req->timeout = g_timeout_add_seconds(req->signal_id == AVDTP_ABORT ? - ABORT_TIMEOUT : REQ_TIMEOUT, - request_timeout, - session); - return 0; - -failed: - g_free(req->data); - g_free(req); - return err; -} - -static int send_request(struct avdtp *session, gboolean priority, - struct avdtp_stream *stream, uint8_t signal_id, - void *buffer, size_t size) -{ - struct pending_req *req; - - if (stream && stream->abort_int && signal_id != AVDTP_ABORT) { - DBG("Unable to send requests while aborting"); - return -EINVAL; - } - - req = g_new0(struct pending_req, 1); - req->signal_id = signal_id; - req->data = g_malloc(size); - memcpy(req->data, buffer, size); - req->data_size = size; - req->stream = stream; - - return send_req(session, priority, req); -} - -static gboolean avdtp_discover_resp(struct avdtp *session, - struct discover_resp *resp, int size) -{ - int sep_count, i; - uint8_t getcap_cmd; - int ret = 0; - gboolean getcap_pending = FALSE; - - if (session->version >= 0x0103 && session->server->version >= 0x0103) - getcap_cmd = AVDTP_GET_ALL_CAPABILITIES; - else - getcap_cmd = AVDTP_GET_CAPABILITIES; - - sep_count = size / sizeof(struct seid_info); - - for (i = 0; i < sep_count; i++) { - struct avdtp_remote_sep *sep; - struct avdtp_stream *stream; - struct seid_req req; - - DBG("seid %d type %d media %d in use %d", - resp->seps[i].seid, resp->seps[i].type, - resp->seps[i].media_type, resp->seps[i].inuse); - - stream = find_stream_by_rseid(session, resp->seps[i].seid); - - sep = find_remote_sep(session->seps, resp->seps[i].seid); - if (!sep) { - if (resp->seps[i].inuse && !stream) - continue; - sep = g_new0(struct avdtp_remote_sep, 1); - session->seps = g_slist_append(session->seps, sep); - } - - sep->stream = stream; - sep->seid = resp->seps[i].seid; - sep->type = resp->seps[i].type; - sep->media_type = resp->seps[i].media_type; - - memset(&req, 0, sizeof(req)); - req.acp_seid = sep->seid; - - ret = send_request(session, TRUE, NULL, getcap_cmd, - &req, sizeof(req)); - if (ret < 0) - break; - getcap_pending = TRUE; - } - - if (!getcap_pending) - finalize_discovery(session, -ret); - - return TRUE; -} - -static gboolean avdtp_get_capabilities_resp(struct avdtp *session, - struct getcap_resp *resp, - unsigned int size) -{ - struct avdtp_remote_sep *sep; - uint8_t seid; - - /* Check for minimum required packet size includes: - * 1. getcap resp header - * 2. media transport capability (2 bytes) - * 3. media codec capability type + length (2 bytes) - * 4. the actual media codec elements - * */ - if (size < (sizeof(struct getcap_resp) + 4 + - sizeof(struct avdtp_media_codec_capability))) { - error("Too short getcap resp packet"); - return FALSE; - } - - seid = ((struct seid_req *) session->req->data)->acp_seid; - - sep = find_remote_sep(session->seps, seid); - - DBG("seid %d type %d media %d", sep->seid, - sep->type, sep->media_type); - - if (sep->caps) { - g_slist_free_full(sep->caps, g_free); - sep->caps = NULL; - sep->codec = NULL; - sep->delay_reporting = FALSE; - } - - sep->caps = caps_to_list(resp->caps, size - sizeof(struct getcap_resp), - &sep->codec, &sep->delay_reporting); - - return TRUE; -} - -static gboolean avdtp_set_configuration_resp(struct avdtp *session, - struct avdtp_stream *stream, - struct avdtp_single_header *resp, - int size) -{ - struct avdtp_local_sep *sep = stream->lsep; - - if (sep->cfm && sep->cfm->set_configuration) - sep->cfm->set_configuration(session, sep, stream, NULL, - sep->user_data); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED); - - return TRUE; -} - -static gboolean avdtp_reconfigure_resp(struct avdtp *session, - struct avdtp_stream *stream, - struct avdtp_single_header *resp, int size) -{ - return TRUE; -} - -static gboolean avdtp_open_resp(struct avdtp *session, struct avdtp_stream *stream, - struct seid_rej *resp, int size) -{ - struct avdtp_local_sep *sep = stream->lsep; - - stream->io = l2cap_connect(session); - if (!stream->io) { - avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); - return FALSE; - } - - session->pending_open = stream; - - return TRUE; -} - -static gboolean avdtp_start_resp(struct avdtp *session, - struct avdtp_stream *stream, - struct seid_rej *resp, int size) -{ - struct avdtp_local_sep *sep = stream->lsep; - - if (sep->cfm && sep->cfm->start) - sep->cfm->start(session, sep, stream, NULL, sep->user_data); - - /* We might be in STREAMING already if both sides send START_CMD at the - * same time and the one in SNK role doesn't reject it as it should */ - if (sep->state != AVDTP_STATE_STREAMING) - avdtp_sep_set_state(session, sep, AVDTP_STATE_STREAMING); - - return TRUE; -} - -static gboolean avdtp_close_resp(struct avdtp *session, - struct avdtp_stream *stream, - struct seid_rej *resp, int size) -{ - struct avdtp_local_sep *sep = stream->lsep; - - avdtp_sep_set_state(session, sep, AVDTP_STATE_CLOSING); - - close_stream(stream); - - return TRUE; -} - -static gboolean avdtp_suspend_resp(struct avdtp *session, - struct avdtp_stream *stream, - void *data, int size) -{ - struct avdtp_local_sep *sep = stream->lsep; - - avdtp_sep_set_state(session, sep, AVDTP_STATE_OPEN); - - if (sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, NULL, sep->user_data); - - return TRUE; -} - -static gboolean avdtp_abort_resp(struct avdtp *session, - struct avdtp_stream *stream, - struct seid_rej *resp, int size) -{ - struct avdtp_local_sep *sep = stream->lsep; - - avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); - - if (sep->cfm && sep->cfm->abort) - sep->cfm->abort(session, sep, stream, NULL, sep->user_data); - - avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE); - - return TRUE; -} - -static gboolean avdtp_delay_report_resp(struct avdtp *session, - struct avdtp_stream *stream, - void *data, int size) -{ - struct avdtp_local_sep *sep = stream->lsep; - - if (sep->cfm && sep->cfm->delay_report) - sep->cfm->delay_report(session, sep, stream, NULL, sep->user_data); - - return TRUE; -} - -static gboolean avdtp_parse_resp(struct avdtp *session, - struct avdtp_stream *stream, - uint8_t transaction, uint8_t signal_id, - void *buf, int size) -{ - struct pending_req *next; - const char *get_all = ""; - - if (session->prio_queue) - next = session->prio_queue->data; - else if (session->req_queue) - next = session->req_queue->data; - else - next = NULL; - - switch (signal_id) { - case AVDTP_DISCOVER: - DBG("DISCOVER request succeeded"); - return avdtp_discover_resp(session, buf, size); - case AVDTP_GET_ALL_CAPABILITIES: - get_all = "ALL_"; - case AVDTP_GET_CAPABILITIES: - DBG("GET_%sCAPABILITIES request succeeded", get_all); - if (!avdtp_get_capabilities_resp(session, buf, size)) - return FALSE; - if (!(next && (next->signal_id == AVDTP_GET_CAPABILITIES || - next->signal_id == AVDTP_GET_ALL_CAPABILITIES))) - finalize_discovery(session, 0); - return TRUE; - } - - /* The remaining commands require an existing stream so bail out - * here if the stream got unexpectedly disconnected */ - if (!stream) { - DBG("AVDTP: stream was closed while waiting for reply"); - return TRUE; - } - - switch (signal_id) { - case AVDTP_SET_CONFIGURATION: - DBG("SET_CONFIGURATION request succeeded"); - return avdtp_set_configuration_resp(session, stream, - buf, size); - case AVDTP_RECONFIGURE: - DBG("RECONFIGURE request succeeded"); - return avdtp_reconfigure_resp(session, stream, buf, size); - case AVDTP_OPEN: - DBG("OPEN request succeeded"); - return avdtp_open_resp(session, stream, buf, size); - case AVDTP_SUSPEND: - DBG("SUSPEND request succeeded"); - return avdtp_suspend_resp(session, stream, buf, size); - case AVDTP_START: - DBG("START request succeeded"); - return avdtp_start_resp(session, stream, buf, size); - case AVDTP_CLOSE: - DBG("CLOSE request succeeded"); - return avdtp_close_resp(session, stream, buf, size); - case AVDTP_ABORT: - DBG("ABORT request succeeded"); - return avdtp_abort_resp(session, stream, buf, size); - case AVDTP_DELAY_REPORT: - DBG("DELAY_REPORT request succeeded"); - return avdtp_delay_report_resp(session, stream, buf, size); - } - - error("Unknown signal id in accept response: %u", signal_id); - return TRUE; -} - -static gboolean seid_rej_to_err(struct seid_rej *rej, unsigned int size, - struct avdtp_error *err) -{ - if (size < sizeof(struct seid_rej)) { - error("Too small packet for seid_rej"); - return FALSE; - } - - avdtp_error_init(err, 0x00, rej->error); - - return TRUE; -} - -static gboolean conf_rej_to_err(struct conf_rej *rej, unsigned int size, - struct avdtp_error *err) -{ - if (size < sizeof(struct conf_rej)) { - error("Too small packet for conf_rej"); - return FALSE; - } - - avdtp_error_init(err, rej->category, rej->error); - - return TRUE; -} - -static gboolean stream_rej_to_err(struct stream_rej *rej, unsigned int size, - struct avdtp_error *err, - uint8_t *acp_seid) -{ - if (size < sizeof(struct stream_rej)) { - error("Too small packet for stream_rej"); - return FALSE; - } - - avdtp_error_init(err, 0x00, rej->error); - - if (acp_seid) - *acp_seid = rej->acp_seid; - - return TRUE; -} - -static gboolean avdtp_parse_rej(struct avdtp *session, - struct avdtp_stream *stream, - uint8_t transaction, uint8_t signal_id, - void *buf, int size) -{ - struct avdtp_error err; - uint8_t acp_seid; - struct avdtp_local_sep *sep = stream ? stream->lsep : NULL; - - switch (signal_id) { - case AVDTP_DISCOVER: - if (!seid_rej_to_err(buf, size, &err)) - return FALSE; - error("DISCOVER request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - return TRUE; - case AVDTP_GET_CAPABILITIES: - case AVDTP_GET_ALL_CAPABILITIES: - if (!seid_rej_to_err(buf, size, &err)) - return FALSE; - error("GET_CAPABILITIES request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - return TRUE; - case AVDTP_OPEN: - if (!seid_rej_to_err(buf, size, &err)) - return FALSE; - error("OPEN request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - if (sep && sep->cfm && sep->cfm->open) - sep->cfm->open(session, sep, stream, &err, - sep->user_data); - return TRUE; - case AVDTP_SET_CONFIGURATION: - if (!conf_rej_to_err(buf, size, &err)) - return FALSE; - error("SET_CONFIGURATION request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - if (sep && sep->cfm && sep->cfm->set_configuration) - sep->cfm->set_configuration(session, sep, stream, - &err, sep->user_data); - return TRUE; - case AVDTP_RECONFIGURE: - if (!conf_rej_to_err(buf, size, &err)) - return FALSE; - error("RECONFIGURE request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - if (sep && sep->cfm && sep->cfm->reconfigure) - sep->cfm->reconfigure(session, sep, stream, &err, - sep->user_data); - return TRUE; - case AVDTP_START: - if (!stream_rej_to_err(buf, size, &err, &acp_seid)) - return FALSE; - error("START request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - if (sep && sep->cfm && sep->cfm->start) { - sep->cfm->start(session, sep, stream, &err, - sep->user_data); - stream->starting = FALSE; - } - return TRUE; - case AVDTP_SUSPEND: - if (!stream_rej_to_err(buf, size, &err, &acp_seid)) - return FALSE; - error("SUSPEND request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - if (sep && sep->cfm && sep->cfm->suspend) - sep->cfm->suspend(session, sep, stream, &err, - sep->user_data); - return TRUE; - case AVDTP_CLOSE: - if (!stream_rej_to_err(buf, size, &err, &acp_seid)) - return FALSE; - error("CLOSE request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - if (sep && sep->cfm && sep->cfm->close) { - sep->cfm->close(session, sep, stream, &err, - sep->user_data); - stream->close_int = FALSE; - } - return TRUE; - case AVDTP_ABORT: - if (!stream_rej_to_err(buf, size, &err, &acp_seid)) - return FALSE; - error("ABORT request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - if (sep && sep->cfm && sep->cfm->abort) - sep->cfm->abort(session, sep, stream, &err, - sep->user_data); - return FALSE; - case AVDTP_DELAY_REPORT: - if (!stream_rej_to_err(buf, size, &err, &acp_seid)) - return FALSE; - error("DELAY_REPORT request rejected: %s (%d)", - avdtp_strerror(&err), err.err.error_code); - if (sep && sep->cfm && sep->cfm->delay_report) - sep->cfm->delay_report(session, sep, stream, &err, - sep->user_data); - return TRUE; - default: - error("Unknown reject response signal id: %u", signal_id); - return TRUE; - } -} - -gboolean avdtp_is_connected(const bdaddr_t *src, const bdaddr_t *dst) -{ - struct avdtp_server *server; - struct avdtp *session; - - server = find_server(servers, src); - if (!server) - return FALSE; - - session = find_session(server->sessions, dst); - if (!session) - return FALSE; - - if (session->state != AVDTP_SESSION_STATE_DISCONNECTED) - return TRUE; - - return FALSE; -} - -struct avdtp_service_capability *avdtp_stream_get_codec( - struct avdtp_stream *stream) -{ - GSList *l; - - for (l = stream->caps; l; l = l->next) { - struct avdtp_service_capability *cap = l->data; - - if (cap->category == AVDTP_MEDIA_CODEC) - return cap; - } - - return NULL; -} - -gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, - struct avdtp_service_capability *cap) -{ - GSList *l; - struct avdtp_service_capability *stream_cap; - - for (l = stream->caps; l; l = g_slist_next(l)) { - stream_cap = l->data; - - if (stream_cap->category != cap->category || - stream_cap->length != cap->length) - continue; - - if (memcmp(stream_cap->data, cap->data, cap->length) == 0) - return TRUE; - } - - return FALSE; -} - -gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, - GSList *caps) -{ - for (; caps; caps = g_slist_next(caps)) { - struct avdtp_service_capability *cap = caps->data; - - if (!avdtp_stream_has_capability(stream, cap)) - return FALSE; - } - - return TRUE; -} - -struct avdtp_remote_sep *avdtp_stream_get_remote_sep( - struct avdtp_stream *stream) -{ - return avdtp_get_remote_sep(stream->session, stream->rseid); -} - -gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, - uint16_t *imtu, uint16_t *omtu, - GSList **caps) -{ - if (stream->io == NULL) - return FALSE; - - if (sock) - *sock = g_io_channel_unix_get_fd(stream->io); - - if (omtu) - *omtu = stream->omtu; - - if (imtu) - *imtu = stream->imtu; - - if (caps) - *caps = stream->caps; - - return TRUE; -} - -static int process_queue(struct avdtp *session) -{ - GSList **queue, *l; - struct pending_req *req; - - if (session->req) - return 0; - - if (session->prio_queue) - queue = &session->prio_queue; - else - queue = &session->req_queue; - - if (!*queue) - return 0; - - l = *queue; - req = l->data; - - *queue = g_slist_remove(*queue, req); - - return send_req(session, FALSE, req); -} - -struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session, - uint8_t seid) -{ - GSList *l; - - for (l = session->seps; l; l = l->next) { - struct avdtp_remote_sep *sep = l->data; - - if (sep->seid == seid) - return sep; - } - - return NULL; -} - -uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep) -{ - return sep->seid; -} - -uint8_t avdtp_get_type(struct avdtp_remote_sep *sep) -{ - return sep->type; -} - -struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep) -{ - return sep->codec; -} - -gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep) -{ - return sep->delay_reporting; -} - -struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep) -{ - return sep->stream; -} - -struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, - void *data, int length) -{ - struct avdtp_service_capability *cap; - - if (category < AVDTP_MEDIA_TRANSPORT || category > AVDTP_DELAY_REPORTING) - return NULL; - - cap = g_malloc(sizeof(struct avdtp_service_capability) + length); - cap->category = category; - cap->length = length; - memcpy(cap->data, data, length); - - return cap; -} - -static gboolean process_discover(gpointer data) -{ - struct avdtp *session = data; - - finalize_discovery(session, 0); - - return FALSE; -} - -int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, - void *user_data) -{ - int err; - - if (session->discov_cb) - return -EBUSY; - - if (session->seps) { - session->discov_cb = cb; - session->user_data = user_data; - g_idle_add(process_discover, session); - return 0; - } - - err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0); - if (err == 0) { - session->discov_cb = cb; - session->user_data = user_data; - } - - return err; -} - -gboolean avdtp_stream_remove_cb(struct avdtp *session, - struct avdtp_stream *stream, - unsigned int id) -{ - GSList *l; - struct stream_callback *cb; - - if (!stream) - return FALSE; - - for (cb = NULL, l = stream->callbacks; l != NULL; l = l->next) { - struct stream_callback *tmp = l->data; - if (tmp && tmp->id == id) { - cb = tmp; - break; - } - } - - if (!cb) - return FALSE; - - stream->callbacks = g_slist_remove(stream->callbacks, cb); - g_free(cb); - - return TRUE; -} - -unsigned int avdtp_stream_add_cb(struct avdtp *session, - struct avdtp_stream *stream, - avdtp_stream_state_cb cb, void *data) -{ - struct stream_callback *stream_cb; - static unsigned int id = 0; - - stream_cb = g_new(struct stream_callback, 1); - stream_cb->cb = cb; - stream_cb->user_data = data; - stream_cb->id = ++id; - - stream->callbacks = g_slist_append(stream->callbacks, stream_cb); - - return stream_cb->id; -} - -int avdtp_get_configuration(struct avdtp *session, struct avdtp_stream *stream) -{ - struct seid_req req; - - if (session->state < AVDTP_SESSION_STATE_CONNECTED) - return -EINVAL; - - memset(&req, 0, sizeof(req)); - req.acp_seid = stream->rseid; - - return send_request(session, FALSE, stream, AVDTP_GET_CONFIGURATION, - &req, sizeof(req)); -} - -static void copy_capabilities(gpointer data, gpointer user_data) -{ - struct avdtp_service_capability *src_cap = data; - struct avdtp_service_capability *dst_cap; - GSList **l = user_data; - - dst_cap = avdtp_service_cap_new(src_cap->category, src_cap->data, - src_cap->length); - - *l = g_slist_append(*l, dst_cap); -} - -int avdtp_set_configuration(struct avdtp *session, - struct avdtp_remote_sep *rsep, - struct avdtp_local_sep *lsep, - GSList *caps, - struct avdtp_stream **stream) -{ - struct setconf_req *req; - struct avdtp_stream *new_stream; - unsigned char *ptr; - int err, caps_len; - struct avdtp_service_capability *cap; - GSList *l; - - if (session->state != AVDTP_SESSION_STATE_CONNECTED) - return -ENOTCONN; - - if (!(lsep && rsep)) - return -EINVAL; - - DBG("%p: int_seid=%u, acp_seid=%u", session, - lsep->info.seid, rsep->seid); - - new_stream = g_new0(struct avdtp_stream, 1); - new_stream->session = session; - new_stream->lsep = lsep; - new_stream->rseid = rsep->seid; - - if (rsep->delay_reporting && lsep->delay_reporting) { - struct avdtp_service_capability *delay_reporting; - - delay_reporting = avdtp_service_cap_new(AVDTP_DELAY_REPORTING, - NULL, 0); - caps = g_slist_append(caps, delay_reporting); - new_stream->delay_reporting = TRUE; - } - - g_slist_foreach(caps, copy_capabilities, &new_stream->caps); - - /* Calculate total size of request */ - for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { - cap = l->data; - caps_len += cap->length + 2; - } - - req = g_malloc0(sizeof(struct setconf_req) + caps_len); - - req->int_seid = lsep->info.seid; - req->acp_seid = rsep->seid; - - /* Copy the capabilities into the request */ - for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { - cap = l->data; - memcpy(ptr, cap, cap->length + 2); - ptr += cap->length + 2; - } - - err = send_request(session, FALSE, new_stream, - AVDTP_SET_CONFIGURATION, req, - sizeof(struct setconf_req) + caps_len); - if (err < 0) - stream_free(new_stream); - else { - lsep->info.inuse = 1; - lsep->stream = new_stream; - rsep->stream = new_stream; - session->streams = g_slist_append(session->streams, new_stream); - if (stream) - *stream = new_stream; - } - - g_free(req); - - return err; -} - -int avdtp_reconfigure(struct avdtp *session, GSList *caps, - struct avdtp_stream *stream) -{ - struct reconf_req *req; - unsigned char *ptr; - int caps_len, err; - GSList *l; - struct avdtp_service_capability *cap; - - if (!g_slist_find(session->streams, stream)) - return -EINVAL; - - if (stream->lsep->state != AVDTP_STATE_OPEN) - return -EINVAL; - - /* Calculate total size of request */ - for (l = caps, caps_len = 0; l != NULL; l = g_slist_next(l)) { - cap = l->data; - caps_len += cap->length + 2; - } - - req = g_malloc0(sizeof(struct reconf_req) + caps_len); - - req->acp_seid = stream->rseid; - - /* Copy the capabilities into the request */ - for (l = caps, ptr = req->caps; l != NULL; l = g_slist_next(l)) { - cap = l->data; - memcpy(ptr, cap, cap->length + 2); - ptr += cap->length + 2; - } - - err = send_request(session, FALSE, stream, AVDTP_RECONFIGURE, req, - sizeof(*req) + caps_len); - g_free(req); - - return err; -} - -int avdtp_open(struct avdtp *session, struct avdtp_stream *stream) -{ - struct seid_req req; - - if (!g_slist_find(session->streams, stream)) - return -EINVAL; - - if (stream->lsep->state > AVDTP_STATE_CONFIGURED) - return -EINVAL; - - memset(&req, 0, sizeof(req)); - req.acp_seid = stream->rseid; - - return send_request(session, FALSE, stream, AVDTP_OPEN, - &req, sizeof(req)); -} - -int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) -{ - struct start_req req; - int ret; - - if (!g_slist_find(session->streams, stream)) - return -EINVAL; - - if (stream->lsep->state != AVDTP_STATE_OPEN) - return -EINVAL; - - /* Recommendation 12: - * If the RD has configured and opened a stream it is also responsible - * to start the streaming via GAVDP_START. - */ - if (stream->open_acp) { - stream->starting = TRUE; - return 0; - } - - if (stream->close_int == TRUE) { - error("avdtp_start: rejecting start since close is initiated"); - return -EINVAL; - } - - if (stream->starting == TRUE) { - DBG("stream already started"); - return -EINPROGRESS; - } - - memset(&req, 0, sizeof(req)); - req.first_seid.seid = stream->rseid; - - ret = send_request(session, FALSE, stream, AVDTP_START, - &req, sizeof(req)); - if (ret == 0) - stream->starting = TRUE; - - return ret; -} - -int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, - gboolean immediate) -{ - struct seid_req req; - int ret; - - if (!g_slist_find(session->streams, stream)) - return -EINVAL; - - if (stream->lsep->state < AVDTP_STATE_OPEN) - return -EINVAL; - - if (stream->close_int == TRUE) { - error("avdtp_close: rejecting since close is already initiated"); - return -EINVAL; - } - - if (immediate && session->req && stream == session->req->stream) - return avdtp_abort(session, stream); - - memset(&req, 0, sizeof(req)); - req.acp_seid = stream->rseid; - - ret = send_request(session, FALSE, stream, AVDTP_CLOSE, - &req, sizeof(req)); - if (ret == 0) - stream->close_int = TRUE; - - return ret; -} - -int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream) -{ - struct seid_req req; - - if (!g_slist_find(session->streams, stream)) - return -EINVAL; - - if (stream->lsep->state <= AVDTP_STATE_OPEN || stream->close_int) - return -EINVAL; - - memset(&req, 0, sizeof(req)); - req.acp_seid = stream->rseid; - - return send_request(session, FALSE, stream, AVDTP_SUSPEND, - &req, sizeof(req)); -} - -int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream) -{ - struct seid_req req; - int ret; - - if (!g_slist_find(session->streams, stream)) - return -EINVAL; - - if (stream->lsep->state == AVDTP_STATE_ABORTING) - return -EINVAL; - - if (session->req && stream == session->req->stream) - return cancel_request(session, ECANCELED); - - memset(&req, 0, sizeof(req)); - req.acp_seid = stream->rseid; - - ret = send_request(session, TRUE, stream, AVDTP_ABORT, - &req, sizeof(req)); - if (ret == 0) - stream->abort_int = TRUE; - - return ret; -} - -int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, - uint16_t delay) -{ - struct delay_req req; - - if (!g_slist_find(session->streams, stream)) - return -EINVAL; - - if (stream->lsep->state != AVDTP_STATE_CONFIGURED && - stream->lsep->state != AVDTP_STATE_STREAMING) - return -EINVAL; - - if (!stream->delay_reporting || session->version < 0x0103 || - session->server->version < 0x0103) - return -EINVAL; - - stream->delay = delay; - - memset(&req, 0, sizeof(req)); - req.acp_seid = stream->rseid; - req.delay = htons(delay); - - return send_request(session, TRUE, stream, AVDTP_DELAY_REPORT, - &req, sizeof(req)); -} - -struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type, - uint8_t media_type, - uint8_t codec_type, - gboolean delay_reporting, - struct avdtp_sep_ind *ind, - struct avdtp_sep_cfm *cfm, - void *user_data) -{ - struct avdtp_server *server; - struct avdtp_local_sep *sep; - - server = find_server(servers, src); - if (!server) - return NULL; - - if (g_slist_length(server->seps) > MAX_SEID) - return NULL; - - sep = g_new0(struct avdtp_local_sep, 1); - - sep->state = AVDTP_STATE_IDLE; - sep->info.seid = g_slist_length(server->seps) + 1; - sep->info.type = type; - sep->info.media_type = media_type; - sep->codec = codec_type; - sep->ind = ind; - sep->cfm = cfm; - sep->user_data = user_data; - sep->server = server; - sep->delay_reporting = TRUE; - - DBG("SEP %p registered: type:%d codec:%d seid:%d", sep, - sep->info.type, sep->codec, sep->info.seid); - server->seps = g_slist_append(server->seps, sep); - - return sep; -} - -int avdtp_unregister_sep(struct avdtp_local_sep *sep) -{ - struct avdtp_server *server; - - if (!sep) - return -EINVAL; - - server = sep->server; - server->seps = g_slist_remove(server->seps, sep); - - if (sep->stream) - release_stream(sep->stream, sep->stream->session); - - DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep, - sep->info.type, sep->codec, sep->info.seid); - - g_free(sep); - - return 0; -} - -static GIOChannel *avdtp_server_socket(const bdaddr_t *src, gboolean master) -{ - GError *err = NULL; - GIOChannel *io; - - io = bt_io_listen(BT_IO_L2CAP, NULL, avdtp_confirm_cb, - NULL, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, src, - BT_IO_OPT_PSM, AVDTP_PSM, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_MASTER, master, - BT_IO_OPT_INVALID); - if (!io) { - error("%s", err->message); - g_error_free(err); - } - - return io; -} - -const char *avdtp_strerror(struct avdtp_error *err) -{ - if (err->category == AVDTP_ERRNO) - return strerror(err->err.posix_errno); - - switch(err->err.error_code) { - case AVDTP_BAD_HEADER_FORMAT: - return "Bad Header Format"; - case AVDTP_BAD_LENGTH: - return "Bad Packet Length"; - case AVDTP_BAD_ACP_SEID: - return "Bad Acceptor SEID"; - case AVDTP_SEP_IN_USE: - return "Stream End Point in Use"; - case AVDTP_SEP_NOT_IN_USE: - return "Stream End Point Not in Use"; - case AVDTP_BAD_SERV_CATEGORY: - return "Bad Service Category"; - case AVDTP_BAD_PAYLOAD_FORMAT: - return "Bad Payload format"; - case AVDTP_NOT_SUPPORTED_COMMAND: - return "Command Not Supported"; - case AVDTP_INVALID_CAPABILITIES: - return "Invalid Capabilities"; - case AVDTP_BAD_RECOVERY_TYPE: - return "Bad Recovery Type"; - case AVDTP_BAD_MEDIA_TRANSPORT_FORMAT: - return "Bad Media Transport Format"; - case AVDTP_BAD_RECOVERY_FORMAT: - return "Bad Recovery Format"; - case AVDTP_BAD_ROHC_FORMAT: - return "Bad Header Compression Format"; - case AVDTP_BAD_CP_FORMAT: - return "Bad Content Protetion Format"; - case AVDTP_BAD_MULTIPLEXING_FORMAT: - return "Bad Multiplexing Format"; - case AVDTP_UNSUPPORTED_CONFIGURATION: - return "Configuration not supported"; - case AVDTP_BAD_STATE: - return "Bad State"; - default: - return "Unknow error"; - } -} - -avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep) -{ - return sep->state; -} - -void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst) -{ - if (src) - bacpy(src, &session->server->src); - if (dst) - bacpy(dst, &session->dst); -} - -int avdtp_init(const bdaddr_t *src, GKeyFile *config, uint16_t *version) -{ - GError *err = NULL; - gboolean tmp, master = TRUE; - struct avdtp_server *server; - uint16_t ver = 0x0102; - - if (!config) - goto proceed; - - tmp = g_key_file_get_boolean(config, "General", - "Master", &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else - master = tmp; - - tmp = g_key_file_get_boolean(config, "General", "AutoConnect", - &err); - if (err) - g_clear_error(&err); - else - auto_connect = tmp; - - if (g_key_file_get_boolean(config, "A2DP", "DelayReporting", NULL)) - ver = 0x0103; - -proceed: - server = g_new0(struct avdtp_server, 1); - if (!server) - return -ENOMEM; - - server->version = ver; - - if (version) - *version = server->version; - - server->io = avdtp_server_socket(src, master); - if (!server->io) { - g_free(server); - return -1; - } - - bacpy(&server->src, src); - - servers = g_slist_append(servers, server); - - return 0; -} - -void avdtp_exit(const bdaddr_t *src) -{ - struct avdtp_server *server; - GSList *l; - - server = find_server(servers, src); - if (!server) - return; - - l = server->sessions; - while (l) { - struct avdtp *session = l->data; - - l = l->next; - /* value of l pointer should be updated before invoking - * connection_lost since it internally uses avdtp_unref - * which operates on server->session list as well - */ - connection_lost(session, -ECONNABORTED); - } - - servers = g_slist_remove(servers, server); - - g_io_channel_shutdown(server->io, TRUE, NULL); - g_io_channel_unref(server->io); - g_free(server); -} - -gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream) -{ - return g_slist_find(session->streams, stream) ? TRUE : FALSE; -} - -void avdtp_set_auto_disconnect(struct avdtp *session, gboolean auto_dc) -{ - session->auto_dc = auto_dc; -} - -gboolean avdtp_stream_setup_active(struct avdtp *session) -{ - return session->stream_setup; -} - -void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc) -{ - session->device_disconnect = dev_dc; -} - -unsigned int avdtp_add_state_cb(avdtp_session_state_cb cb, void *user_data) -{ - struct avdtp_state_callback *state_cb; - static unsigned int id = 0; - - state_cb = g_new(struct avdtp_state_callback, 1); - state_cb->cb = cb; - state_cb->user_data = user_data; - state_cb->id = ++id; - - avdtp_callbacks = g_slist_append(avdtp_callbacks, state_cb); - - return state_cb->id; -} - -gboolean avdtp_remove_state_cb(unsigned int id) -{ - GSList *l; - - for (l = avdtp_callbacks; l != NULL; l = l->next) { - struct avdtp_state_callback *cb = l->data; - if (cb && cb->id == id) { - avdtp_callbacks = g_slist_remove(avdtp_callbacks, cb); - g_free(cb); - return TRUE; - } - } - - return FALSE; -} diff -Nru bluez-4.101/audio/avdtp.h bluez-5.23/audio/avdtp.h --- bluez-4.101/audio/avdtp.h 2012-06-22 16:36:49.000000000 +0000 +++ bluez-5.23/audio/avdtp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,316 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -typedef enum { - AVDTP_SESSION_STATE_DISCONNECTED, - AVDTP_SESSION_STATE_CONNECTING, - AVDTP_SESSION_STATE_CONNECTED -} avdtp_session_state_t; - -struct avdtp; -struct avdtp_stream; -struct avdtp_local_sep; -struct avdtp_remote_sep; -struct avdtp_error { - uint8_t category; - union { - uint8_t error_code; - int posix_errno; - } err; -}; - -/* SEP capability categories */ -#define AVDTP_MEDIA_TRANSPORT 0x01 -#define AVDTP_REPORTING 0x02 -#define AVDTP_RECOVERY 0x03 -#define AVDTP_CONTENT_PROTECTION 0x04 -#define AVDTP_HEADER_COMPRESSION 0x05 -#define AVDTP_MULTIPLEXING 0x06 -#define AVDTP_MEDIA_CODEC 0x07 -#define AVDTP_DELAY_REPORTING 0x08 -#define AVDTP_ERRNO 0xff - -/* AVDTP error definitions */ -#define AVDTP_BAD_HEADER_FORMAT 0x01 -#define AVDTP_BAD_LENGTH 0x11 -#define AVDTP_BAD_ACP_SEID 0x12 -#define AVDTP_SEP_IN_USE 0x13 -#define AVDTP_SEP_NOT_IN_USE 0x14 -#define AVDTP_BAD_SERV_CATEGORY 0x17 -#define AVDTP_BAD_PAYLOAD_FORMAT 0x18 -#define AVDTP_NOT_SUPPORTED_COMMAND 0x19 -#define AVDTP_INVALID_CAPABILITIES 0x1A -#define AVDTP_BAD_RECOVERY_TYPE 0x22 -#define AVDTP_BAD_MEDIA_TRANSPORT_FORMAT 0x23 -#define AVDTP_BAD_RECOVERY_FORMAT 0x25 -#define AVDTP_BAD_ROHC_FORMAT 0x26 -#define AVDTP_BAD_CP_FORMAT 0x27 -#define AVDTP_BAD_MULTIPLEXING_FORMAT 0x28 -#define AVDTP_UNSUPPORTED_CONFIGURATION 0x29 -#define AVDTP_BAD_STATE 0x31 - -/* SEP types definitions */ -#define AVDTP_SEP_TYPE_SOURCE 0x00 -#define AVDTP_SEP_TYPE_SINK 0x01 - -/* Media types definitions */ -#define AVDTP_MEDIA_TYPE_AUDIO 0x00 -#define AVDTP_MEDIA_TYPE_VIDEO 0x01 -#define AVDTP_MEDIA_TYPE_MULTIMEDIA 0x02 - -typedef enum { - AVDTP_STATE_IDLE, - AVDTP_STATE_CONFIGURED, - AVDTP_STATE_OPEN, - AVDTP_STATE_STREAMING, - AVDTP_STATE_CLOSING, - AVDTP_STATE_ABORTING, -} avdtp_state_t; - -struct avdtp_service_capability { - uint8_t category; - uint8_t length; - uint8_t data[0]; -} __attribute__ ((packed)); - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct avdtp_media_codec_capability { - uint8_t rfa0:4; - uint8_t media_type:4; - uint8_t media_codec_type; - uint8_t data[0]; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct avdtp_media_codec_capability { - uint8_t media_type:4; - uint8_t rfa0:4; - uint8_t media_codec_type; - uint8_t data[0]; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif - -typedef void (*avdtp_session_state_cb) (struct audio_device *dev, - struct avdtp *session, - avdtp_session_state_t old_state, - avdtp_session_state_t new_state, - void *user_data); - -typedef void (*avdtp_stream_state_cb) (struct avdtp_stream *stream, - avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, - void *user_data); - -typedef void (*avdtp_set_configuration_cb) (struct avdtp *session, - struct avdtp_stream *stream, - struct avdtp_error *err); - -/* Callbacks for when a reply is received to a command that we sent */ -struct avdtp_sep_cfm { - void (*set_configuration) (struct avdtp *session, - struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - struct avdtp_error *err, - void *user_data); - void (*get_configuration) (struct avdtp *session, - struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - struct avdtp_error *err, - void *user_data); - void (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data); - void (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, struct avdtp_error *err, - void *user_data); - void (*suspend) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data); - void (*close) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data); - void (*abort) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data); - void (*reconfigure) (struct avdtp *session, - struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data); - void (*delay_report) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data); -}; - -/* Callbacks for indicating when we received a new command. The return value - * indicates whether the command should be rejected or accepted */ -struct avdtp_sep_ind { - gboolean (*get_capability) (struct avdtp *session, - struct avdtp_local_sep *sep, - gboolean get_all, - GSList **caps, uint8_t *err, - void *user_data); - gboolean (*set_configuration) (struct avdtp *session, - struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, - GSList *caps, - avdtp_set_configuration_cb cb, - void *user_data); - gboolean (*get_configuration) (struct avdtp *session, - struct avdtp_local_sep *lsep, - uint8_t *err, void *user_data); - gboolean (*open) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data); - gboolean (*start) (struct avdtp *session, struct avdtp_local_sep *lsep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data); - gboolean (*suspend) (struct avdtp *session, - struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data); - gboolean (*close) (struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data); - void (*abort) (struct avdtp *session, struct avdtp_local_sep *sep, - struct avdtp_stream *stream, uint8_t *err, - void *user_data); - gboolean (*reconfigure) (struct avdtp *session, - struct avdtp_local_sep *lsep, - uint8_t *err, void *user_data); - gboolean (*delayreport) (struct avdtp *session, - struct avdtp_local_sep *lsep, - uint8_t rseid, uint16_t delay, - uint8_t *err, void *user_data); -}; - -typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps, - struct avdtp_error *err, void *user_data); - -struct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst); - -void avdtp_unref(struct avdtp *session); -struct avdtp *avdtp_ref(struct avdtp *session); - -gboolean avdtp_is_connected(const bdaddr_t *src, const bdaddr_t *dst); - -struct avdtp_service_capability *avdtp_service_cap_new(uint8_t category, - void *data, int size); - -struct avdtp_remote_sep *avdtp_get_remote_sep(struct avdtp *session, - uint8_t seid); - -uint8_t avdtp_get_seid(struct avdtp_remote_sep *sep); - -uint8_t avdtp_get_type(struct avdtp_remote_sep *sep); - -struct avdtp_service_capability *avdtp_get_codec(struct avdtp_remote_sep *sep); - -gboolean avdtp_get_delay_reporting(struct avdtp_remote_sep *sep); - -struct avdtp_stream *avdtp_get_stream(struct avdtp_remote_sep *sep); - -int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, - void *user_data); - -gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream); - -unsigned int avdtp_stream_add_cb(struct avdtp *session, - struct avdtp_stream *stream, - avdtp_stream_state_cb cb, void *data); -gboolean avdtp_stream_remove_cb(struct avdtp *session, - struct avdtp_stream *stream, - unsigned int id); - -gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock, - uint16_t *imtu, uint16_t *omtu, - GSList **caps); -struct avdtp_service_capability *avdtp_stream_get_codec( - struct avdtp_stream *stream); -gboolean avdtp_stream_has_capability(struct avdtp_stream *stream, - struct avdtp_service_capability *cap); -gboolean avdtp_stream_has_capabilities(struct avdtp_stream *stream, - GSList *caps); -struct avdtp_remote_sep *avdtp_stream_get_remote_sep( - struct avdtp_stream *stream); - -unsigned int avdtp_add_state_cb(avdtp_session_state_cb cb, void *user_data); - -gboolean avdtp_remove_state_cb(unsigned int id); - -int avdtp_set_configuration(struct avdtp *session, - struct avdtp_remote_sep *rsep, - struct avdtp_local_sep *lsep, - GSList *caps, - struct avdtp_stream **stream); - -int avdtp_get_configuration(struct avdtp *session, - struct avdtp_stream *stream); - -int avdtp_open(struct avdtp *session, struct avdtp_stream *stream); -int avdtp_reconfigure(struct avdtp *session, GSList *caps, - struct avdtp_stream *stream); -int avdtp_start(struct avdtp *session, struct avdtp_stream *stream); -int avdtp_suspend(struct avdtp *session, struct avdtp_stream *stream); -int avdtp_close(struct avdtp *session, struct avdtp_stream *stream, - gboolean immediate); -int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream); -int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream, - uint16_t delay); - -struct avdtp_local_sep *avdtp_register_sep(const bdaddr_t *src, uint8_t type, - uint8_t media_type, - uint8_t codec_type, - gboolean delay_reporting, - struct avdtp_sep_ind *ind, - struct avdtp_sep_cfm *cfm, - void *user_data); - -/* Find a matching pair of local and remote SEP ID's */ -struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session, - struct avdtp_local_sep *lsep); - -int avdtp_unregister_sep(struct avdtp_local_sep *sep); - -avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep); - -void avdtp_error_init(struct avdtp_error *err, uint8_t type, int id); -const char *avdtp_strerror(struct avdtp_error *err); -uint8_t avdtp_error_category(struct avdtp_error *err); -int avdtp_error_error_code(struct avdtp_error *err); -int avdtp_error_posix_errno(struct avdtp_error *err); - -void avdtp_get_peers(struct avdtp *session, bdaddr_t *src, bdaddr_t *dst); - -void avdtp_set_auto_disconnect(struct avdtp *session, gboolean auto_dc); -gboolean avdtp_stream_setup_active(struct avdtp *session); -void avdtp_set_device_disconnect(struct avdtp *session, gboolean dev_dc); - -int avdtp_init(const bdaddr_t *src, GKeyFile *config, uint16_t *version); -void avdtp_exit(const bdaddr_t *src); diff -Nru bluez-4.101/audio/avrcp.c bluez-5.23/audio/avrcp.c --- bluez-4.101/audio/avrcp.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/avrcp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1468 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * Copyright (C) 2011 Texas Instruments, Inc. - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include "../src/adapter.h" -#include "../src/device.h" - -#include "log.h" -#include "error.h" -#include "device.h" -#include "manager.h" -#include "avctp.h" -#include "avrcp.h" -#include "sdpd.h" -#include "dbus-common.h" - -/* Company IDs for vendor dependent commands */ -#define IEEEID_BTSIG 0x001958 - -/* Error codes for metadata transfer */ -#define E_INVALID_COMMAND 0x00 -#define E_INVALID_PARAM 0x01 -#define E_PARAM_NOT_FOUND 0x02 -#define E_INTERNAL 0x03 - -/* Packet types */ -#define AVRCP_PACKET_TYPE_SINGLE 0x00 -#define AVRCP_PACKET_TYPE_START 0x01 -#define AVRCP_PACKET_TYPE_CONTINUING 0x02 -#define AVRCP_PACKET_TYPE_END 0x03 - -/* PDU types for metadata transfer */ -#define AVRCP_GET_CAPABILITIES 0x10 -#define AVRCP_LIST_PLAYER_ATTRIBUTES 0X11 -#define AVRCP_LIST_PLAYER_VALUES 0x12 -#define AVRCP_GET_CURRENT_PLAYER_VALUE 0x13 -#define AVRCP_SET_PLAYER_VALUE 0x14 -#define AVRCP_GET_PLAYER_ATTRIBUTE_TEXT 0x15 -#define AVRCP_GET_PLAYER_VALUE_TEXT 0x16 -#define AVRCP_DISPLAYABLE_CHARSET 0x17 -#define AVRCP_CT_BATTERY_STATUS 0x18 -#define AVRCP_GET_ELEMENT_ATTRIBUTES 0x20 -#define AVRCP_GET_PLAY_STATUS 0x30 -#define AVRCP_REGISTER_NOTIFICATION 0x31 -#define AVRCP_REQUEST_CONTINUING 0x40 -#define AVRCP_ABORT_CONTINUING 0x41 -#define AVRCP_SET_ABSOLUTE_VOLUME 0x50 - -/* Capabilities for AVRCP_GET_CAPABILITIES pdu */ -#define CAP_COMPANY_ID 0x02 -#define CAP_EVENTS_SUPPORTED 0x03 - -#define AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH 5 - -#define AVRCP_FEATURE_CATEGORY_1 0x0001 -#define AVRCP_FEATURE_CATEGORY_2 0x0002 -#define AVRCP_FEATURE_CATEGORY_3 0x0004 -#define AVRCP_FEATURE_CATEGORY_4 0x0008 -#define AVRCP_FEATURE_PLAYER_SETTINGS 0x0010 - -enum battery_status { - BATTERY_STATUS_NORMAL = 0, - BATTERY_STATUS_WARNING = 1, - BATTERY_STATUS_CRITICAL = 2, - BATTERY_STATUS_EXTERNAL = 3, - BATTERY_STATUS_FULL_CHARGE = 4, -}; - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct avrcp_header { - uint8_t company_id[3]; - uint8_t pdu_id; - uint8_t packet_type:2; - uint8_t rsvd:6; - uint16_t params_len; - uint8_t params[0]; -} __attribute__ ((packed)); -#define AVRCP_HEADER_LENGTH 7 - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct avrcp_header { - uint8_t company_id[3]; - uint8_t pdu_id; - uint8_t rsvd:6; - uint8_t packet_type:2; - uint16_t params_len; - uint8_t params[0]; -} __attribute__ ((packed)); -#define AVRCP_HEADER_LENGTH 7 - -#else -#error "Unknown byte order" -#endif - -#define AVRCP_MTU (AVC_MTU - AVC_HEADER_LENGTH) -#define AVRCP_PDU_MTU (AVRCP_MTU - AVRCP_HEADER_LENGTH) - -struct avrcp_server { - bdaddr_t src; - uint32_t tg_record_id; - uint32_t ct_record_id; - GSList *players; - struct avrcp_player *active_player; -}; - -struct pending_pdu { - uint8_t pdu_id; - GList *attr_ids; - uint16_t offset; -}; - -struct avrcp_player { - struct avrcp_server *server; - struct avctp *session; - struct audio_device *dev; - - unsigned int handler; - uint16_t registered_events; - uint8_t transaction_events[AVRCP_EVENT_LAST + 1]; - struct pending_pdu *pending_pdu; - - struct avrcp_player_cb *cb; - void *user_data; - GDestroyNotify destroy; -}; - -static GSList *servers = NULL; -static unsigned int avctp_id = 0; - -/* Company IDs supported by this device */ -static uint32_t company_ids[] = { - IEEEID_BTSIG, -}; - -static void register_volume_notification(struct avrcp_player *player); - -static sdp_record_t *avrcp_ct_record(void) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, l2cap, avctp, avrct; - sdp_profile_desc_t profile[1]; - sdp_list_t *aproto, *proto[2]; - sdp_record_t *record; - sdp_data_t *psm, *version, *features; - uint16_t lp = AVCTP_PSM; - uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103; - uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | - AVRCP_FEATURE_CATEGORY_2 | - AVRCP_FEATURE_CATEGORY_3 | - AVRCP_FEATURE_CATEGORY_4 ); - - record = sdp_record_alloc(); - if (!record) - return NULL; - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(record, root); - - /* Service Class ID List */ - sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID); - svclass_id = sdp_list_append(0, &avrct); - sdp_set_service_classes(record, svclass_id); - - /* Protocol Descriptor List */ - sdp_uuid16_create(&l2cap, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap); - psm = sdp_data_alloc(SDP_UINT16, &lp); - proto[0] = sdp_list_append(proto[0], psm); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&avctp, AVCTP_UUID); - proto[1] = sdp_list_append(0, &avctp); - version = sdp_data_alloc(SDP_UINT16, &avctp_ver); - proto[1] = sdp_list_append(proto[1], version); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(record, aproto); - - /* Bluetooth Profile Descriptor List */ - sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); - profile[0].version = avrcp_ver; - pfseq = sdp_list_append(0, &profile[0]); - sdp_set_profile_descs(record, pfseq); - - features = sdp_data_alloc(SDP_UINT16, &feat); - sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); - - sdp_set_info_attr(record, "AVRCP CT", 0, 0); - - free(psm); - free(version); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); - - return record; -} - -static sdp_record_t *avrcp_tg_record(void) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, l2cap, avctp, avrtg; - sdp_profile_desc_t profile[1]; - sdp_list_t *aproto, *proto[2]; - sdp_record_t *record; - sdp_data_t *psm, *version, *features; - uint16_t lp = AVCTP_PSM; - uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103; - uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | - AVRCP_FEATURE_CATEGORY_2 | - AVRCP_FEATURE_CATEGORY_3 | - AVRCP_FEATURE_CATEGORY_4 | - AVRCP_FEATURE_PLAYER_SETTINGS ); - - record = sdp_record_alloc(); - if (!record) - return NULL; - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(record, root); - - /* Service Class ID List */ - sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID); - svclass_id = sdp_list_append(0, &avrtg); - sdp_set_service_classes(record, svclass_id); - - /* Protocol Descriptor List */ - sdp_uuid16_create(&l2cap, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap); - psm = sdp_data_alloc(SDP_UINT16, &lp); - proto[0] = sdp_list_append(proto[0], psm); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&avctp, AVCTP_UUID); - proto[1] = sdp_list_append(0, &avctp); - version = sdp_data_alloc(SDP_UINT16, &avctp_ver); - proto[1] = sdp_list_append(proto[1], version); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(record, aproto); - - /* Bluetooth Profile Descriptor List */ - sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); - profile[0].version = avrcp_ver; - pfseq = sdp_list_append(0, &profile[0]); - sdp_set_profile_descs(record, pfseq); - - features = sdp_data_alloc(SDP_UINT16, &feat); - sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); - - sdp_set_info_attr(record, "AVRCP TG", 0, 0); - - free(psm); - free(version); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); - - return record; -} - -static unsigned int attr_get_max_val(uint8_t attr) -{ - switch (attr) { - case AVRCP_ATTRIBUTE_EQUALIZER: - return AVRCP_EQUALIZER_ON; - case AVRCP_ATTRIBUTE_REPEAT_MODE: - return AVRCP_REPEAT_MODE_GROUP; - case AVRCP_ATTRIBUTE_SHUFFLE: - return AVRCP_SHUFFLE_GROUP; - case AVRCP_ATTRIBUTE_SCAN: - return AVRCP_SCAN_GROUP; - } - - return 0; -} - -static const char *battery_status_to_str(enum battery_status status) -{ - switch (status) { - case BATTERY_STATUS_NORMAL: - return "normal"; - case BATTERY_STATUS_WARNING: - return "warning"; - case BATTERY_STATUS_CRITICAL: - return "critical"; - case BATTERY_STATUS_EXTERNAL: - return "external"; - case BATTERY_STATUS_FULL_CHARGE: - return "fullcharge"; - } - - return NULL; -} - -/* - * get_company_id: - * - * Get three-byte Company_ID from incoming AVRCP message - */ -static uint32_t get_company_id(const uint8_t cid[3]) -{ - return cid[0] << 16 | cid[1] << 8 | cid[2]; -} - -/* - * set_company_id: - * - * Set three-byte Company_ID into outgoing AVRCP message - */ -static void set_company_id(uint8_t cid[3], const uint32_t cid_in) -{ - cid[0] = cid_in >> 16; - cid[1] = cid_in >> 8; - cid[2] = cid_in; -} - -int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data) -{ - uint8_t buf[AVRCP_HEADER_LENGTH + 9]; - struct avrcp_header *pdu = (void *) buf; - uint16_t size; - int err; - - if (player->session == NULL) - return -ENOTCONN; - - if (!(player->registered_events & (1 << id))) - return 0; - - memset(buf, 0, sizeof(buf)); - - set_company_id(pdu->company_id, IEEEID_BTSIG); - - pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION; - pdu->params[0] = id; - - DBG("id=%u", id); - - switch (id) { - case AVRCP_EVENT_STATUS_CHANGED: - size = 2; - pdu->params[1] = *((uint8_t *)data); - - break; - case AVRCP_EVENT_TRACK_CHANGED: - size = 9; - memcpy(&pdu->params[1], data, sizeof(uint64_t)); - - break; - case AVRCP_EVENT_TRACK_REACHED_END: - case AVRCP_EVENT_TRACK_REACHED_START: - size = 1; - break; - default: - error("Unknown event %u", id); - return -EINVAL; - } - - pdu->params_len = htons(size); - - err = avctp_send_vendordep(player->session, player->transaction_events[id], - AVC_CTYPE_CHANGED, 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 */ - player->registered_events ^= 1 << id; - - return 0; -} - -static uint16_t player_write_media_attribute(struct avrcp_player *player, - uint32_t id, uint8_t *buf, - uint16_t *pos, - uint16_t *offset) -{ - uint16_t len; - uint16_t attr_len; - char valstr[20]; - void *value; - - DBG("%u", id); - - value = player->cb->get_metadata(id, player->user_data); - if (value == NULL) { - *offset = 0; - return 0; - } - - switch (id) { - case AVRCP_MEDIA_ATTRIBUTE_TRACK: - case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS: - case AVRCP_MEDIA_ATTRIBUTE_DURATION: - snprintf(valstr, 20, "%u", GPOINTER_TO_UINT(value)); - value = valstr; - break; - } - - attr_len = strlen(value); - value = ((char *) value) + *offset; - len = attr_len - *offset; - - if (len > AVRCP_PDU_MTU - *pos) { - len = AVRCP_PDU_MTU - *pos; - *offset += len; - } else { - *offset = 0; - } - - memcpy(&buf[*pos], value, len); - *pos += len; - - return attr_len; -} - -static GList *player_fill_media_attribute(struct avrcp_player *player, - GList *attr_ids, uint8_t *buf, - uint16_t *pos, uint16_t *offset) -{ - struct media_attribute_header { - uint32_t id; - uint16_t charset; - uint16_t len; - } *hdr = NULL; - GList *l; - - for (l = attr_ids; l != NULL; l = g_list_delete_link(l, l)) { - uint32_t attr = GPOINTER_TO_UINT(l->data); - uint16_t attr_len; - - if (*offset == 0) { - if (*pos + sizeof(*hdr) >= AVRCP_PDU_MTU) - break; - - hdr = (void *) &buf[*pos]; - hdr->id = htonl(attr); - hdr->charset = htons(0x6A); /* Always use UTF-8 */ - *pos += sizeof(*hdr); - } - - attr_len = player_write_media_attribute(player, attr, buf, - pos, offset); - - if (hdr != NULL) - hdr->len = htons(attr_len); - - if (*offset > 0) - break; - } - - return l; -} - -static struct pending_pdu *pending_pdu_new(uint8_t pdu_id, GList *attr_ids, - unsigned int offset) -{ - struct pending_pdu *pending = g_new(struct pending_pdu, 1); - - pending->pdu_id = pdu_id; - pending->attr_ids = attr_ids; - pending->offset = offset; - - return pending; -} - -static gboolean player_abort_pending_pdu(struct avrcp_player *player) -{ - if (player->pending_pdu == NULL) - return FALSE; - - g_list_free(player->pending_pdu->attr_ids); - g_free(player->pending_pdu); - player->pending_pdu = NULL; - - return TRUE; -} - -static int player_set_attribute(struct avrcp_player *player, - uint8_t attr, uint8_t val) -{ - DBG("Change attribute: %u %u", attr, val); - - return player->cb->set_setting(attr, val, player->user_data); -} - -static int player_get_attribute(struct avrcp_player *player, uint8_t attr) -{ - int value; - - DBG("attr %u", attr); - - value = player->cb->get_setting(attr, player->user_data); - if (value < 0) - DBG("attr %u not supported by player", attr); - - return value; -} - -static uint8_t avrcp_handle_get_capabilities(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - unsigned int i; - - if (len != 1) - goto err; - - DBG("id=%u", pdu->params[0]); - - switch (pdu->params[0]) { - case CAP_COMPANY_ID: - for (i = 0; i < G_N_ELEMENTS(company_ids); i++) { - set_company_id(&pdu->params[2 + i * 3], - company_ids[i]); - } - - pdu->params_len = htons(2 + (3 * G_N_ELEMENTS(company_ids))); - pdu->params[1] = G_N_ELEMENTS(company_ids); - - return AVC_CTYPE_STABLE; - case CAP_EVENTS_SUPPORTED: - pdu->params[1] = 4; - pdu->params[2] = AVRCP_EVENT_STATUS_CHANGED; - pdu->params[3] = AVRCP_EVENT_TRACK_CHANGED; - pdu->params[4] = AVRCP_EVENT_TRACK_REACHED_START; - pdu->params[5] = AVRCP_EVENT_TRACK_REACHED_END; - - pdu->params_len = htons(2 + pdu->params[1]); - return AVC_CTYPE_STABLE; - } - -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - - return AVC_CTYPE_REJECTED; -} - -static uint8_t avrcp_handle_list_player_attributes(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - unsigned int i; - - if (len != 0) { - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; - } - - if (!player) - goto done; - - for (i = 1; i <= AVRCP_ATTRIBUTE_SCAN; i++) { - if (player_get_attribute(player, i) < 0) - continue; - - len++; - pdu->params[len] = i; - } - -done: - pdu->params[0] = len; - pdu->params_len = htons(len + 1); - - return AVC_CTYPE_STABLE; -} - -static uint8_t avrcp_handle_list_player_values(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - unsigned int i; - - if (len != 1 || !player) - goto err; - - if (player_get_attribute(player, pdu->params[0]) < 0) - goto err; - - len = attr_get_max_val(pdu->params[0]); - - for (i = 1; i <= len; i++) - pdu->params[i] = i; - - pdu->params[0] = len; - pdu->params_len = htons(len + 1); - - return AVC_CTYPE_STABLE; - -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; -} - -static uint8_t avrcp_handle_get_element_attributes(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - uint64_t *identifier = (uint64_t *) &pdu->params[0]; - uint16_t pos; - uint8_t nattr; - GList *attr_ids; - uint16_t offset; - - if (len < 9 || *identifier != 0) - goto err; - - nattr = pdu->params[8]; - - if (len < nattr * sizeof(uint32_t) + 1) - goto err; - - if (!nattr) { - /* - * Return all available information, at least - * title must be returned if there's a track selected. - */ - attr_ids = player->cb->list_metadata(player->user_data); - len = g_list_length(attr_ids); - } else { - unsigned int i; - uint32_t *attr = (uint32_t *) &pdu->params[9]; - - for (i = 0, len = 0, attr_ids = NULL; i < nattr; i++, attr++) { - uint32_t id = ntohl(bt_get_unaligned(attr)); - - /* Don't add invalid attributes */ - if (id == AVRCP_MEDIA_ATTRIBUTE_ILLEGAL || - id > AVRCP_MEDIA_ATTRIBUTE_LAST) - continue; - - len++; - attr_ids = g_list_prepend(attr_ids, - GUINT_TO_POINTER(id)); - } - - attr_ids = g_list_reverse(attr_ids); - } - - if (!len) - goto err; - - player_abort_pending_pdu(player); - pos = 1; - offset = 0; - attr_ids = player_fill_media_attribute(player, attr_ids, pdu->params, - &pos, &offset); - - if (attr_ids != NULL) { - player->pending_pdu = pending_pdu_new(pdu->pdu_id, attr_ids, - offset); - pdu->packet_type = AVRCP_PACKET_TYPE_START; - } - - pdu->params[0] = len; - pdu->params_len = htons(pos); - - return AVC_CTYPE_STABLE; -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; -} - -static uint8_t avrcp_handle_get_current_player_value(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - uint8_t *settings; - unsigned int i; - - if (player == NULL || len <= 1 || pdu->params[0] != len - 1) - goto err; - - /* - * Save a copy of requested settings because we can override them - * while responding - */ - settings = g_memdup(&pdu->params[1], pdu->params[0]); - len = 0; - - /* - * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs - * and send a response with the existent ones. Only if all IDs are - * non-existent we should send an error. - */ - for (i = 0; i < pdu->params[0]; i++) { - int val; - - if (settings[i] < AVRCP_ATTRIBUTE_EQUALIZER || - settings[i] > AVRCP_ATTRIBUTE_SCAN) { - DBG("Ignoring %u", settings[i]); - continue; - } - - val = player_get_attribute(player, settings[i]); - if (val < 0) - continue; - - pdu->params[++len] = settings[i]; - pdu->params[++len] = val; - } - - g_free(settings); - - if (len) { - pdu->params[0] = len / 2; - pdu->params_len = htons(len + 1); - - return AVC_CTYPE_STABLE; - } - - error("No valid attributes in request"); - -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - - return AVC_CTYPE_REJECTED; -} - -static uint8_t avrcp_handle_set_player_value(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - unsigned int i; - uint8_t *param; - - if (len < 3 || len > 2 * pdu->params[0] + 1U) - goto err; - - /* - * From sec. 5.7 of AVRCP 1.3 spec, we should igore non-existent IDs - * and set the existent ones. Sec. 5.2.4 is not clear however how to - * indicate that a certain ID was not accepted. If at least one - * attribute is valid, we respond with no parameters. Otherwise an - * E_INVALID_PARAM is sent. - */ - for (len = 0, i = 0, param = &pdu->params[1]; i < pdu->params[0]; - i++, param += 2) { - if (player_set_attribute(player, param[0], param[1]) < 0) - continue; - - len++; - } - - if (len) { - pdu->params_len = 0; - - return AVC_CTYPE_ACCEPTED; - } - -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; -} - -static uint8_t avrcp_handle_displayable_charset(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - - if (len < 3) { - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; - } - - /* - * We acknowledge the commands, but we always use UTF-8 for - * encoding since CT is obliged to support it. - */ - pdu->params_len = 0; - return AVC_CTYPE_STABLE; -} - -static uint8_t avrcp_handle_ct_battery_status(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - const char *valstr; - - if (len != 1) - goto err; - - valstr = battery_status_to_str(pdu->params[0]); - if (valstr == NULL) - goto err; - - pdu->params_len = 0; - - return AVC_CTYPE_STABLE; - -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; -} - -static uint8_t avrcp_handle_get_play_status(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - uint32_t position; - uint32_t duration; - void *pduration; - - if (len != 0) { - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; - } - - position = player->cb->get_position(player->user_data); - pduration = player->cb->get_metadata(AVRCP_MEDIA_ATTRIBUTE_DURATION, - player->user_data); - if (pduration != NULL) - duration = htonl(GPOINTER_TO_UINT(pduration)); - else - duration = htonl(UINT32_MAX); - - position = htonl(position); - - memcpy(&pdu->params[0], &duration, 4); - memcpy(&pdu->params[4], &position, 4); - pdu->params[8] = player->cb->get_status(player->user_data);; - - pdu->params_len = htons(9); - - return AVC_CTYPE_STABLE; -} - -static uint8_t avrcp_handle_register_notification(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - uint64_t uid; - - /* - * 1 byte for EventID, 4 bytes for Playback interval but the latest - * one is applicable only for EVENT_PLAYBACK_POS_CHANGED. See AVRCP - * 1.3 spec, section 5.4.2. - */ - if (len != 5) - goto err; - - switch (pdu->params[0]) { - case AVRCP_EVENT_STATUS_CHANGED: - len = 2; - pdu->params[1] = player->cb->get_status(player->user_data); - - break; - case AVRCP_EVENT_TRACK_CHANGED: - len = 9; - uid = player->cb->get_uid(player->user_data); - memcpy(&pdu->params[1], &uid, sizeof(uint64_t)); - - break; - case AVRCP_EVENT_TRACK_REACHED_END: - case AVRCP_EVENT_TRACK_REACHED_START: - len = 1; - break; - default: - /* All other events are not supported yet */ - goto err; - } - - /* Register event and save the transaction used */ - player->registered_events |= (1 << pdu->params[0]); - player->transaction_events[pdu->params[0]] = transaction; - - pdu->params_len = htons(len); - - return AVC_CTYPE_INTERIM; - -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; -} - -static uint8_t avrcp_handle_request_continuing(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - struct pending_pdu *pending; - - if (len != 1 || player->pending_pdu == NULL) - goto err; - - pending = player->pending_pdu; - - if (pending->pdu_id != pdu->params[0]) - goto err; - - - len = 0; - pending->attr_ids = player_fill_media_attribute(player, - pending->attr_ids, - pdu->params, &len, - &pending->offset); - pdu->pdu_id = pending->pdu_id; - - if (pending->attr_ids == NULL) { - g_free(player->pending_pdu); - player->pending_pdu = NULL; - pdu->packet_type = AVRCP_PACKET_TYPE_END; - } else { - pdu->packet_type = AVRCP_PACKET_TYPE_CONTINUING; - } - - pdu->params_len = htons(len); - - return AVC_CTYPE_STABLE; -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; -} - -static uint8_t avrcp_handle_abort_continuing(struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction) -{ - uint16_t len = ntohs(pdu->params_len); - struct pending_pdu *pending; - - if (len != 1 || player->pending_pdu == NULL) - goto err; - - pending = player->pending_pdu; - - if (pending->pdu_id != pdu->params[0]) - goto err; - - player_abort_pending_pdu(player); - pdu->params_len = 0; - - return AVC_CTYPE_ACCEPTED; - -err: - pdu->params_len = htons(1); - pdu->params[0] = E_INVALID_PARAM; - return AVC_CTYPE_REJECTED; -} - -static struct pdu_handler { - uint8_t pdu_id; - uint8_t code; - uint8_t (*func) (struct avrcp_player *player, - struct avrcp_header *pdu, - uint8_t transaction); -} handlers[] = { - { AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS, - avrcp_handle_get_capabilities }, - { AVRCP_LIST_PLAYER_ATTRIBUTES, AVC_CTYPE_STATUS, - avrcp_handle_list_player_attributes }, - { AVRCP_LIST_PLAYER_VALUES, AVC_CTYPE_STATUS, - avrcp_handle_list_player_values }, - { AVRCP_GET_ELEMENT_ATTRIBUTES, AVC_CTYPE_STATUS, - avrcp_handle_get_element_attributes }, - { AVRCP_GET_CURRENT_PLAYER_VALUE, AVC_CTYPE_STATUS, - avrcp_handle_get_current_player_value }, - { AVRCP_SET_PLAYER_VALUE, AVC_CTYPE_CONTROL, - avrcp_handle_set_player_value }, - { AVRCP_GET_PLAYER_ATTRIBUTE_TEXT, AVC_CTYPE_STATUS, - NULL }, - { AVRCP_GET_PLAYER_VALUE_TEXT, AVC_CTYPE_STATUS, - NULL }, - { AVRCP_DISPLAYABLE_CHARSET, AVC_CTYPE_STATUS, - avrcp_handle_displayable_charset }, - { AVRCP_CT_BATTERY_STATUS, AVC_CTYPE_STATUS, - avrcp_handle_ct_battery_status }, - { AVRCP_GET_PLAY_STATUS, AVC_CTYPE_STATUS, - avrcp_handle_get_play_status }, - { AVRCP_REGISTER_NOTIFICATION, AVC_CTYPE_NOTIFY, - avrcp_handle_register_notification }, - { AVRCP_REQUEST_CONTINUING, AVC_CTYPE_CONTROL, - avrcp_handle_request_continuing }, - { AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL, - avrcp_handle_abort_continuing }, - { }, -}; - -/* handle vendordep pdu inside an avctp packet */ -static size_t handle_vendordep_pdu(struct avctp *session, uint8_t transaction, - uint8_t *code, uint8_t *subunit, - uint8_t *operands, size_t operand_count, - void *user_data) -{ - struct avrcp_player *player = user_data; - struct pdu_handler *handler; - struct avrcp_header *pdu = (void *) operands; - uint32_t company_id = get_company_id(pdu->company_id); - - if (company_id != IEEEID_BTSIG) { - *code = AVC_CTYPE_NOT_IMPLEMENTED; - return 0; - } - - DBG("AVRCP PDU 0x%02X, company 0x%06X len 0x%04X", - pdu->pdu_id, company_id, pdu->params_len); - - pdu->packet_type = 0; - pdu->rsvd = 0; - - if (operand_count < AVRCP_HEADER_LENGTH) { - pdu->params[0] = E_INVALID_COMMAND; - goto err_metadata; - } - - for (handler = handlers; handler; handler++) { - if (handler->pdu_id == pdu->pdu_id) - break; - } - - if (!handler || handler->code != *code) { - pdu->params[0] = E_INVALID_COMMAND; - goto err_metadata; - } - - if (!handler->func) { - pdu->params[0] = E_INVALID_PARAM; - goto err_metadata; - } - - *code = handler->func(player, pdu, transaction); - - if (*code != AVC_CTYPE_REJECTED && - pdu->pdu_id != AVRCP_GET_ELEMENT_ATTRIBUTES && - pdu->pdu_id != AVRCP_REQUEST_CONTINUING && - pdu->pdu_id != AVRCP_ABORT_CONTINUING) - player_abort_pending_pdu(player); - - return AVRCP_HEADER_LENGTH + ntohs(pdu->params_len); - -err_metadata: - pdu->params_len = htons(1); - *code = AVC_CTYPE_REJECTED; - - return AVRCP_HEADER_LENGTH + 1; -} - -size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands) -{ - struct avrcp_header *pdu = (void *) operands; - uint32_t company_id = get_company_id(pdu->company_id); - - *code = AVC_CTYPE_REJECTED; - pdu->params_len = htons(1); - pdu->params[0] = E_INTERNAL; - - DBG("rejecting AVRCP PDU 0x%02X, company 0x%06X len 0x%04X", - pdu->pdu_id, company_id, pdu->params_len); - - return AVRCP_HEADER_LENGTH + 1; -} - -static struct avrcp_server *find_server(GSList *list, const bdaddr_t *src) -{ - for (; list; list = list->next) { - struct avrcp_server *server = list->data; - - if (bacmp(&server->src, src) == 0) - return server; - } - - return NULL; -} - -static gboolean avrcp_handle_volume_changed(struct avctp *session, - uint8_t code, uint8_t subunit, - uint8_t *operands, size_t operand_count, - void *user_data) -{ - struct avrcp_player *player = user_data; - struct avrcp_header *pdu = (void *) operands; - uint8_t volume; - - if (code != AVC_CTYPE_INTERIM && code != AVC_CTYPE_CHANGED) - return FALSE; - - volume = pdu->params[1] & 0x7F; - - player->cb->set_volume(volume, player->dev, player->user_data); - - if (code == AVC_CTYPE_CHANGED) { - register_volume_notification(player); - return FALSE; - } - - return TRUE; -} - -static void register_volume_notification(struct avrcp_player *player) -{ - uint8_t buf[AVRCP_HEADER_LENGTH + AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH]; - struct avrcp_header *pdu = (void *) buf; - uint8_t length; - - memset(buf, 0, sizeof(buf)); - - set_company_id(pdu->company_id, IEEEID_BTSIG); - pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION; - pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE; - pdu->params[0] = AVRCP_EVENT_VOLUME_CHANGED; - pdu->params_len = htons(AVRCP_REGISTER_NOTIFICATION_PARAM_LENGTH); - - length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len); - - avctp_send_vendordep_req(player->session, AVC_CTYPE_NOTIFY, - AVC_SUBUNIT_PANEL, buf, length, - avrcp_handle_volume_changed, player); -} - -static void state_changed(struct audio_device *dev, avctp_state_t old_state, - avctp_state_t new_state, void *user_data) -{ - struct avrcp_server *server; - struct avrcp_player *player; - const sdp_record_t *rec; - sdp_list_t *list; - sdp_profile_desc_t *desc; - - server = find_server(servers, &dev->src); - if (!server) - return; - - player = server->active_player; - if (!player) - return; - - switch (new_state) { - case AVCTP_STATE_DISCONNECTED: - player->session = NULL; - player->dev = NULL; - player->registered_events = 0; - - if (player->handler) { - avctp_unregister_pdu_handler(player->handler); - player->handler = 0; - } - - break; - case AVCTP_STATE_CONNECTING: - player->session = avctp_connect(&dev->src, &dev->dst); - player->dev = dev; - - if (!player->handler) - player->handler = avctp_register_pdu_handler( - AVC_OP_VENDORDEP, - handle_vendordep_pdu, - player); - break; - case AVCTP_STATE_CONNECTED: - rec = btd_device_get_record(dev->btd_dev, AVRCP_TARGET_UUID); - if (rec == NULL) - return; - - if (sdp_get_profile_descs(rec, &list) < 0) - return; - - desc = list->data; - - if (desc && desc->version >= 0x0104) - register_volume_notification(player); - - sdp_list_free(list, free); - default: - return; - } -} - -gboolean avrcp_connect(struct audio_device *dev) -{ - struct avctp *session; - - session = avctp_connect(&dev->src, &dev->dst); - if (session) - return FALSE; - - return TRUE; -} - -void avrcp_disconnect(struct audio_device *dev) -{ - struct avctp *session; - - session = avctp_get(&dev->src, &dev->dst); - if (!session) - return; - - avctp_disconnect(session); -} - -int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config) -{ - sdp_record_t *record; - gboolean tmp, master = TRUE; - GError *err = NULL; - struct avrcp_server *server; - - if (config) { - tmp = g_key_file_get_boolean(config, "General", - "Master", &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_error_free(err); - } else - master = tmp; - } - - server = g_new0(struct avrcp_server, 1); - if (!server) - return -ENOMEM; - - record = avrcp_tg_record(); - if (!record) { - error("Unable to allocate new service record"); - g_free(server); - return -1; - } - - if (add_record_to_server(src, record) < 0) { - error("Unable to register AVRCP target service record"); - g_free(server); - sdp_record_free(record); - return -1; - } - server->tg_record_id = record->handle; - - record = avrcp_ct_record(); - if (!record) { - error("Unable to allocate new service record"); - g_free(server); - return -1; - } - - if (add_record_to_server(src, record) < 0) { - error("Unable to register AVRCP service record"); - sdp_record_free(record); - g_free(server); - return -1; - } - server->ct_record_id = record->handle; - - if (avctp_register(src, master) < 0) { - remove_record_from_server(server->ct_record_id); - remove_record_from_server(server->tg_record_id); - g_free(server); - return -1; - } - - bacpy(&server->src, src); - - servers = g_slist_append(servers, server); - - return 0; -} - -static void player_destroy(gpointer data) -{ - struct avrcp_player *player = data; - - if (player->destroy) - player->destroy(player->user_data); - - player_abort_pending_pdu(player); - - if (player->handler) - avctp_unregister_pdu_handler(player->handler); - - g_free(player); -} - -void avrcp_unregister(const bdaddr_t *src) -{ - struct avrcp_server *server; - - server = find_server(servers, src); - if (!server) - return; - - g_slist_free_full(server->players, player_destroy); - - servers = g_slist_remove(servers, server); - - remove_record_from_server(server->ct_record_id); - remove_record_from_server(server->tg_record_id); - - avctp_unregister(&server->src); - g_free(server); - - if (servers) - return; - - if (avctp_id) { - avctp_remove_state_cb(avctp_id); - avctp_id = 0; - } -} - -struct avrcp_player *avrcp_register_player(const bdaddr_t *src, - struct avrcp_player_cb *cb, - void *user_data, - GDestroyNotify destroy) -{ - struct avrcp_server *server; - struct avrcp_player *player; - - server = find_server(servers, src); - if (!server) - return NULL; - - player = g_new0(struct avrcp_player, 1); - player->server = server; - player->cb = cb; - player->user_data = user_data; - player->destroy = destroy; - - if (!server->players) - server->active_player = player; - - if (!avctp_id) - avctp_id = avctp_add_state_cb(state_changed, NULL); - - server->players = g_slist_append(server->players, player); - - return player; -} - -void avrcp_unregister_player(struct avrcp_player *player) -{ - struct avrcp_server *server = player->server; - - server->players = g_slist_remove(server->players, player); - - if (server->active_player == player) - server->active_player = g_slist_nth_data(server->players, 0); - - player_destroy(player); -} - -static gboolean avrcp_handle_set_volume(struct avctp *session, - uint8_t code, uint8_t subunit, - uint8_t *operands, size_t operand_count, - void *user_data) -{ - struct avrcp_player *player = user_data; - struct avrcp_header *pdu = (void *) operands; - uint8_t volume; - - if (code == AVC_CTYPE_REJECTED || code == AVC_CTYPE_NOT_IMPLEMENTED) - return FALSE; - - volume = pdu->params[0] & 0x7F; - - player->cb->set_volume(volume, player->dev, player->user_data); - - return FALSE; -} - -int avrcp_set_volume(struct audio_device *dev, uint8_t volume) -{ - struct avrcp_server *server; - struct avrcp_player *player; - uint8_t buf[AVRCP_HEADER_LENGTH + 1]; - struct avrcp_header *pdu = (void *) buf; - - server = find_server(servers, &dev->src); - if (server == NULL) - return -EINVAL; - - player = server->active_player; - if (player == NULL) - return -ENOTSUP; - - if (player->session == NULL) - return -ENOTCONN; - - memset(buf, 0, sizeof(buf)); - - set_company_id(pdu->company_id, IEEEID_BTSIG); - - pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME; - pdu->params[0] = volume; - pdu->params_len = htons(1); - - DBG("volume=%u", volume); - - return avctp_send_vendordep_req(player->session, AVC_CTYPE_CONTROL, - AVC_SUBUNIT_PANEL, buf, sizeof(buf), - avrcp_handle_set_volume, player); -} diff -Nru bluez-4.101/audio/avrcp.h bluez-5.23/audio/avrcp.h --- bluez-4.101/audio/avrcp.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/avrcp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -/* player attributes */ -#define AVRCP_ATTRIBUTE_ILEGAL 0x00 -#define AVRCP_ATTRIBUTE_EQUALIZER 0x01 -#define AVRCP_ATTRIBUTE_REPEAT_MODE 0x02 -#define AVRCP_ATTRIBUTE_SHUFFLE 0x03 -#define AVRCP_ATTRIBUTE_SCAN 0x04 - -/* equalizer values */ -#define AVRCP_EQUALIZER_OFF 0x01 -#define AVRCP_EQUALIZER_ON 0x02 - -/* repeat mode values */ -#define AVRCP_REPEAT_MODE_OFF 0x01 -#define AVRCP_REPEAT_MODE_SINGLE 0x02 -#define AVRCP_REPEAT_MODE_ALL 0x03 -#define AVRCP_REPEAT_MODE_GROUP 0x04 - -/* shuffle values */ -#define AVRCP_SHUFFLE_OFF 0x01 -#define AVRCP_SHUFFLE_ALL 0x02 -#define AVRCP_SHUFFLE_GROUP 0x03 - -/* scan values */ -#define AVRCP_SCAN_OFF 0x01 -#define AVRCP_SCAN_ALL 0x02 -#define AVRCP_SCAN_GROUP 0x03 - -/* media attributes */ -#define AVRCP_MEDIA_ATTRIBUTE_ILLEGAL 0x00 -#define AVRCP_MEDIA_ATTRIBUTE_TITLE 0x01 -#define AVRCP_MEDIA_ATTRIBUTE_ARTIST 0x02 -#define AVRCP_MEDIA_ATTRIBUTE_ALBUM 0x03 -#define AVRCP_MEDIA_ATTRIBUTE_TRACK 0x04 -#define AVRCP_MEDIA_ATTRIBUTE_N_TRACKS 0x05 -#define AVRCP_MEDIA_ATTRIBUTE_GENRE 0x06 -#define AVRCP_MEDIA_ATTRIBUTE_DURATION 0x07 -#define AVRCP_MEDIA_ATTRIBUTE_LAST AVRCP_MEDIA_ATTRIBUTE_DURATION - -/* play status */ -#define AVRCP_PLAY_STATUS_STOPPED 0x00 -#define AVRCP_PLAY_STATUS_PLAYING 0x01 -#define AVRCP_PLAY_STATUS_PAUSED 0x02 -#define AVRCP_PLAY_STATUS_FWD_SEEK 0x03 -#define AVRCP_PLAY_STATUS_REV_SEEK 0x04 -#define AVRCP_PLAY_STATUS_ERROR 0xFF - -/* Notification events */ -#define AVRCP_EVENT_STATUS_CHANGED 0x01 -#define AVRCP_EVENT_TRACK_CHANGED 0x02 -#define AVRCP_EVENT_TRACK_REACHED_END 0x03 -#define AVRCP_EVENT_TRACK_REACHED_START 0x04 -#define AVRCP_EVENT_VOLUME_CHANGED 0x0d -#define AVRCP_EVENT_LAST AVRCP_EVENT_VOLUME_CHANGED - -struct avrcp_player_cb { - int (*get_setting) (uint8_t attr, void *user_data); - int (*set_setting) (uint8_t attr, uint8_t value, void *user_data); - uint64_t (*get_uid) (void *user_data); - void *(*get_metadata) (uint32_t id, void *user_data); - GList *(*list_metadata) (void *user_data); - uint8_t (*get_status) (void *user_data); - uint32_t (*get_position) (void *user_data); - void (*set_volume) (uint8_t volume, struct audio_device *dev, - void *user_data); -}; - -int avrcp_register(DBusConnection *conn, const bdaddr_t *src, GKeyFile *config); -void avrcp_unregister(const bdaddr_t *src); - -gboolean avrcp_connect(struct audio_device *dev); -void avrcp_disconnect(struct audio_device *dev); -int avrcp_set_volume(struct audio_device *dev, uint8_t volume); - -struct avrcp_player *avrcp_register_player(const bdaddr_t *src, - struct avrcp_player_cb *cb, - void *user_data, - GDestroyNotify destroy); -void avrcp_unregister_player(struct avrcp_player *player); - -int avrcp_player_event(struct avrcp_player *player, uint8_t id, void *data); - - -size_t avrcp_handle_vendor_reject(uint8_t *code, uint8_t *operands); diff -Nru bluez-4.101/audio/bluetooth.conf bluez-5.23/audio/bluetooth.conf --- bluez-4.101/audio/bluetooth.conf 2009-05-03 23:21:09.000000000 +0000 +++ bluez-5.23/audio/bluetooth.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -# Please note that this ALSA configuration file fragment needs be enabled in -# /etc/asound.conf or a similar configuration file with directives similar to -# the following: -# -#@hooks [ -# { -# func load -# files [ -# "/etc/alsa/bluetooth.conf" -# ] -# errors false -# } -#] - -pcm.rawbluetooth { - @args [ ADDRESS ] - @args.ADDRESS { - type string - } - type bluetooth - device $ADDRESS -} - -pcm.bluetooth { - @args [ ADDRESS ] - @args.ADDRESS { - type string - } - type plug - slave { - pcm { - type bluetooth - device $ADDRESS - } - } -} diff -Nru bluez-4.101/audio/control.c bluez-5.23/audio/control.c --- bluez-4.101/audio/control.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/control.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,279 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * Copyright (C) 2011 Texas Instruments, Inc. - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "log.h" -#include "error.h" -#include "device.h" -#include "manager.h" -#include "avctp.h" -#include "control.h" -#include "sdpd.h" -#include "glib-helper.h" -#include "dbus-common.h" - -static unsigned int avctp_id = 0; - -struct control { - struct audio_device *dev; - struct avctp *session; - - gboolean target; -}; - -static void state_changed(struct audio_device *dev, avctp_state_t old_state, - avctp_state_t new_state, void *user_data) -{ - struct control *control = dev->control; - gboolean value; - - switch (new_state) { - case AVCTP_STATE_DISCONNECTED: - control->session = NULL; - - if (old_state != AVCTP_STATE_CONNECTED) - break; - - value = FALSE; - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_CONTROL_INTERFACE, - "Disconnected", DBUS_TYPE_INVALID); - emit_property_changed(dev->conn, dev->path, - AUDIO_CONTROL_INTERFACE, "Connected", - DBUS_TYPE_BOOLEAN, &value); - - break; - case AVCTP_STATE_CONNECTING: - if (control->session) - break; - - control->session = avctp_get(&dev->src, &dev->dst); - - break; - case AVCTP_STATE_CONNECTED: - value = TRUE; - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_CONTROL_INTERFACE, "Connected", - DBUS_TYPE_INVALID); - emit_property_changed(dev->conn, dev->path, - AUDIO_CONTROL_INTERFACE, "Connected", - DBUS_TYPE_BOOLEAN, &value); - break; - default: - return; - } -} - -static DBusMessage *control_is_connected(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct control *control = device->control; - DBusMessage *reply; - dbus_bool_t connected; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - connected = (control->session != NULL); - - dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, - DBUS_TYPE_INVALID); - - return reply; -} - -static DBusMessage *volume_up(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct control *control = device->control; - int err; - - if (!control->session) - return btd_error_not_connected(msg); - - if (!control->target) - return btd_error_not_supported(msg); - - err = avctp_send_passthrough(control->session, VOL_UP_OP); - if (err < 0) - return btd_error_failed(msg, strerror(-err)); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *volume_down(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct control *control = device->control; - int err; - - if (!control->session) - return btd_error_not_connected(msg); - - if (!control->target) - return btd_error_not_supported(msg); - - err = avctp_send_passthrough(control->session, VOL_DOWN_OP); - if (err < 0) - return btd_error_failed(msg, strerror(-err)); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *control_get_properties(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter dict; - gboolean value; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - /* Connected */ - value = (device->control->session != NULL); - dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value); - - dbus_message_iter_close_container(&iter, &dict); - - return reply; -} - -static const GDBusMethodTable control_methods[] = { - { GDBUS_ASYNC_METHOD("IsConnected", - NULL, GDBUS_ARGS({ "connected", "b" }), - control_is_connected) }, - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - control_get_properties) }, - { GDBUS_METHOD("VolumeUp", NULL, NULL, volume_up) }, - { GDBUS_METHOD("VolumeDown", NULL, NULL, volume_down) }, - { } -}; - -static const GDBusSignalTable control_signals[] = { - { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) }, - { GDBUS_SIGNAL("PropertyChanged", - GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { } -}; - -static void path_unregister(void *data) -{ - struct audio_device *dev = data; - struct control *control = dev->control; - - DBG("Unregistered interface %s on path %s", - AUDIO_CONTROL_INTERFACE, dev->path); - - if (control->session) - avctp_disconnect(control->session); - - g_free(control); - dev->control = NULL; -} - -void control_unregister(struct audio_device *dev) -{ - g_dbus_unregister_interface(dev->conn, dev->path, - AUDIO_CONTROL_INTERFACE); -} - -void control_update(struct control *control, uint16_t uuid16) -{ - if (uuid16 == AV_REMOTE_TARGET_SVCLASS_ID) - control->target = TRUE; -} - -struct control *control_init(struct audio_device *dev, uint16_t uuid16) -{ - struct control *control; - - if (!g_dbus_register_interface(dev->conn, dev->path, - AUDIO_CONTROL_INTERFACE, - control_methods, control_signals, NULL, - dev, path_unregister)) - return NULL; - - DBG("Registered interface %s on path %s", - AUDIO_CONTROL_INTERFACE, dev->path); - - control = g_new0(struct control, 1); - control->dev = dev; - - control_update(control, uuid16); - - if (!avctp_id) - avctp_id = avctp_add_state_cb(state_changed, NULL); - - return control; -} - -gboolean control_is_active(struct audio_device *dev) -{ - struct control *control = dev->control; - - if (control && control->session) - return TRUE; - - return FALSE; -} diff -Nru bluez-4.101/audio/control.h bluez-5.23/audio/control.h --- bluez-4.101/audio/control.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/control.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define AUDIO_CONTROL_INTERFACE "org.bluez.Control" - -struct control *control_init(struct audio_device *dev, uint16_t uuid16); -void control_update(struct control *control, uint16_t uuid16); -void control_unregister(struct audio_device *dev); -gboolean control_is_active(struct audio_device *dev); diff -Nru bluez-4.101/audio/ctl_bluetooth.c bluez-5.23/audio/ctl_bluetooth.c --- bluez-4.101/audio/ctl_bluetooth.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/ctl_bluetooth.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,383 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include -#include - -#include -#include - -#include - -#include "ipc.h" - -#ifdef ENABLE_DEBUG -#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) -#else -#define DBG(fmt, arg...) -#endif - -#define BLUETOOTH_MINVOL 0 -#define BLUETOOTH_MAXVOL 15 - -struct bluetooth_data { - snd_ctl_ext_t ext; - int sock; -}; - -enum { - BLUETOOTH_PLAYBACK, - BLUETOOTH_CAPTURE, -}; - -static const char *vol_devices[2] = { - [BLUETOOTH_PLAYBACK] = "Playback volume", - [BLUETOOTH_CAPTURE] = "Capture volume", -}; - -static void bluetooth_exit(struct bluetooth_data *data) -{ - if (data == NULL) - return; - - if (data->sock >= 0) - bt_audio_service_close(data->sock); - - free(data); -} - -static void bluetooth_close(snd_ctl_ext_t *ext) -{ - struct bluetooth_data *data = ext->private_data; - - DBG("ext %p", ext); - - bluetooth_exit(data); -} - -static int bluetooth_elem_count(snd_ctl_ext_t *ext) -{ - DBG("ext %p", ext); - - return 2; -} - -static int bluetooth_elem_list(snd_ctl_ext_t *ext, - unsigned int offset, snd_ctl_elem_id_t *id) -{ - DBG("ext %p offset %d id %p", ext, offset, id); - - snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); - - if (offset > 1) - return -EINVAL; - - snd_ctl_elem_id_set_name(id, vol_devices[offset]); - - return 0; -} - -static snd_ctl_ext_key_t bluetooth_find_elem(snd_ctl_ext_t *ext, - const snd_ctl_elem_id_t *id) -{ - const char *name = snd_ctl_elem_id_get_name(id); - int i; - - DBG("ext %p id %p name '%s'", ext, id, name); - - for (i = 0; i < 2; i++) - if (strcmp(name, vol_devices[i]) == 0) - return i; - - return SND_CTL_EXT_KEY_NOT_FOUND; -} - -static int bluetooth_get_attribute(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, - int *type, unsigned int *acc, unsigned int *count) -{ - DBG("ext %p key %ld", ext, key); - - *type = SND_CTL_ELEM_TYPE_INTEGER; - *acc = SND_CTL_EXT_ACCESS_READWRITE; - *count = 1; - - return 0; -} - -static int bluetooth_get_integer_info(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, - long *imin, long *imax, long *istep) -{ - DBG("ext %p key %ld", ext, key); - - *istep = 1; - *imin = BLUETOOTH_MINVOL; - *imax = BLUETOOTH_MAXVOL; - - return 0; -} - -static int bluetooth_send_ctl(struct bluetooth_data *data, - uint8_t mode, uint8_t key, struct bt_control_rsp *rsp) -{ - int ret; - struct bt_control_req *req = (void *) rsp; - bt_audio_error_t *err = (void *) rsp; - const char *type, *name; - - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - req->h.type = BT_REQUEST; - req->h.name = BT_CONTROL; - req->h.length = sizeof(*req); - - req->mode = mode; - req->key = key; - - ret = send(data->sock, req, BT_SUGGESTED_BUFFER_SIZE, MSG_NOSIGNAL); - if (ret <= 0) { - SYSERR("Unable to request new volume value to server"); - return -errno; - } - - ret = recv(data->sock, rsp, BT_SUGGESTED_BUFFER_SIZE, 0); - if (ret <= 0) { - SNDERR("Unable to receive new volume value from server"); - return -errno; - } - - if (rsp->h.type == BT_ERROR) { - SNDERR("BT_CONTROL failed : %s (%d)", - strerror(err->posix_errno), - err->posix_errno); - return -err->posix_errno; - } - - type = bt_audio_strtype(rsp->h.type); - if (!type) { - SNDERR("Bogus message type %d " - "received from audio service", - rsp->h.type); - return -EINVAL; - } - - name = bt_audio_strname(rsp->h.name); - if (!name) { - SNDERR("Bogus message name %d " - "received from audio service", - rsp->h.name); - return -EINVAL; - } - - if (rsp->h.name != BT_CONTROL) { - SNDERR("Unexpected message %s received", type); - return -EINVAL; - } - - return 0; -} - -static int bluetooth_read_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, - long *value) -{ - struct bluetooth_data *data = ext->private_data; - int ret; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_control_rsp *rsp = (void *) buf; - - DBG("ext %p key %ld", ext, key); - - memset(buf, 0, sizeof(buf)); - *value = 0; - - ret = bluetooth_send_ctl(data, key, 0, rsp); - if (ret == 0) - *value = rsp->key; - - return ret; -} - -static int bluetooth_write_integer(snd_ctl_ext_t *ext, snd_ctl_ext_key_t key, - long *value) -{ - struct bluetooth_data *data = ext->private_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_control_rsp *rsp = (void *) buf; - long current; - int ret, keyvalue; - - DBG("ext %p key %ld", ext, key); - - ret = bluetooth_read_integer(ext, key, ¤t); - if (ret < 0) - return ret; - - if (*value == current) - return 0; - - while (*value != current) { - keyvalue = (*value > current) ? BT_CONTROL_KEY_VOL_UP : - BT_CONTROL_KEY_VOL_DOWN; - - ret = bluetooth_send_ctl(data, key, keyvalue, rsp); - if (ret < 0) - break; - - current = keyvalue; - } - - return ret; -} - -static int bluetooth_read_event(snd_ctl_ext_t *ext, snd_ctl_elem_id_t *id, - unsigned int *event_mask) -{ - struct bluetooth_data *data = ext->private_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_control_ind *ind = (void *) buf; - int err; - const char *type, *name; - - DBG("ext %p id %p", ext, id); - - memset(buf, 0, sizeof(buf)); - - err = recv(data->sock, ind, BT_SUGGESTED_BUFFER_SIZE, MSG_DONTWAIT); - if (err < 0) { - err = -errno; - SNDERR("Failed while receiving data: %s (%d)", strerror(-err), - -err); - return err; - } - - type = bt_audio_strtype(ind->h.type); - if (!type) { - SNDERR("Bogus message type %d " - "received from audio service", - ind->h.type); - return -EAGAIN; - } - - name = bt_audio_strname(ind->h.name); - if (!name) { - SNDERR("Bogus message name %d " - "received from audio service", - ind->h.name); - return -EAGAIN; - } - - if (ind->h.name != BT_CONTROL) { - SNDERR("Unexpected message %s received", name); - return -EAGAIN; - } - - snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); - snd_ctl_elem_id_set_name(id, ind->mode == BLUETOOTH_PLAYBACK ? - vol_devices[BLUETOOTH_PLAYBACK] : - vol_devices[BLUETOOTH_CAPTURE]); - *event_mask = SND_CTL_EVENT_MASK_VALUE; - - return 1; -} - -static snd_ctl_ext_callback_t bluetooth_callback = { - .close = bluetooth_close, - .elem_count = bluetooth_elem_count, - .elem_list = bluetooth_elem_list, - .find_elem = bluetooth_find_elem, - .get_attribute = bluetooth_get_attribute, - .get_integer_info = bluetooth_get_integer_info, - .read_integer = bluetooth_read_integer, - .write_integer = bluetooth_write_integer, - .read_event = bluetooth_read_event, -}; - -static int bluetooth_init(struct bluetooth_data *data) -{ - int sk; - - if (!data) - return -EINVAL; - - memset(data, 0, sizeof(struct bluetooth_data)); - - data->sock = -1; - - sk = bt_audio_service_open(); - if (sk < 0) - return -errno; - - data->sock = sk; - - return 0; -} - -SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth); - -SND_CTL_PLUGIN_DEFINE_FUNC(bluetooth) -{ - struct bluetooth_data *data; - int err; - - DBG("Bluetooth Control plugin"); - - data = malloc(sizeof(struct bluetooth_data)); - if (!data) { - err = -ENOMEM; - goto error; - } - - err = bluetooth_init(data); - if (err < 0) - goto error; - - data->ext.version = SND_CTL_EXT_VERSION; - data->ext.card_idx = -1; - - strncpy(data->ext.id, "bluetooth", sizeof(data->ext.id) - 1); - strncpy(data->ext.driver, "Bluetooth-Audio", sizeof(data->ext.driver) - 1); - strncpy(data->ext.name, "Bluetooth Audio", sizeof(data->ext.name) - 1); - strncpy(data->ext.longname, "Bluetooth Audio", sizeof(data->ext.longname) - 1); - strncpy(data->ext.mixername, "Bluetooth Audio", sizeof(data->ext.mixername) - 1); - - data->ext.callback = &bluetooth_callback; - data->ext.poll_fd = data->sock; - data->ext.private_data = data; - - err = snd_ctl_ext_create(&data->ext, name, mode); - if (err < 0) - goto error; - - *handlep = data->ext.handle; - - return 0; - -error: - bluetooth_exit(data); - - return err; -} - -SND_CTL_PLUGIN_SYMBOL(bluetooth); diff -Nru bluez-4.101/audio/device.c bluez-5.23/audio/device.c --- bluez-4.101/audio/device.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,872 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "log.h" -#include "../src/adapter.h" -#include "../src/device.h" - -#include "error.h" -#include "ipc.h" -#include "dbus-common.h" -#include "device.h" -#include "unix.h" -#include "avdtp.h" -#include "control.h" -#include "avctp.h" -#include "avrcp.h" -#include "headset.h" -#include "gateway.h" -#include "sink.h" -#include "source.h" - -#define AUDIO_INTERFACE "org.bluez.Audio" - -#define CONTROL_CONNECT_TIMEOUT 2 -#define AVDTP_CONNECT_TIMEOUT 1 -#define AVDTP_CONNECT_TIMEOUT_BOOST 1 -#define HEADSET_CONNECT_TIMEOUT 1 - -typedef enum { - AUDIO_STATE_DISCONNECTED, - AUDIO_STATE_CONNECTING, - AUDIO_STATE_CONNECTED, -} audio_state_t; - -struct service_auth { - service_auth_cb cb; - void *user_data; -}; - -struct dev_priv { - audio_state_t state; - - headset_state_t hs_state; - sink_state_t sink_state; - avctp_state_t avctp_state; - GSList *auths; - - DBusMessage *conn_req; - DBusMessage *dc_req; - - guint control_timer; - guint avdtp_timer; - guint headset_timer; - guint dc_id; - - gboolean disconnecting; - gboolean authorized; - guint auth_idle_id; -}; - -static unsigned int sink_callback_id = 0; -static unsigned int avctp_callback_id = 0; -static unsigned int avdtp_callback_id = 0; -static unsigned int headset_callback_id = 0; - -static void device_free(struct audio_device *dev) -{ - struct dev_priv *priv = dev->priv; - - if (dev->conn) - dbus_connection_unref(dev->conn); - - btd_device_unref(dev->btd_dev); - - if (priv) { - if (priv->auths) - audio_device_cancel_authorization(dev, NULL, NULL); - if (priv->control_timer) - g_source_remove(priv->control_timer); - if (priv->avdtp_timer) - g_source_remove(priv->avdtp_timer); - if (priv->headset_timer) - g_source_remove(priv->headset_timer); - if (priv->dc_req) - dbus_message_unref(priv->dc_req); - if (priv->conn_req) - dbus_message_unref(priv->conn_req); - if (priv->dc_id) - device_remove_disconnect_watch(dev->btd_dev, - priv->dc_id); - g_free(priv); - } - - g_free(dev->path); - g_free(dev); -} - -static const char *state2str(audio_state_t state) -{ - switch (state) { - case AUDIO_STATE_DISCONNECTED: - return "disconnected"; - case AUDIO_STATE_CONNECTING: - return "connecting"; - case AUDIO_STATE_CONNECTED: - return "connected"; - default: - error("Invalid audio state %d", state); - return NULL; - } -} - -static gboolean control_connect_timeout(gpointer user_data) -{ - struct audio_device *dev = user_data; - - dev->priv->control_timer = 0; - - if (dev->control) - avrcp_connect(dev); - - return FALSE; -} - -static gboolean device_set_control_timer(struct audio_device *dev) -{ - struct dev_priv *priv = dev->priv; - - if (!dev->control) - return FALSE; - - if (priv->control_timer) - return FALSE; - - priv->control_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT, - control_connect_timeout, - dev); - - return TRUE; -} - -static void device_remove_control_timer(struct audio_device *dev) -{ - if (dev->priv->control_timer) - g_source_remove(dev->priv->control_timer); - dev->priv->control_timer = 0; -} - -static void device_remove_avdtp_timer(struct audio_device *dev) -{ - if (dev->priv->avdtp_timer) - g_source_remove(dev->priv->avdtp_timer); - dev->priv->avdtp_timer = 0; -} - -static void device_remove_headset_timer(struct audio_device *dev) -{ - if (dev->priv->headset_timer) - g_source_remove(dev->priv->headset_timer); - dev->priv->headset_timer = 0; -} - -static void disconnect_cb(struct btd_device *btd_dev, gboolean removal, - void *user_data) -{ - struct audio_device *dev = user_data; - struct dev_priv *priv = dev->priv; - - if (priv->state == AUDIO_STATE_DISCONNECTED) - return; - - if (priv->disconnecting) - return; - - priv->disconnecting = TRUE; - - device_remove_control_timer(dev); - device_remove_avdtp_timer(dev); - device_remove_headset_timer(dev); - - if (dev->control) - avrcp_disconnect(dev); - - if (dev->sink && priv->sink_state != SINK_STATE_DISCONNECTED) - sink_shutdown(dev->sink); - else if (priv->hs_state != HEADSET_STATE_DISCONNECTED) - headset_shutdown(dev); - else - priv->disconnecting = FALSE; -} - -static void device_set_state(struct audio_device *dev, audio_state_t new_state) -{ - struct dev_priv *priv = dev->priv; - const char *state_str; - DBusMessage *reply = NULL; - - state_str = state2str(new_state); - if (!state_str) - return; - - if (new_state == AUDIO_STATE_DISCONNECTED) { - priv->authorized = FALSE; - - if (priv->dc_id) { - device_remove_disconnect_watch(dev->btd_dev, - priv->dc_id); - priv->dc_id = 0; - } - } else if (new_state == AUDIO_STATE_CONNECTING) - priv->dc_id = device_add_disconnect_watch(dev->btd_dev, - disconnect_cb, dev, NULL); - - if (dev->priv->state == new_state) { - DBG("state change attempted from %s to %s", - state_str, state_str); - return; - } - - dev->priv->state = new_state; - - if (new_state == AUDIO_STATE_DISCONNECTED) { - if (priv->dc_req) { - reply = dbus_message_new_method_return(priv->dc_req); - dbus_message_unref(priv->dc_req); - priv->dc_req = NULL; - g_dbus_send_message(dev->conn, reply); - } - priv->disconnecting = FALSE; - } - - if (priv->conn_req && new_state != AUDIO_STATE_CONNECTING) { - if (new_state == AUDIO_STATE_CONNECTED) - reply = dbus_message_new_method_return(priv->conn_req); - else - reply = btd_error_failed(priv->conn_req, - "Connect Failed"); - - dbus_message_unref(priv->conn_req); - priv->conn_req = NULL; - g_dbus_send_message(dev->conn, reply); - } - - emit_property_changed(dev->conn, dev->path, - AUDIO_INTERFACE, "State", - DBUS_TYPE_STRING, &state_str); -} - -static gboolean avdtp_connect_timeout(gpointer user_data) -{ - struct audio_device *dev = user_data; - - dev->priv->avdtp_timer = 0; - - if (dev->sink) { - struct avdtp *session = avdtp_get(&dev->src, &dev->dst); - - if (!session) - return FALSE; - - sink_setup_stream(dev->sink, session); - avdtp_unref(session); - } - - return FALSE; -} - -static gboolean device_set_avdtp_timer(struct audio_device *dev) -{ - struct dev_priv *priv = dev->priv; - guint timeout = AVDTP_CONNECT_TIMEOUT; - - if (!dev->sink) - return FALSE; - - if (priv->avdtp_timer) - return FALSE; - - /* If the headset is the HSP/HFP RFCOMM initiator, give the headset - * time to initiate AVDTP signalling (and avoid further racing) */ - if (dev->headset && headset_get_rfcomm_initiator(dev)) - timeout += AVDTP_CONNECT_TIMEOUT_BOOST; - - priv->avdtp_timer = g_timeout_add_seconds(timeout, - avdtp_connect_timeout, - dev); - - return TRUE; -} - -static gboolean headset_connect_timeout(gpointer user_data) -{ - struct audio_device *dev = user_data; - struct dev_priv *priv = dev->priv; - - dev->priv->headset_timer = 0; - - if (dev->headset == NULL) - return FALSE; - - if (headset_config_stream(dev, FALSE, NULL, NULL) == 0) { - if (priv->state != AUDIO_STATE_CONNECTED && - (priv->sink_state == SINK_STATE_CONNECTED || - priv->sink_state == SINK_STATE_PLAYING)) - device_set_state(dev, AUDIO_STATE_CONNECTED); - } - - return FALSE; -} - -static gboolean device_set_headset_timer(struct audio_device *dev) -{ - struct dev_priv *priv = dev->priv; - - if (!dev->headset) - return FALSE; - - if (priv->headset_timer) - return FALSE; - - priv->headset_timer = g_timeout_add_seconds(HEADSET_CONNECT_TIMEOUT, - headset_connect_timeout, dev); - - return TRUE; -} - -static void device_avdtp_cb(struct audio_device *dev, struct avdtp *session, - avdtp_session_state_t old_state, - avdtp_session_state_t new_state, - void *user_data) -{ - if (!dev->sink || !dev->control) - return; - - if (new_state == AVDTP_SESSION_STATE_CONNECTED) { - if (avdtp_stream_setup_active(session)) - device_set_control_timer(dev); - else - avrcp_connect(dev); - } -} - -static void device_sink_cb(struct audio_device *dev, - sink_state_t old_state, - sink_state_t new_state, - void *user_data) -{ - struct dev_priv *priv = dev->priv; - - if (!dev->sink) - return; - - priv->sink_state = new_state; - - switch (new_state) { - case SINK_STATE_DISCONNECTED: - if (dev->control) { - device_remove_control_timer(dev); - avrcp_disconnect(dev); - } - if (priv->hs_state != HEADSET_STATE_DISCONNECTED && - (priv->dc_req || priv->disconnecting)) { - headset_shutdown(dev); - break; - } - if (priv->hs_state == HEADSET_STATE_DISCONNECTED) - device_set_state(dev, AUDIO_STATE_DISCONNECTED); - else if (old_state == SINK_STATE_CONNECTING) { - switch (priv->hs_state) { - case HEADSET_STATE_CONNECTED: - case HEADSET_STATE_PLAY_IN_PROGRESS: - case HEADSET_STATE_PLAYING: - device_set_state(dev, AUDIO_STATE_CONNECTED); - default: - break; - } - } - break; - case SINK_STATE_CONNECTING: - device_remove_avdtp_timer(dev); - if (priv->hs_state == HEADSET_STATE_DISCONNECTED) - device_set_state(dev, AUDIO_STATE_CONNECTING); - break; - case SINK_STATE_CONNECTED: - if (old_state == SINK_STATE_PLAYING) - break; - if (dev->auto_connect) { - if (!dev->headset) - device_set_state(dev, AUDIO_STATE_CONNECTED); - else if (priv->hs_state == HEADSET_STATE_DISCONNECTED) - device_set_headset_timer(dev); - else if (priv->hs_state == HEADSET_STATE_CONNECTED || - priv->hs_state == HEADSET_STATE_PLAY_IN_PROGRESS || - priv->hs_state == HEADSET_STATE_PLAYING) - device_set_state(dev, AUDIO_STATE_CONNECTED); - } else if (priv->hs_state == HEADSET_STATE_DISCONNECTED || - priv->hs_state == HEADSET_STATE_CONNECTING) - device_set_state(dev, AUDIO_STATE_CONNECTED); - break; - case SINK_STATE_PLAYING: - break; - } -} - -static void device_avctp_cb(struct audio_device *dev, - avctp_state_t old_state, - avctp_state_t new_state, - void *user_data) -{ - if (!dev->control) - return; - - dev->priv->avctp_state = new_state; - - switch (new_state) { - case AVCTP_STATE_DISCONNECTED: - break; - case AVCTP_STATE_CONNECTING: - device_remove_control_timer(dev); - break; - case AVCTP_STATE_CONNECTED: - break; - } -} - -static void device_headset_cb(struct audio_device *dev, - headset_state_t old_state, - headset_state_t new_state, - void *user_data) -{ - struct dev_priv *priv = dev->priv; - - if (!dev->headset) - return; - - priv->hs_state = new_state; - - switch (new_state) { - case HEADSET_STATE_DISCONNECTED: - device_remove_avdtp_timer(dev); - if (priv->sink_state != SINK_STATE_DISCONNECTED && dev->sink && - (priv->dc_req || priv->disconnecting)) { - sink_shutdown(dev->sink); - break; - } - if (priv->sink_state == SINK_STATE_DISCONNECTED) - device_set_state(dev, AUDIO_STATE_DISCONNECTED); - else if (old_state == HEADSET_STATE_CONNECTING && - (priv->sink_state == SINK_STATE_CONNECTED || - priv->sink_state == SINK_STATE_PLAYING)) - device_set_state(dev, AUDIO_STATE_CONNECTED); - break; - case HEADSET_STATE_CONNECTING: - device_remove_headset_timer(dev); - if (priv->sink_state == SINK_STATE_DISCONNECTED) - device_set_state(dev, AUDIO_STATE_CONNECTING); - break; - case HEADSET_STATE_CONNECTED: - if (old_state == HEADSET_STATE_CONNECTED || - old_state == HEADSET_STATE_PLAY_IN_PROGRESS || - old_state == HEADSET_STATE_PLAYING) - break; - if (dev->auto_connect) { - if (!dev->sink) - device_set_state(dev, AUDIO_STATE_CONNECTED); - else if (priv->sink_state == SINK_STATE_DISCONNECTED) - device_set_avdtp_timer(dev); - else if (priv->sink_state == SINK_STATE_CONNECTED || - priv->sink_state == SINK_STATE_PLAYING) - device_set_state(dev, AUDIO_STATE_CONNECTED); - } else if (priv->sink_state == SINK_STATE_DISCONNECTED || - priv->sink_state == SINK_STATE_CONNECTING) - device_set_state(dev, AUDIO_STATE_CONNECTED); - break; - case HEADSET_STATE_PLAY_IN_PROGRESS: - break; - case HEADSET_STATE_PLAYING: - break; - } -} - -static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *dev = data; - struct dev_priv *priv = dev->priv; - - if (priv->state == AUDIO_STATE_CONNECTING) - return btd_error_in_progress(msg); - else if (priv->state == AUDIO_STATE_CONNECTED) - return btd_error_already_connected(msg); - - dev->auto_connect = TRUE; - - if (dev->headset) - headset_config_stream(dev, FALSE, NULL, NULL); - - if (priv->state != AUDIO_STATE_CONNECTING && dev->sink) { - struct avdtp *session = avdtp_get(&dev->src, &dev->dst); - - if (!session) - return btd_error_failed(msg, - "Failed to get AVDTP session"); - - sink_setup_stream(dev->sink, session); - avdtp_unref(session); - } - - /* The previous calls should cause a call to the state callback to - * indicate AUDIO_STATE_CONNECTING */ - if (priv->state != AUDIO_STATE_CONNECTING) - return btd_error_failed(msg, "Connect Failed"); - - priv->conn_req = dbus_message_ref(msg); - - return NULL; -} - -static DBusMessage *dev_disconnect(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *dev = data; - struct dev_priv *priv = dev->priv; - - if (priv->state == AUDIO_STATE_DISCONNECTED) - return btd_error_not_connected(msg); - - if (priv->dc_req) - return dbus_message_new_method_return(msg); - - priv->dc_req = dbus_message_ref(msg); - - if (dev->control) { - device_remove_control_timer(dev); - avrcp_disconnect(dev); - } - - if (dev->sink && priv->sink_state != SINK_STATE_DISCONNECTED) - sink_shutdown(dev->sink); - else if (priv->hs_state != HEADSET_STATE_DISCONNECTED) - headset_shutdown(dev); - else { - dbus_message_unref(priv->dc_req); - priv->dc_req = NULL; - return dbus_message_new_method_return(msg); - } - - return NULL; -} - -static DBusMessage *dev_get_properties(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter dict; - const char *state; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - /* State */ - state = state2str(device->priv->state); - if (state) - dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state); - - dbus_message_iter_close_container(&iter, &dict); - - return reply; -} - -static const GDBusMethodTable dev_methods[] = { - { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) }, - { GDBUS_METHOD("Disconnect", NULL, NULL, dev_disconnect) }, - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - dev_get_properties) }, - { } -}; - -static const GDBusSignalTable dev_signals[] = { - { GDBUS_SIGNAL("PropertyChanged", - GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { } -}; - -struct audio_device *audio_device_register(DBusConnection *conn, - struct btd_device *device, - const char *path, const bdaddr_t *src, - const bdaddr_t *dst) -{ - struct audio_device *dev; - - if (!conn || !path) - return NULL; - - dev = g_new0(struct audio_device, 1); - - dev->btd_dev = btd_device_ref(device); - dev->path = g_strdup(path); - bacpy(&dev->dst, dst); - bacpy(&dev->src, src); - dev->conn = dbus_connection_ref(conn); - dev->priv = g_new0(struct dev_priv, 1); - dev->priv->state = AUDIO_STATE_DISCONNECTED; - - if (!g_dbus_register_interface(dev->conn, dev->path, - AUDIO_INTERFACE, - dev_methods, dev_signals, NULL, - dev, NULL)) { - error("Unable to register %s on %s", AUDIO_INTERFACE, - dev->path); - device_free(dev); - return NULL; - } - - DBG("Registered interface %s on path %s", AUDIO_INTERFACE, - dev->path); - - if (sink_callback_id == 0) - sink_callback_id = sink_add_state_cb(device_sink_cb, NULL); - - if (avdtp_callback_id == 0) - avdtp_callback_id = avdtp_add_state_cb(device_avdtp_cb, NULL); - if (avctp_callback_id == 0) - avctp_callback_id = avctp_add_state_cb(device_avctp_cb, NULL); - - if (headset_callback_id == 0) - headset_callback_id = headset_add_state_cb(device_headset_cb, - NULL); - - return dev; -} - -gboolean audio_device_is_active(struct audio_device *dev, - const char *interface) -{ - if (!interface) { - if ((dev->sink || dev->source) && - avdtp_is_connected(&dev->src, &dev->dst)) - return TRUE; - if (dev->headset && headset_is_active(dev)) - return TRUE; - } else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink && - avdtp_is_connected(&dev->src, &dev->dst)) - return TRUE; - else if (!strcmp(interface, AUDIO_SOURCE_INTERFACE) && dev->source && - avdtp_is_connected(&dev->src, &dev->dst)) - return TRUE; - else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset && - headset_is_active(dev)) - return TRUE; - else if (!strcmp(interface, AUDIO_CONTROL_INTERFACE) && dev->control && - control_is_active(dev)) - return TRUE; - else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway && - gateway_is_active(dev)) - return TRUE; - - return FALSE; -} - -void audio_device_unregister(struct audio_device *device) -{ - unix_device_removed(device); - - if (device->hs_preauth_id) { - g_source_remove(device->hs_preauth_id); - device->hs_preauth_id = 0; - } - - if (device->headset) - headset_unregister(device); - - if (device->gateway) - gateway_unregister(device); - - if (device->sink) - sink_unregister(device); - - if (device->source) - source_unregister(device); - - if (device->control) - control_unregister(device); - - g_dbus_unregister_interface(device->conn, device->path, - AUDIO_INTERFACE); - - device_free(device); -} - -static void auth_cb(DBusError *derr, void *user_data) -{ - struct audio_device *dev = user_data; - struct dev_priv *priv = dev->priv; - - if (derr == NULL) - priv->authorized = TRUE; - - while (priv->auths) { - struct service_auth *auth = priv->auths->data; - - auth->cb(derr, auth->user_data); - priv->auths = g_slist_remove(priv->auths, auth); - g_free(auth); - } -} - -static gboolean auth_idle_cb(gpointer user_data) -{ - struct audio_device *dev = user_data; - struct dev_priv *priv = dev->priv; - - priv->auth_idle_id = 0; - - auth_cb(NULL, dev); - - return FALSE; -} - -static gboolean audio_device_is_connected(struct audio_device *dev) -{ - if (dev->headset) { - headset_state_t state = headset_get_state(dev); - - if (state == HEADSET_STATE_CONNECTED || - state == HEADSET_STATE_PLAY_IN_PROGRESS || - state == HEADSET_STATE_PLAYING) - return TRUE; - } - - if (dev->sink) { - sink_state_t state = sink_get_state(dev); - - if (state == SINK_STATE_CONNECTED || - state == SINK_STATE_PLAYING) - return TRUE; - } - - if (dev->source) { - source_state_t state = source_get_state(dev); - - if (state == SOURCE_STATE_CONNECTED || - state == SOURCE_STATE_PLAYING) - return TRUE; - } - - return FALSE; -} - -int audio_device_request_authorization(struct audio_device *dev, - const char *uuid, service_auth_cb cb, - void *user_data) -{ - struct dev_priv *priv = dev->priv; - struct service_auth *auth; - int err; - - auth = g_try_new0(struct service_auth, 1); - if (!auth) - return -ENOMEM; - - auth->cb = cb; - auth->user_data = user_data; - - priv->auths = g_slist_append(priv->auths, auth); - if (g_slist_length(priv->auths) > 1) - return 0; - - if (priv->authorized || audio_device_is_connected(dev)) { - priv->auth_idle_id = g_idle_add(auth_idle_cb, dev); - return 0; - } - - err = btd_request_authorization(&dev->src, &dev->dst, uuid, auth_cb, - dev); - if (err < 0) { - priv->auths = g_slist_remove(priv->auths, auth); - g_free(auth); - } - - return err; -} - -int audio_device_cancel_authorization(struct audio_device *dev, - authorization_cb cb, void *user_data) -{ - struct dev_priv *priv = dev->priv; - GSList *l, *next; - - for (l = priv->auths; l != NULL; l = next) { - struct service_auth *auth = l->data; - - next = g_slist_next(l); - - if (cb && auth->cb != cb) - continue; - - if (user_data && auth->user_data != user_data) - continue; - - priv->auths = g_slist_remove(priv->auths, auth); - g_free(auth); - } - - if (g_slist_length(priv->auths) == 0) { - if (priv->auth_idle_id > 0) { - g_source_remove(priv->auth_idle_id); - priv->auth_idle_id = 0; - } else - btd_cancel_authorization(&dev->src, &dev->dst); - } - - return 0; -} - -void audio_device_set_authorized(struct audio_device *dev, gboolean auth) -{ - struct dev_priv *priv = dev->priv; - - priv->authorized = auth; -} diff -Nru bluez-4.101/audio/device.h bluez-5.23/audio/device.h --- bluez-4.101/audio/device.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -struct source; -struct control; -struct target; -struct sink; -struct headset; -struct gateway; -struct dev_priv; - -struct audio_device { - struct btd_device *btd_dev; - - DBusConnection *conn; - char *path; - bdaddr_t src; - bdaddr_t dst; - - gboolean auto_connect; - - struct headset *headset; - struct gateway *gateway; - struct sink *sink; - struct source *source; - struct control *control; - struct target *target; - - guint hs_preauth_id; - - struct dev_priv *priv; -}; - -struct audio_device *audio_device_register(DBusConnection *conn, - struct btd_device *device, - const char *path, const bdaddr_t *src, - const bdaddr_t *dst); - -void audio_device_unregister(struct audio_device *device); - -gboolean audio_device_is_active(struct audio_device *dev, - const char *interface); - -typedef void (*authorization_cb) (DBusError *derr, void *user_data); - -int audio_device_cancel_authorization(struct audio_device *dev, - authorization_cb cb, void *user_data); - -int audio_device_request_authorization(struct audio_device *dev, - const char *uuid, authorization_cb cb, - void *user_data); - -void audio_device_set_authorized(struct audio_device *dev, gboolean auth); diff -Nru bluez-4.101/audio/gateway.c bluez-5.23/audio/gateway.c --- bluez-4.101/audio/gateway.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gateway.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,997 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * Copyright (C) 2008-2009 Leonid Movshovich - * Copyright (C) 2010 ProFUSION embedded systems - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "sdp-client.h" -#include "device.h" -#include "gateway.h" -#include "log.h" -#include "error.h" -#include "btio.h" -#include "dbus-common.h" - -struct hf_agent { - char *name; /* Bus id */ - char *path; /* D-Bus path */ - guint watch; /* Disconnect watch */ -}; - -struct connect_cb { - unsigned int id; - gateway_stream_cb_t cb; - void *cb_data; -}; - -struct gateway { - gateway_state_t state; - GIOChannel *rfcomm; - GIOChannel *sco; - GIOChannel *incoming; - GSList *callbacks; - struct hf_agent *agent; - DBusMessage *msg; - int version; - gateway_lock_t lock; -}; - -struct gateway_state_callback { - gateway_state_cb cb; - void *user_data; - unsigned int id; -}; - -static GSList *gateway_callbacks = NULL; - -int gateway_close(struct audio_device *device); - -GQuark gateway_error_quark(void) -{ - return g_quark_from_static_string("gateway-error-quark"); -} - -static const char *state2str(gateway_state_t state) -{ - switch (state) { - case GATEWAY_STATE_DISCONNECTED: - return "disconnected"; - case GATEWAY_STATE_CONNECTING: - return "connecting"; - case GATEWAY_STATE_CONNECTED: - return "connected"; - case GATEWAY_STATE_PLAYING: - return "playing"; - default: - return ""; - } -} - -static void agent_free(struct hf_agent *agent) -{ - if (!agent) - return; - - g_free(agent->name); - g_free(agent->path); - g_free(agent); -} - -static void change_state(struct audio_device *dev, gateway_state_t new_state) -{ - struct gateway *gw = dev->gateway; - const char *val; - GSList *l; - gateway_state_t old_state; - - if (gw->state == new_state) - return; - - val = state2str(new_state); - old_state = gw->state; - gw->state = new_state; - - emit_property_changed(dev->conn, dev->path, - AUDIO_GATEWAY_INTERFACE, "State", - DBUS_TYPE_STRING, &val); - - for (l = gateway_callbacks; l != NULL; l = l->next) { - struct gateway_state_callback *cb = l->data; - cb->cb(dev, old_state, new_state, cb->user_data); - } -} - -void gateway_set_state(struct audio_device *dev, gateway_state_t new_state) -{ - switch (new_state) { - case GATEWAY_STATE_DISCONNECTED: - gateway_close(dev); - break; - case GATEWAY_STATE_CONNECTING: - case GATEWAY_STATE_CONNECTED: - case GATEWAY_STATE_PLAYING: - break; - } -} - -static void agent_disconnect(struct audio_device *dev, struct hf_agent *agent) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(agent->name, agent->path, - "org.bluez.HandsfreeAgent", "Release"); - - g_dbus_send_message(dev->conn, msg); -} - -static gboolean agent_sendfd(struct hf_agent *agent, int fd, - DBusPendingCallNotifyFunction notify, void *data) -{ - struct audio_device *dev = data; - struct gateway *gw = dev->gateway; - DBusMessage *msg; - DBusPendingCall *call; - - msg = dbus_message_new_method_call(agent->name, agent->path, - "org.bluez.HandsfreeAgent", "NewConnection"); - - dbus_message_append_args(msg, DBUS_TYPE_UNIX_FD, &fd, - DBUS_TYPE_UINT16, &gw->version, - DBUS_TYPE_INVALID); - - if (dbus_connection_send_with_reply(dev->conn, msg, - &call, -1) == FALSE) { - dbus_message_unref(msg); - return FALSE; - } - - dbus_pending_call_set_notify(call, notify, dev, NULL); - dbus_pending_call_unref(call); - dbus_message_unref(msg); - - return TRUE; -} - -static unsigned int connect_cb_new(struct gateway *gw, - gateway_stream_cb_t func, - void *user_data) -{ - struct connect_cb *cb; - static unsigned int free_cb_id = 1; - - if (!func) - return 0; - - cb = g_new(struct connect_cb, 1); - - cb->cb = func; - cb->cb_data = user_data; - cb->id = free_cb_id++; - - gw->callbacks = g_slist_append(gw->callbacks, cb); - - return cb->id; -} - -static void run_connect_cb(struct audio_device *dev, GError *err) -{ - struct gateway *gw = dev->gateway; - GSList *l; - - for (l = gw->callbacks; l != NULL; l = l->next) { - struct connect_cb *cb = l->data; - cb->cb(dev, err, cb->cb_data); - } - - g_slist_free_full(gw->callbacks, g_free); - gw->callbacks = NULL; -} - -static gboolean sco_io_cb(GIOChannel *chan, GIOCondition cond, - struct audio_device *dev) -{ - struct gateway *gw = dev->gateway; - - if (cond & G_IO_NVAL) - return FALSE; - - DBG("sco connection is released"); - g_io_channel_shutdown(gw->sco, TRUE, NULL); - g_io_channel_unref(gw->sco); - gw->sco = NULL; - change_state(dev, GATEWAY_STATE_CONNECTED); - - return FALSE; -} - -static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) -{ - struct audio_device *dev = (struct audio_device *) user_data; - struct gateway *gw = dev->gateway; - - DBG("at the begin of sco_connect_cb() in gateway.c"); - - gw->sco = g_io_channel_ref(chan); - - if (err) { - error("sco_connect_cb(): %s", err->message); - gateway_suspend_stream(dev); - return; - } - - g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) sco_io_cb, dev); - - change_state(dev, GATEWAY_STATE_PLAYING); - run_connect_cb(dev, NULL); -} - -static gboolean rfcomm_disconnect_cb(GIOChannel *chan, GIOCondition cond, - struct audio_device *dev) -{ - if (cond & G_IO_NVAL) - return FALSE; - - gateway_close(dev); - - return FALSE; -} - -static void newconnection_reply(DBusPendingCall *call, void *data) -{ - struct audio_device *dev = data; - struct gateway *gw = dev->gateway; - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError derr; - - if (!dev->gateway->rfcomm) { - DBG("RFCOMM disconnected from server before agent reply"); - goto done; - } - - dbus_error_init(&derr); - if (!dbus_set_error_from_message(&derr, reply)) { - DBG("Agent reply: file descriptor passed successfully"); - g_io_add_watch(gw->rfcomm, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) rfcomm_disconnect_cb, dev); - change_state(dev, GATEWAY_STATE_CONNECTED); - goto done; - } - - DBG("Agent reply: %s", derr.message); - - dbus_error_free(&derr); - gateway_close(dev); - -done: - dbus_message_unref(reply); -} - -static void rfcomm_connect_cb(GIOChannel *chan, GError *err, - gpointer user_data) -{ - struct audio_device *dev = user_data; - struct gateway *gw = dev->gateway; - DBusMessage *reply; - int sk, ret; - - if (err) { - error("connect(): %s", err->message); - goto fail; - } - - if (!gw->agent) { - error("Handsfree Agent not registered"); - goto fail; - } - - sk = g_io_channel_unix_get_fd(chan); - - if (gw->rfcomm == NULL) - gw->rfcomm = g_io_channel_ref(chan); - - ret = agent_sendfd(gw->agent, sk, newconnection_reply, dev); - - if (!gw->msg) - return; - - if (ret) - reply = dbus_message_new_method_return(gw->msg); - else - reply = btd_error_failed(gw->msg, "Can't pass file descriptor"); - - g_dbus_send_message(dev->conn, reply); - - return; - -fail: - if (gw->msg) { - DBusMessage *reply; - reply = btd_error_failed(gw->msg, "Connect failed"); - g_dbus_send_message(dev->conn, reply); - } - - gateway_close(dev); -} - -static int get_remote_profile_version(sdp_record_t *rec) -{ - uuid_t uuid; - sdp_list_t *profiles; - sdp_profile_desc_t *desc; - int ver = 0; - - sdp_uuid16_create(&uuid, HANDSFREE_PROFILE_ID); - - sdp_get_profile_descs(rec, &profiles); - if (profiles == NULL) - goto done; - - desc = profiles->data; - - if (sdp_uuid16_cmp(&desc->uuid, &uuid) == 0) - ver = desc->version; - - sdp_list_free(profiles, free); - -done: - return ver; -} - -static void get_incoming_record_cb(sdp_list_t *recs, int err, - gpointer user_data) -{ - struct audio_device *dev = user_data; - struct gateway *gw = dev->gateway; - GError *gerr = NULL; - - if (err < 0) { - error("Unable to get service record: %s (%d)", strerror(-err), - -err); - goto fail; - } - - if (!recs || !recs->data) { - error("No records found"); - goto fail; - } - - gw->version = get_remote_profile_version(recs->data); - if (gw->version == 0) - goto fail; - - rfcomm_connect_cb(gw->incoming, gerr, dev); - return; - -fail: - gateway_close(dev); -} - -static void unregister_incoming(gpointer user_data) -{ - struct audio_device *dev = user_data; - struct gateway *gw = dev->gateway; - - if (gw->incoming) { - g_io_channel_unref(gw->incoming); - gw->incoming = NULL; - } -} - -static void rfcomm_incoming_cb(GIOChannel *chan, GError *err, - gpointer user_data) -{ - struct audio_device *dev = user_data; - struct gateway *gw = dev->gateway; - uuid_t uuid; - - gw->incoming = g_io_channel_ref(chan); - - sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID); - if (bt_search_service(&dev->src, &dev->dst, &uuid, - get_incoming_record_cb, dev, - unregister_incoming) == 0) - return; - - unregister_incoming(dev); - gateway_close(dev); -} - -static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) -{ - struct audio_device *dev = user_data; - struct gateway *gw = dev->gateway; - int ch; - sdp_list_t *protos, *classes; - uuid_t uuid; - GIOChannel *io; - GError *gerr = NULL; - - if (err < 0) { - error("Unable to get service record: %s (%d)", strerror(-err), - -err); - goto fail; - } - - if (!recs || !recs->data) { - error("No records found"); - err = -EIO; - goto fail; - } - - if (sdp_get_service_classes(recs->data, &classes) < 0) { - error("Unable to get service classes from record"); - err = -EINVAL; - goto fail; - } - - if (sdp_get_access_protos(recs->data, &protos) < 0) { - error("Unable to get access protocols from record"); - err = -ENODATA; - goto fail; - } - - gw->version = get_remote_profile_version(recs->data); - if (gw->version == 0) { - error("Unable to get profile version from record"); - err = -EINVAL; - goto fail; - } - - memcpy(&uuid, classes->data, sizeof(uuid)); - sdp_list_free(classes, free); - - if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 || - uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) { - sdp_list_free(protos, NULL); - error("Invalid service record or not HFP"); - err = -EIO; - goto fail; - } - - ch = sdp_get_proto_port(protos, RFCOMM_UUID); - sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); - sdp_list_free(protos, NULL); - if (ch <= 0) { - error("Unable to extract RFCOMM channel from service record"); - err = -EIO; - goto fail; - } - - io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, dev, NULL, &gerr, - BT_IO_OPT_SOURCE_BDADDR, &dev->src, - BT_IO_OPT_DEST_BDADDR, &dev->dst, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_CHANNEL, ch, - BT_IO_OPT_INVALID); - if (!io) { - error("Unable to connect: %s", gerr->message); - goto fail; - } - - g_io_channel_unref(io); - return; - -fail: - if (gw->msg) { - DBusMessage *reply = btd_error_failed(gw->msg, - gerr ? gerr->message : strerror(-err)); - g_dbus_send_message(dev->conn, reply); - } - - gateway_close(dev); - - if (gerr) - g_error_free(gerr); -} - -static int get_records(struct audio_device *device) -{ - uuid_t uuid; - - change_state(device, GATEWAY_STATE_CONNECTING); - sdp_uuid16_create(&uuid, HANDSFREE_AGW_SVCLASS_ID); - return bt_search_service(&device->src, &device->dst, &uuid, - get_record_cb, device, NULL); -} - -static DBusMessage *ag_connect(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *au_dev = (struct audio_device *) data; - struct gateway *gw = au_dev->gateway; - int err; - - if (!gw->agent) - return btd_error_agent_not_available(msg); - - err = get_records(au_dev); - if (err < 0) - return btd_error_failed(msg, strerror(-err)); - - gw->msg = dbus_message_ref(msg); - - return NULL; -} - -int gateway_close(struct audio_device *device) -{ - GError *gerr = NULL; - struct gateway *gw = device->gateway; - int sock; - - if (gw->rfcomm) { - sock = g_io_channel_unix_get_fd(gw->rfcomm); - shutdown(sock, SHUT_RDWR); - - g_io_channel_shutdown(gw->rfcomm, TRUE, NULL); - g_io_channel_unref(gw->rfcomm); - gw->rfcomm = NULL; - } - - if (gw->sco) { - g_io_channel_shutdown(gw->sco, TRUE, NULL); - g_io_channel_unref(gw->sco); - gw->sco = NULL; - } - - change_state(device, GATEWAY_STATE_DISCONNECTED); - g_set_error(&gerr, GATEWAY_ERROR, - GATEWAY_ERROR_DISCONNECTED, "Disconnected"); - run_connect_cb(device, gerr); - g_error_free(gerr); - - return 0; -} - -static DBusMessage *ag_disconnect(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct gateway *gw = device->gateway; - DBusMessage *reply = NULL; - char gw_addr[18]; - - if (!device->conn) - return NULL; - - if (!gw->rfcomm) - return btd_error_not_connected(msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - gateway_close(device); - ba2str(&device->dst, gw_addr); - DBG("Disconnected from %s, %s", gw_addr, device->path); - - return reply; -} - -static void agent_exited(DBusConnection *conn, void *data) -{ - struct gateway *gateway = data; - struct hf_agent *agent = gateway->agent; - - DBG("Agent %s exited", agent->name); - - agent_free(agent); - gateway->agent = NULL; -} - -static DBusMessage *ag_get_properties(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct gateway *gw = device->gateway; - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter dict; - const char *value; - - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - value = state2str(gw->state); - dict_append_entry(&dict, "State", - DBUS_TYPE_STRING, &value); - - dbus_message_iter_close_container(&iter, &dict); - - return reply; -} - -static DBusMessage *register_agent(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - struct gateway *gw = device->gateway; - struct hf_agent *agent; - const char *path, *name; - - if (gw->agent) - return btd_error_already_exists(msg); - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - name = dbus_message_get_sender(msg); - agent = g_new0(struct hf_agent, 1); - - agent->name = g_strdup(name); - agent->path = g_strdup(path); - - agent->watch = g_dbus_add_disconnect_watch(conn, name, - agent_exited, gw, NULL); - - gw->agent = agent; - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *unregister_agent(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - struct gateway *gw = device->gateway; - const char *path; - - if (!gw->agent) - goto done; - - if (strcmp(gw->agent->name, dbus_message_get_sender(msg)) != 0) - return btd_error_not_authorized(msg); - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - if (strcmp(gw->agent->path, path) != 0) - return btd_error_does_not_exist(msg); - - g_dbus_remove_watch(device->conn, gw->agent->watch); - - agent_free(gw->agent); - gw->agent = NULL; - -done: - return dbus_message_new_method_return(msg); -} - -static const GDBusMethodTable gateway_methods[] = { - { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, ag_connect) }, - { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, ag_disconnect) }, - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - ag_get_properties) }, - { GDBUS_METHOD("RegisterAgent", - GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) }, - { GDBUS_METHOD("UnregisterAgent", - GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) }, - { } -}; - -static const GDBusSignalTable gateway_signals[] = { - { GDBUS_SIGNAL("PropertyChanged", - GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { } -}; - -static void path_unregister(void *data) -{ - struct audio_device *dev = data; - - DBG("Unregistered interface %s on path %s", - AUDIO_GATEWAY_INTERFACE, dev->path); - - gateway_close(dev); - - g_free(dev->gateway); - dev->gateway = NULL; -} - -void gateway_unregister(struct audio_device *dev) -{ - if (dev->gateway->agent) - agent_disconnect(dev, dev->gateway->agent); - - g_dbus_unregister_interface(dev->conn, dev->path, - AUDIO_GATEWAY_INTERFACE); -} - -struct gateway *gateway_init(struct audio_device *dev) -{ - if (!g_dbus_register_interface(dev->conn, dev->path, - AUDIO_GATEWAY_INTERFACE, - gateway_methods, gateway_signals, - NULL, dev, path_unregister)) - return NULL; - - return g_new0(struct gateway, 1); -} - -gboolean gateway_is_connected(struct audio_device *dev) -{ - struct gateway *gw = dev->gateway; - - if (gw->state == GATEWAY_STATE_CONNECTED) - return TRUE; - - return FALSE; -} - -gboolean gateway_is_active(struct audio_device *dev) -{ - struct gateway *gw = dev->gateway; - - if (gw->state != GATEWAY_STATE_DISCONNECTED) - return TRUE; - - return FALSE; -} - -int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io) -{ - if (!io) - return -EINVAL; - - dev->gateway->rfcomm = g_io_channel_ref(io); - - change_state(dev, GATEWAY_STATE_CONNECTING); - - return 0; -} - -int gateway_connect_sco(struct audio_device *dev, GIOChannel *io) -{ - struct gateway *gw = dev->gateway; - - if (gw->sco) - return -EISCONN; - - gw->sco = g_io_channel_ref(io); - - g_io_add_watch(gw->sco, G_IO_ERR | G_IO_HUP | G_IO_NVAL, - (GIOFunc) sco_io_cb, dev); - - change_state(dev, GATEWAY_STATE_PLAYING); - - return 0; -} - -void gateway_start_service(struct audio_device *dev) -{ - struct gateway *gw = dev->gateway; - GError *err = NULL; - - if (gw->rfcomm == NULL) - return; - - if (!bt_io_accept(gw->rfcomm, rfcomm_incoming_cb, dev, NULL, &err)) { - error("bt_io_accept: %s", err->message); - g_error_free(err); - gateway_close(dev); - } -} - -static gboolean request_stream_cb(gpointer data) -{ - run_connect_cb(data, NULL); - return FALSE; -} - -/* These are functions to be called from unix.c for audio system - * ifaces (alsa, gstreamer, etc.) */ -unsigned int gateway_request_stream(struct audio_device *dev, - gateway_stream_cb_t cb, void *user_data) -{ - struct gateway *gw = dev->gateway; - GError *err = NULL; - GIOChannel *io; - - if (!gw->rfcomm) - get_records(dev); - else if (!gw->sco) { - io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &dev->src, - BT_IO_OPT_DEST_BDADDR, &dev->dst, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_INVALID); - if (!io) { - error("%s", err->message); - g_error_free(err); - return 0; - } - } else - g_idle_add(request_stream_cb, dev); - - return connect_cb_new(gw, cb, user_data); -} - -int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t cb, - void *user_data) -{ - struct gateway *gw = dev->gateway; - unsigned int id; - - id = connect_cb_new(gw, cb, user_data); - - if (!gw->rfcomm) - get_records(dev); - else if (cb) - g_idle_add(request_stream_cb, dev); - - return id; -} - -gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id) -{ - struct gateway *gw = dev->gateway; - GSList *l; - struct connect_cb *cb = NULL; - - for (l = gw->callbacks; l != NULL; l = l->next) { - struct connect_cb *tmp = l->data; - - if (tmp->id == id) { - cb = tmp; - break; - } - } - - if (!cb) - return FALSE; - - gw->callbacks = g_slist_remove(gw->callbacks, cb); - g_free(cb); - - gateway_suspend_stream(dev); - - return TRUE; -} - -int gateway_get_sco_fd(struct audio_device *dev) -{ - struct gateway *gw = dev->gateway; - - if (!gw || !gw->sco) - return -1; - - return g_io_channel_unix_get_fd(gw->sco); -} - -void gateway_suspend_stream(struct audio_device *dev) -{ - GError *gerr = NULL; - struct gateway *gw = dev->gateway; - - if (!gw || !gw->sco) - return; - - g_io_channel_shutdown(gw->sco, TRUE, NULL); - g_io_channel_unref(gw->sco); - gw->sco = NULL; - g_set_error(&gerr, GATEWAY_ERROR, GATEWAY_ERROR_SUSPENDED, "Suspended"); - run_connect_cb(dev, gerr); - g_error_free(gerr); - change_state(dev, GATEWAY_STATE_CONNECTED); -} - -unsigned int gateway_add_state_cb(gateway_state_cb cb, void *user_data) -{ - struct gateway_state_callback *state_cb; - static unsigned int id = 0; - - state_cb = g_new(struct gateway_state_callback, 1); - state_cb->cb = cb; - state_cb->user_data = user_data; - state_cb->id = ++id; - - gateway_callbacks = g_slist_append(gateway_callbacks, state_cb); - - return state_cb->id; -} - -gboolean gateway_remove_state_cb(unsigned int id) -{ - GSList *l; - - for (l = gateway_callbacks; l != NULL; l = l->next) { - struct gateway_state_callback *cb = l->data; - if (cb && cb->id == id) { - gateway_callbacks = g_slist_remove(gateway_callbacks, - cb); - g_free(cb); - return TRUE; - } - } - - return FALSE; -} - -gateway_lock_t gateway_get_lock(struct audio_device *dev) -{ - struct gateway *gw = dev->gateway; - - return gw->lock; -} - -gboolean gateway_lock(struct audio_device *dev, gateway_lock_t lock) -{ - struct gateway *gw = dev->gateway; - - if (gw->lock & lock) - return FALSE; - - gw->lock |= lock; - - return TRUE; -} - -gboolean gateway_unlock(struct audio_device *dev, gateway_lock_t lock) -{ - struct gateway *gw = dev->gateway; - - if (!(gw->lock & lock)) - return FALSE; - - gw->lock &= ~lock; - - if (gw->lock) - return TRUE; - - if (gw->state == GATEWAY_STATE_PLAYING) - gateway_suspend_stream(dev); - - return TRUE; -} diff -Nru bluez-4.101/audio/gateway.h bluez-5.23/audio/gateway.h --- bluez-4.101/audio/gateway.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gateway.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define AUDIO_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway" - -#define DEFAULT_HFP_HS_CHANNEL 7 - -typedef enum { - GATEWAY_STATE_DISCONNECTED, - GATEWAY_STATE_CONNECTING, - GATEWAY_STATE_CONNECTED, - GATEWAY_STATE_PLAYING, -} gateway_state_t; - -typedef enum { - GATEWAY_LOCK_READ = 1, - GATEWAY_LOCK_WRITE = 1 << 1, -} gateway_lock_t; - -typedef enum { - GATEWAY_ERROR_DISCONNECTED, - GATEWAY_ERROR_SUSPENDED, -} gateway_error_t; - -#define GATEWAY_ERROR gateway_error_quark() - -GQuark gateway_error_quark(void); - -typedef void (*gateway_state_cb) (struct audio_device *dev, - gateway_state_t old_state, - gateway_state_t new_state, - void *user_data); -typedef void (*gateway_stream_cb_t) (struct audio_device *dev, GError *err, - void *user_data); - -void gateway_set_state(struct audio_device *dev, gateway_state_t new_state); -void gateway_unregister(struct audio_device *dev); -struct gateway *gateway_init(struct audio_device *device); -gboolean gateway_is_active(struct audio_device *dev); -gboolean gateway_is_connected(struct audio_device *dev); -int gateway_connect_rfcomm(struct audio_device *dev, GIOChannel *io); -int gateway_connect_sco(struct audio_device *dev, GIOChannel *chan); -void gateway_start_service(struct audio_device *device); -unsigned int gateway_request_stream(struct audio_device *dev, - gateway_stream_cb_t cb, void *user_data); -int gateway_config_stream(struct audio_device *dev, gateway_stream_cb_t cb, - void *user_data); -gboolean gateway_cancel_stream(struct audio_device *dev, unsigned int id); -int gateway_get_sco_fd(struct audio_device *dev); -void gateway_suspend_stream(struct audio_device *dev); -unsigned int gateway_add_state_cb(gateway_state_cb cb, void *user_data); -gboolean gateway_remove_state_cb(unsigned int id); -gateway_lock_t gateway_get_lock(struct audio_device *dev); -gboolean gateway_lock(struct audio_device *dev, gateway_lock_t lock); -gboolean gateway_unlock(struct audio_device *dev, gateway_lock_t lock); diff -Nru bluez-4.101/audio/gsta2dpsink.c bluez-5.23/audio/gsta2dpsink.c --- bluez-4.101/audio/gsta2dpsink.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gsta2dpsink.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,729 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include -#include - -#include "gstpragma.h" -#include "gsta2dpsink.h" - -GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug); -#define GST_CAT_DEFAULT gst_a2dp_sink_debug - -#define A2DP_SBC_RTP_PAYLOAD_TYPE 1 -#define TEMPLATE_MAX_BITPOOL_STR "64" - -#define DEFAULT_AUTOCONNECT TRUE - -enum { - PROP_0, - PROP_DEVICE, - PROP_AUTOCONNECT, - PROP_TRANSPORT -}; - -GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN); - -static const GstElementDetails gst_a2dp_sink_details = - GST_ELEMENT_DETAILS("Bluetooth A2DP sink", - "Sink/Audio", - "Plays audio to an A2DP device", - "Marcel Holtmann "); - -static GstStaticPadTemplate gst_a2dp_sink_factory = - GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc, " - "rate = (int) { 16000, 32000, 44100, 48000 }, " - "channels = (int) [ 1, 2 ], " - "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, " - "blocks = (int) { 4, 8, 12, 16 }, " - "subbands = (int) { 4, 8 }, " - "allocation = (string) { \"snr\", \"loudness\" }, " - "bitpool = (int) [ 2, " - TEMPLATE_MAX_BITPOOL_STR " ]; " - "audio/mpeg" - )); - -static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event); -static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps); -static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad); -static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self); -static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self); -static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self); - -static void gst_a2dp_sink_finalize(GObject *obj) -{ - GstA2dpSink *self = GST_A2DP_SINK(obj); - - g_mutex_free(self->cb_mutex); - - G_OBJECT_CLASS(parent_class)->finalize(obj); -} - -static GstState gst_a2dp_sink_get_state(GstA2dpSink *self) -{ - GstState current, pending; - - gst_element_get_state(GST_ELEMENT(self), ¤t, &pending, 0); - if (pending == GST_STATE_VOID_PENDING) - return current; - - return pending; -} - -/* - * Helper function to create elements, add to the bin and link it - * to another element. - */ -static GstElement *gst_a2dp_sink_init_element(GstA2dpSink *self, - const gchar *elementname, const gchar *name, - GstElement *link_to) -{ - GstElement *element; - GstState state; - - GST_LOG_OBJECT(self, "Initializing %s", elementname); - - element = gst_element_factory_make(elementname, name); - if (element == NULL) { - GST_DEBUG_OBJECT(self, "Couldn't create %s", elementname); - return NULL; - } - - if (!gst_bin_add(GST_BIN(self), element)) { - GST_DEBUG_OBJECT(self, "failed to add %s to the bin", - elementname); - goto cleanup_and_fail; - } - - state = gst_a2dp_sink_get_state(self); - if (gst_element_set_state(element, state) == - GST_STATE_CHANGE_FAILURE) { - GST_DEBUG_OBJECT(self, "%s failed to go to playing", - elementname); - goto remove_element_and_fail; - } - - if (link_to != NULL) - if (!gst_element_link(link_to, element)) { - GST_DEBUG_OBJECT(self, "couldn't link %s", - elementname); - goto remove_element_and_fail; - } - - return element; - -remove_element_and_fail: - gst_element_set_state(element, GST_STATE_NULL); - gst_bin_remove(GST_BIN(self), element); - return NULL; - -cleanup_and_fail: - g_object_unref(G_OBJECT(element)); - - return NULL; -} - -static void gst_a2dp_sink_base_init(gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - - gst_element_class_set_details(element_class, - &gst_a2dp_sink_details); - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&gst_a2dp_sink_factory)); -} - -static void gst_a2dp_sink_set_property(GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - GstA2dpSink *self = GST_A2DP_SINK(object); - - switch (prop_id) { - case PROP_DEVICE: - if (self->sink != NULL) - gst_avdtp_sink_set_device(self->sink, - g_value_get_string(value)); - - if (self->device != NULL) - g_free(self->device); - self->device = g_value_dup_string(value); - break; - - case PROP_TRANSPORT: - if (self->sink != NULL) - gst_avdtp_sink_set_transport(self->sink, - g_value_get_string(value)); - - if (self->transport != NULL) - g_free(self->transport); - self->transport = g_value_dup_string(value); - break; - - case PROP_AUTOCONNECT: - self->autoconnect = g_value_get_boolean(value); - - if (self->sink != NULL) - g_object_set(G_OBJECT(self->sink), "auto-connect", - self->autoconnect, NULL); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void gst_a2dp_sink_get_property(GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - GstA2dpSink *self = GST_A2DP_SINK(object); - gchar *device, *transport; - - switch (prop_id) { - case PROP_DEVICE: - if (self->sink != NULL) { - device = gst_avdtp_sink_get_device(self->sink); - if (device != NULL) - g_value_take_string(value, device); - } - break; - case PROP_AUTOCONNECT: - if (self->sink != NULL) - g_object_get(G_OBJECT(self->sink), "auto-connect", - &self->autoconnect, NULL); - - g_value_set_boolean(value, self->autoconnect); - break; - case PROP_TRANSPORT: - if (self->sink != NULL) { - transport = gst_avdtp_sink_get_transport(self->sink); - if (transport != NULL) - g_value_take_string(value, transport); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self) -{ - GstPad *capsfilter_pad; - - /* we search for the capsfilter sinkpad */ - capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink"); - - /* now we add a ghostpad */ - self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink", - capsfilter_pad)); - g_object_unref(capsfilter_pad); - - /* the getcaps of our ghostpad must reflect the device caps */ - gst_pad_set_getcaps_function(GST_PAD(self->ghostpad), - gst_a2dp_sink_get_caps); - self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad); - gst_pad_set_setcaps_function(GST_PAD(self->ghostpad), - GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps)); - - /* we need to handle events on our own and we also need the eventfunc - * of the ghostpad for forwarding calls */ - self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad)); - gst_pad_set_event_function(GST_PAD(self->ghostpad), - gst_a2dp_sink_handle_event); - - if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad))) - GST_ERROR_OBJECT(self, "failed to add ghostpad"); - - return TRUE; -} - -static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self) -{ - if (self->rtp) { - GST_LOG_OBJECT(self, "removing rtp element from the bin"); - if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp))) - GST_WARNING_OBJECT(self, "failed to remove rtp " - "element from bin"); - else - self->rtp = NULL; - } -} - -static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element, - GstStateChange transition) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstA2dpSink *self = GST_A2DP_SINK(element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - self->taglist = gst_tag_list_new(); - - gst_a2dp_sink_init_fakesink(self); - break; - - case GST_STATE_CHANGE_NULL_TO_READY: - self->sink_is_in_bin = FALSE; - self->sink = GST_AVDTP_SINK(gst_element_factory_make( - "avdtpsink", "avdtpsink")); - if (self->sink == NULL) { - GST_WARNING_OBJECT(self, "failed to create avdtpsink"); - return GST_STATE_CHANGE_FAILURE; - } - - if (self->device != NULL) - gst_avdtp_sink_set_device(self->sink, - self->device); - - if (self->transport != NULL) - gst_avdtp_sink_set_transport(self->sink, - self->transport); - - g_object_set(G_OBJECT(self->sink), "auto-connect", - self->autoconnect, NULL); - - ret = gst_element_set_state(GST_ELEMENT(self->sink), - GST_STATE_READY); - break; - default: - break; - } - - if (ret == GST_STATE_CHANGE_FAILURE) - return ret; - - ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, - transition); - - switch (transition) { - case GST_STATE_CHANGE_PAUSED_TO_READY: - if (self->taglist) { - gst_tag_list_free(self->taglist); - self->taglist = NULL; - } - if (self->newseg_event != NULL) { - gst_event_unref(self->newseg_event); - self->newseg_event = NULL; - } - gst_a2dp_sink_remove_fakesink(self); - break; - - case GST_STATE_CHANGE_READY_TO_NULL: - if (self->sink_is_in_bin) { - if (!gst_bin_remove(GST_BIN(self), - GST_ELEMENT(self->sink))) - GST_WARNING_OBJECT(self, "Failed to remove " - "avdtpsink from bin"); - } else if (self->sink != NULL) { - gst_element_set_state(GST_ELEMENT(self->sink), - GST_STATE_NULL); - g_object_unref(G_OBJECT(self->sink)); - } - - self->sink = NULL; - - gst_a2dp_sink_remove_dynamic_elements(self); - break; - default: - break; - } - - return ret; -} - -static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GstElementClass *element_class = GST_ELEMENT_CLASS(klass); - - parent_class = g_type_class_peek_parent(klass); - - object_class->set_property = GST_DEBUG_FUNCPTR( - gst_a2dp_sink_set_property); - object_class->get_property = GST_DEBUG_FUNCPTR( - gst_a2dp_sink_get_property); - - object_class->finalize = GST_DEBUG_FUNCPTR( - gst_a2dp_sink_finalize); - - element_class->change_state = GST_DEBUG_FUNCPTR( - gst_a2dp_sink_change_state); - - g_object_class_install_property(object_class, PROP_DEVICE, - g_param_spec_string("device", "Device", - "Bluetooth remote device address", - NULL, G_PARAM_READWRITE)); - - g_object_class_install_property(object_class, PROP_AUTOCONNECT, - g_param_spec_boolean("auto-connect", "Auto-connect", - "Automatically attempt to connect to device", - DEFAULT_AUTOCONNECT, G_PARAM_READWRITE)); - - g_object_class_install_property(object_class, PROP_TRANSPORT, - g_param_spec_string("transport", "Transport", - "Use configured transport", - NULL, G_PARAM_READWRITE)); - - GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0, - "A2DP sink element"); -} - -GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self) -{ - return gst_avdtp_sink_get_device_caps(self->sink); -} - -static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad) -{ - GstCaps *caps; - GstCaps *caps_aux; - GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); - - if (self->sink == NULL) { - GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized " - "returning template caps"); - caps = gst_static_pad_template_get_caps( - &gst_a2dp_sink_factory); - } else { - GST_LOG_OBJECT(self, "Getting device caps"); - caps = gst_a2dp_sink_get_device_caps(self); - if (caps == NULL) - caps = gst_static_pad_template_get_caps( - &gst_a2dp_sink_factory); - } - caps_aux = gst_caps_copy(caps); - g_object_set(self->capsfilter, "caps", caps_aux, NULL); - gst_caps_unref(caps_aux); - return caps; -} - -static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self) -{ - GstElement *sink; - - /* check if we don't need a new sink */ - if (self->sink_is_in_bin) - return TRUE; - - if (self->sink == NULL) - sink = gst_element_factory_make("avdtpsink", "avdtpsink"); - else - sink = GST_ELEMENT(self->sink); - - if (sink == NULL) { - GST_ERROR_OBJECT(self, "Couldn't create avdtpsink"); - return FALSE; - } - - if (!gst_bin_add(GST_BIN(self), sink)) { - GST_ERROR_OBJECT(self, "failed to add avdtpsink " - "to the bin"); - goto cleanup_and_fail; - } - - if (gst_element_set_state(sink, GST_STATE_READY) == - GST_STATE_CHANGE_FAILURE) { - GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready"); - goto remove_element_and_fail; - } - - if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) { - GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay " - "to avdtpsink"); - goto remove_element_and_fail; - } - - self->sink = GST_AVDTP_SINK(sink); - self->sink_is_in_bin = TRUE; - g_object_set(G_OBJECT(self->sink), "device", self->device, NULL); - g_object_set(G_OBJECT(self->sink), "transport", self->transport, NULL); - - gst_element_set_state(sink, GST_STATE_PAUSED); - - return TRUE; - -remove_element_and_fail: - gst_element_set_state(sink, GST_STATE_NULL); - gst_bin_remove(GST_BIN(self), sink); - return FALSE; - -cleanup_and_fail: - if (sink != NULL) - g_object_unref(G_OBJECT(sink)); - - return FALSE; -} - -static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self) -{ - GstElement *rtppay; - - /* if we already have a rtp, we don't need a new one */ - if (self->rtp != NULL) - return TRUE; - - rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp", - self->capsfilter); - if (rtppay == NULL) - return FALSE; - - self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); - g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL); - - gst_element_set_state(rtppay, GST_STATE_PAUSED); - - return TRUE; -} - -static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self) -{ - GstElement *rtppay; - - /* check if we don't need a new rtp */ - if (self->rtp) - return TRUE; - - GST_LOG_OBJECT(self, "Initializing rtp mpeg element"); - /* if capsfilter is not created then we can't have our rtp element */ - if (self->capsfilter == NULL) - return FALSE; - - rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp", - self->capsfilter); - if (rtppay == NULL) - return FALSE; - - self->rtp = GST_BASE_RTP_PAYLOAD(rtppay); - - gst_element_set_state(rtppay, GST_STATE_PAUSED); - - return TRUE; -} - -static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self, - GstCaps *caps) -{ - GstStructure *structure; - GstEvent *event; - GstPad *capsfilterpad; - gboolean crc; - gchar *mode = NULL; - - structure = gst_caps_get_structure(caps, 0); - - /* before everything we need to remove fakesink */ - gst_a2dp_sink_remove_fakesink(self); - - /* first, we need to create our rtp payloader */ - if (gst_structure_has_name(structure, "audio/x-sbc")) { - GST_LOG_OBJECT(self, "sbc media received"); - if (!gst_a2dp_sink_init_rtp_sbc_element(self)) - return FALSE; - } else if (gst_structure_has_name(structure, "audio/mpeg")) { - GST_LOG_OBJECT(self, "mp3 media received"); - if (!gst_a2dp_sink_init_rtp_mpeg_element(self)) - return FALSE; - } else { - GST_ERROR_OBJECT(self, "Unexpected media type"); - return FALSE; - } - - if (!gst_a2dp_sink_init_avdtp_sink(self)) - return FALSE; - - /* check if we should push the taglist FIXME should we push this? - * we can send the tags directly if needed */ - if (self->taglist != NULL && - gst_structure_has_name(structure, "audio/mpeg")) { - - event = gst_event_new_tag(self->taglist); - - /* send directly the crc */ - if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc)) - gst_avdtp_sink_set_crc(self->sink, crc); - - if (gst_tag_list_get_string(self->taglist, "channel-mode", - &mode)) - gst_avdtp_sink_set_channel_mode(self->sink, mode); - - capsfilterpad = gst_ghost_pad_get_target(self->ghostpad); - gst_pad_send_event(capsfilterpad, event); - self->taglist = NULL; - g_free(mode); - } - - if (!gst_avdtp_sink_set_device_caps(self->sink, caps)) - return FALSE; - - g_object_set(G_OBJECT(self->rtp), "mtu", - gst_avdtp_sink_get_link_mtu(self->sink), NULL); - - /* we forward our new segment here if we have one */ - if (self->newseg_event) { - gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp), - self->newseg_event); - self->newseg_event = NULL; - } - - return TRUE; -} - -static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps) -{ - GstA2dpSink *self; - - self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); - GST_INFO_OBJECT(self, "setting caps"); - - /* now we know the caps */ - gst_a2dp_sink_init_dynamic_elements(self, caps); - - return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps); -} - -/* used for catching newsegment events while we don't have a sink, for - * later forwarding it to the sink */ -static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event) -{ - GstA2dpSink *self; - GstTagList *taglist = NULL; - GstObject *parent; - - self = GST_A2DP_SINK(GST_PAD_PARENT(pad)); - parent = gst_element_get_parent(GST_ELEMENT(self->sink)); - - if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT && - parent != GST_OBJECT_CAST(self)) { - if (self->newseg_event != NULL) - gst_event_unref(self->newseg_event); - self->newseg_event = gst_event_ref(event); - - } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG && - parent != GST_OBJECT_CAST(self)) { - if (self->taglist == NULL) - gst_event_parse_tag(event, &self->taglist); - else { - gst_event_parse_tag(event, &taglist); - gst_tag_list_insert(self->taglist, taglist, - GST_TAG_MERGE_REPLACE); - } - } - - if (parent != NULL) - gst_object_unref(GST_OBJECT(parent)); - - return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event); -} - -static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self) -{ - GstElement *element; - - element = gst_element_factory_make("capsfilter", "filter"); - if (element == NULL) - goto failed; - - if (!gst_bin_add(GST_BIN(self), element)) - goto failed; - - self->capsfilter = element; - return TRUE; - -failed: - GST_ERROR_OBJECT(self, "Failed to initialize caps filter"); - return FALSE; -} - -static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self) -{ - if (self->fakesink != NULL) - return TRUE; - - g_mutex_lock(self->cb_mutex); - self->fakesink = gst_a2dp_sink_init_element(self, "fakesink", - "fakesink", self->capsfilter); - g_mutex_unlock(self->cb_mutex); - - if (!self->fakesink) - return FALSE; - - return TRUE; -} - -static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self) -{ - g_mutex_lock(self->cb_mutex); - - if (self->fakesink != NULL) { - gst_element_set_locked_state(self->fakesink, TRUE); - gst_element_set_state(self->fakesink, GST_STATE_NULL); - - gst_bin_remove(GST_BIN(self), self->fakesink); - self->fakesink = NULL; - } - - g_mutex_unlock(self->cb_mutex); - - return TRUE; -} - -static void gst_a2dp_sink_init(GstA2dpSink *self, - GstA2dpSinkClass *klass) -{ - self->sink = NULL; - self->fakesink = NULL; - self->rtp = NULL; - self->device = NULL; - self->transport = NULL; - self->autoconnect = DEFAULT_AUTOCONNECT; - self->capsfilter = NULL; - self->newseg_event = NULL; - self->taglist = NULL; - self->ghostpad = NULL; - self->sink_is_in_bin = FALSE; - - self->cb_mutex = g_mutex_new(); - - /* we initialize our capsfilter */ - gst_a2dp_sink_init_caps_filter(self); - g_object_set(self->capsfilter, "caps", - gst_static_pad_template_get_caps(&gst_a2dp_sink_factory), - NULL); - - gst_a2dp_sink_init_fakesink(self); - - gst_a2dp_sink_init_ghost_pad(self); - -} - -gboolean gst_a2dp_sink_plugin_init(GstPlugin *plugin) -{ - return gst_element_register(plugin, "a2dpsink", - GST_RANK_MARGINAL, GST_TYPE_A2DP_SINK); -} diff -Nru bluez-4.101/audio/gsta2dpsink.h bluez-5.23/audio/gsta2dpsink.h --- bluez-4.101/audio/gsta2dpsink.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gsta2dpsink.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 - * - */ - -#ifndef __GST_A2DP_SINK_H__ -#define __GST_A2DP_SINK_H__ - -#include -#include -#include "gstavdtpsink.h" - -G_BEGIN_DECLS - -#define GST_TYPE_A2DP_SINK \ - (gst_a2dp_sink_get_type()) -#define GST_A2DP_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_A2DP_SINK,GstA2dpSink)) -#define GST_A2DP_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_A2DP_SINK,GstA2dpSinkClass)) -#define GST_IS_A2DP_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_A2DP_SINK)) -#define GST_IS_A2DP_SINK_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_A2DP_SINK)) - -typedef struct _GstA2dpSink GstA2dpSink; -typedef struct _GstA2dpSinkClass GstA2dpSinkClass; - -struct _GstA2dpSink { - GstBin bin; - - GstBaseRTPPayload *rtp; - GstAvdtpSink *sink; - GstElement *capsfilter; - GstElement *fakesink; - - gchar *device; - gchar *transport; - gboolean autoconnect; - gboolean sink_is_in_bin; - - GstGhostPad *ghostpad; - GstPadSetCapsFunction ghostpad_setcapsfunc; - GstPadEventFunction ghostpad_eventfunc; - - GstEvent *newseg_event; - /* Store the tags received before the a2dpsender sink is created - * when it is created we forward this to it */ - GstTagList *taglist; - - GMutex *cb_mutex; -}; - -struct _GstA2dpSinkClass { - GstBinClass parent_class; -}; - -GType gst_a2dp_sink_get_type(void); - -gboolean gst_a2dp_sink_plugin_init (GstPlugin * plugin); - -GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self); - -G_END_DECLS - -#endif diff -Nru bluez-4.101/audio/gstavdtpsink.c bluez-5.23/audio/gstavdtpsink.c --- bluez-4.101/audio/gstavdtpsink.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gstavdtpsink.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2034 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include - -#include "ipc.h" -#include "rtp.h" -#include "a2dp-codecs.h" - -#include "gstpragma.h" -#include "gstavdtpsink.h" - -GST_DEBUG_CATEGORY_STATIC(avdtp_sink_debug); -#define GST_CAT_DEFAULT avdtp_sink_debug - -#define BUFFER_SIZE 2048 -#define TEMPLATE_MAX_BITPOOL 64 -#define CRC_PROTECTED 1 -#define CRC_UNPROTECTED 0 - -#define DEFAULT_AUTOCONNECT TRUE - -#define GST_AVDTP_SINK_MUTEX_LOCK(s) G_STMT_START { \ - g_mutex_lock(s->sink_lock); \ - } G_STMT_END - -#define GST_AVDTP_SINK_MUTEX_UNLOCK(s) G_STMT_START { \ - g_mutex_unlock(s->sink_lock); \ - } G_STMT_END - -struct bluetooth_data { - struct bt_get_capabilities_rsp *caps; /* Bluetooth device caps */ - guint link_mtu; - - DBusConnection *conn; - guint8 codec; /* Bluetooth transport configuration */ - gchar *uuid; - guint8 *config; - gint config_size; - - gchar buffer[BUFFER_SIZE]; /* Codec transfer buffer */ -}; - -#define IS_SBC(n) (strcmp((n), "audio/x-sbc") == 0) -#define IS_MPEG_AUDIO(n) (strcmp((n), "audio/mpeg") == 0) - -enum { - PROP_0, - PROP_DEVICE, - PROP_AUTOCONNECT, - PROP_TRANSPORT -}; - -GST_BOILERPLATE(GstAvdtpSink, gst_avdtp_sink, GstBaseSink, - GST_TYPE_BASE_SINK); - -static const GstElementDetails avdtp_sink_details = - GST_ELEMENT_DETAILS("Bluetooth AVDTP sink", - "Sink/Audio", - "Plays audio to an A2DP device", - "Marcel Holtmann "); - -static GstStaticPadTemplate avdtp_sink_factory = - GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("application/x-rtp, " - "media = (string) \"audio\"," - "payload = (int) " - GST_RTP_PAYLOAD_DYNAMIC_STRING ", " - "clock-rate = (int) { 16000, 32000, " - "44100, 48000 }, " - "encoding-name = (string) \"SBC\"; " - "application/x-rtp, " - "media = (string) \"audio\", " - "payload = (int) " - GST_RTP_PAYLOAD_MPA_STRING ", " - "clock-rate = (int) 90000; " - "application/x-rtp, " - "media = (string) \"audio\", " - "payload = (int) " - GST_RTP_PAYLOAD_DYNAMIC_STRING ", " - "clock-rate = (int) 90000, " - "encoding-name = (string) \"MPA\"" - )); - -static int gst_avdtp_sink_audioservice_send(GstAvdtpSink *self, - const bt_audio_msg_header_t *msg); -static int gst_avdtp_sink_audioservice_expect(GstAvdtpSink *self, - bt_audio_msg_header_t *outmsg, - guint8 expected_name); - - -static void gst_avdtp_sink_base_init(gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&avdtp_sink_factory)); - - gst_element_class_set_details(element_class, &avdtp_sink_details); -} - -static void gst_avdtp_sink_transport_release(GstAvdtpSink *self) -{ - DBusMessage *msg; - const char *access_type = "w"; - - msg = dbus_message_new_method_call("org.bluez", self->transport, - "org.bluez.MediaTransport", - "Release"); - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type, - DBUS_TYPE_INVALID); - - dbus_connection_send(self->data->conn, msg, NULL); - - dbus_message_unref(msg); -} - -static gboolean gst_avdtp_sink_stop(GstBaseSink *basesink) -{ - GstAvdtpSink *self = GST_AVDTP_SINK(basesink); - - GST_INFO_OBJECT(self, "stop"); - - if (self->watch_id != 0) { - g_source_remove(self->watch_id); - self->watch_id = 0; - } - - if (self->server) { - bt_audio_service_close(g_io_channel_unix_get_fd(self->server)); - g_io_channel_unref(self->server); - self->server = NULL; - } - - if (self->stream) { - g_io_channel_shutdown(self->stream, TRUE, NULL); - g_io_channel_unref(self->stream); - self->stream = NULL; - } - - if (self->data) { - if (self->transport) - gst_avdtp_sink_transport_release(self); - if (self->data->conn) - dbus_connection_unref(self->data->conn); - g_free(self->data); - self->data = NULL; - } - - if (self->stream_caps) { - gst_caps_unref(self->stream_caps); - self->stream_caps = NULL; - } - - if (self->dev_caps) { - gst_caps_unref(self->dev_caps); - self->dev_caps = NULL; - } - - return TRUE; -} - -static void gst_avdtp_sink_finalize(GObject *object) -{ - GstAvdtpSink *self = GST_AVDTP_SINK(object); - - if (self->data) - gst_avdtp_sink_stop(GST_BASE_SINK(self)); - - if (self->device) - g_free(self->device); - - if (self->transport) - g_free(self->transport); - - g_mutex_free(self->sink_lock); - - G_OBJECT_CLASS(parent_class)->finalize(object); -} - -static void gst_avdtp_sink_set_property(GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - GstAvdtpSink *sink = GST_AVDTP_SINK(object); - - switch (prop_id) { - case PROP_DEVICE: - if (sink->device) - g_free(sink->device); - sink->device = g_value_dup_string(value); - break; - - case PROP_AUTOCONNECT: - sink->autoconnect = g_value_get_boolean(value); - break; - - case PROP_TRANSPORT: - if (sink->transport) - g_free(sink->transport); - sink->transport = g_value_dup_string(value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void gst_avdtp_sink_get_property(GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - GstAvdtpSink *sink = GST_AVDTP_SINK(object); - - switch (prop_id) { - case PROP_DEVICE: - g_value_set_string(value, sink->device); - break; - - case PROP_AUTOCONNECT: - g_value_set_boolean(value, sink->autoconnect); - break; - - case PROP_TRANSPORT: - g_value_set_string(value, sink->transport); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static gint gst_avdtp_sink_bluetooth_recvmsg_fd(GstAvdtpSink *sink) -{ - int err, ret; - - ret = bt_audio_service_get_data_fd( - g_io_channel_unix_get_fd(sink->server)); - - if (ret < 0) { - err = -errno; - GST_ERROR_OBJECT(sink, "Unable to receive fd: %s (%d)", - strerror(-err), -err); - return err; - } - - sink->stream = g_io_channel_unix_new(ret); - g_io_channel_set_encoding(sink->stream, NULL, NULL); - GST_DEBUG_OBJECT(sink, "stream_fd=%d", ret); - - return 0; -} - -static codec_capabilities_t *gst_avdtp_find_caps(GstAvdtpSink *sink, - uint8_t codec_type) -{ - struct bt_get_capabilities_rsp *rsp = sink->data->caps; - codec_capabilities_t *codec = (void *) rsp->data; - int bytes_left = rsp->h.length - sizeof(*rsp); - - while (bytes_left > 0) { - if ((codec->type == codec_type) && - !(codec->lock & BT_WRITE_LOCK)) - break; - - bytes_left -= codec->length; - codec = (void *) codec + codec->length; - } - - if (bytes_left <= 0) - return NULL; - - return codec; -} - -static gboolean gst_avdtp_sink_init_sbc_pkt_conf(GstAvdtpSink *sink, - GstCaps *caps, - sbc_capabilities_t *pkt) -{ - sbc_capabilities_t *cfg; - const GValue *value = NULL; - const char *pref, *name; - gint rate, subbands, blocks; - GstStructure *structure = gst_caps_get_structure(caps, 0); - - cfg = (void *) gst_avdtp_find_caps(sink, BT_A2DP_SBC_SINK); - name = gst_structure_get_name(structure); - - if (!(IS_SBC(name))) { - GST_ERROR_OBJECT(sink, "Unexpected format %s, " - "was expecting sbc", name); - return FALSE; - } - - value = gst_structure_get_value(structure, "rate"); - rate = g_value_get_int(value); - if (rate == 44100) - cfg->frequency = BT_SBC_SAMPLING_FREQ_44100; - else if (rate == 48000) - cfg->frequency = BT_SBC_SAMPLING_FREQ_48000; - else if (rate == 32000) - cfg->frequency = BT_SBC_SAMPLING_FREQ_32000; - else if (rate == 16000) - cfg->frequency = BT_SBC_SAMPLING_FREQ_16000; - else { - GST_ERROR_OBJECT(sink, "Invalid rate while setting caps"); - return FALSE; - } - - value = gst_structure_get_value(structure, "mode"); - pref = g_value_get_string(value); - if (strcmp(pref, "mono") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; - else if (strcmp(pref, "dual") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (strcmp(pref, "stereo") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; - else if (strcmp(pref, "joint") == 0) - cfg->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else { - GST_ERROR_OBJECT(sink, "Invalid mode %s", pref); - return FALSE; - } - - value = gst_structure_get_value(structure, "allocation"); - pref = g_value_get_string(value); - if (strcmp(pref, "loudness") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; - else if (strcmp(pref, "snr") == 0) - cfg->allocation_method = BT_A2DP_ALLOCATION_SNR; - else { - GST_ERROR_OBJECT(sink, "Invalid allocation: %s", pref); - return FALSE; - } - - value = gst_structure_get_value(structure, "subbands"); - subbands = g_value_get_int(value); - if (subbands == 8) - cfg->subbands = BT_A2DP_SUBBANDS_8; - else if (subbands == 4) - cfg->subbands = BT_A2DP_SUBBANDS_4; - else { - GST_ERROR_OBJECT(sink, "Invalid subbands %d", subbands); - return FALSE; - } - - value = gst_structure_get_value(structure, "blocks"); - blocks = g_value_get_int(value); - if (blocks == 16) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_16; - else if (blocks == 12) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_12; - else if (blocks == 8) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_8; - else if (blocks == 4) - cfg->block_length = BT_A2DP_BLOCK_LENGTH_4; - else { - GST_ERROR_OBJECT(sink, "Invalid blocks %d", blocks); - return FALSE; - } - - value = gst_structure_get_value(structure, "bitpool"); - cfg->max_bitpool = cfg->min_bitpool = g_value_get_int(value); - - memcpy(pkt, cfg, sizeof(*pkt)); - - return TRUE; -} - -static gboolean gst_avdtp_sink_conf_recv_stream_fd( - GstAvdtpSink *self) -{ - struct bluetooth_data *data = self->data; - gint ret; - GError *gerr = NULL; - GIOStatus status; - GIOFlags flags; - int fd; - - /* Proceed if stream was already acquired */ - if (self->stream != NULL) - goto proceed; - - ret = gst_avdtp_sink_bluetooth_recvmsg_fd(self); - if (ret < 0) - return FALSE; - - if (!self->stream) { - GST_ERROR_OBJECT(self, "Error while configuring device: " - "could not acquire audio socket"); - return FALSE; - } - -proceed: - /* set stream socket to nonblock */ - GST_LOG_OBJECT(self, "setting stream socket to nonblock"); - flags = g_io_channel_get_flags(self->stream); - flags |= G_IO_FLAG_NONBLOCK; - status = g_io_channel_set_flags(self->stream, flags, &gerr); - if (status != G_IO_STATUS_NORMAL) { - if (gerr) - GST_WARNING_OBJECT(self, "Error while " - "setting server socket to nonblock: " - "%s", gerr->message); - else - GST_WARNING_OBJECT(self, "Error while " - "setting server " - "socket to nonblock"); - } - - fd = g_io_channel_unix_get_fd(self->stream); - - /* It is possible there is some outstanding - data in the pipe - we have to empty it */ - GST_LOG_OBJECT(self, "emptying stream pipe"); - while (1) { - ssize_t bread = read(fd, data->buffer, data->link_mtu); - if (bread <= 0) - break; - } - - /* set stream socket to block */ - GST_LOG_OBJECT(self, "setting stream socket to block"); - flags = g_io_channel_get_flags(self->stream); - flags &= ~G_IO_FLAG_NONBLOCK; - status = g_io_channel_set_flags(self->stream, flags, &gerr); - if (status != G_IO_STATUS_NORMAL) { - if (gerr) - GST_WARNING_OBJECT(self, "Error while " - "setting server socket to block:" - "%s", gerr->message); - else - GST_WARNING_OBJECT(self, "Error while " - "setting server " - "socket to block"); - } - - memset(data->buffer, 0, sizeof(data->buffer)); - - return TRUE; -} - -static gboolean server_callback(GIOChannel *chan, - GIOCondition cond, gpointer data) -{ - if (cond & G_IO_HUP || cond & G_IO_NVAL) - return FALSE; - else if (cond & G_IO_ERR) - GST_WARNING_OBJECT(GST_AVDTP_SINK(data), - "Untreated callback G_IO_ERR"); - - return TRUE; -} - -static GstStructure *gst_avdtp_sink_parse_sbc_caps( - GstAvdtpSink *self, sbc_capabilities_t *sbc) -{ - GstStructure *structure; - GValue *value; - GValue *list; - gboolean mono, stereo; - - if (sbc == NULL) - return NULL; - - structure = gst_structure_empty_new("audio/x-sbc"); - value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); - - /* mode */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "mode", list); - g_free(list); - list = NULL; - } - - /* subbands */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - value = g_value_init(value, G_TYPE_INT); - if (sbc->subbands & BT_A2DP_SUBBANDS_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - if (sbc->subbands & BT_A2DP_SUBBANDS_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "subbands", list); - g_free(list); - list = NULL; - } - - /* blocks */ - value = g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { - g_value_set_int(value, 16); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { - g_value_set_int(value, 12); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "blocks", list); - g_free(list); - list = NULL; - } - - /* allocation */ - g_value_init(value, G_TYPE_STRING); - list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); - if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - } - if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "allocation", list); - g_free(list); - list = NULL; - } - - /* rate */ - g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) { - g_value_set_int(value, 48000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) { - g_value_set_int(value, 44100); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) { - g_value_set_int(value, 32000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) { - g_value_set_int(value, 16000); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "rate", list); - g_free(list); - list = NULL; - } - - /* bitpool */ - value = g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, - MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL), - MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL)); - gst_structure_set_value(structure, "bitpool", value); - g_value_unset(value); - - /* channels */ - mono = FALSE; - stereo = FALSE; - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - mono = TRUE; - if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || - (sbc->channel_mode & - BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || - (sbc->channel_mode & - BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, 1, 2); - } else { - g_value_init(value, G_TYPE_INT); - if (mono) - g_value_set_int(value, 1); - else if (stereo) - g_value_set_int(value, 2); - else { - GST_ERROR_OBJECT(self, - "Unexpected number of channels"); - g_value_set_int(value, 0); - } - } - - gst_structure_set_value(structure, "channels", value); - g_free(value); - - return structure; -} - -static GstStructure *gst_avdtp_sink_parse_mpeg_caps( - GstAvdtpSink *self, mpeg_capabilities_t *mpeg) -{ - GstStructure *structure; - GValue *value; - GValue *list; - gboolean valid_layer = FALSE; - gboolean mono, stereo; - - if (!mpeg) - return NULL; - - GST_LOG_OBJECT(self, "parsing mpeg caps"); - - structure = gst_structure_empty_new("audio/mpeg"); - value = g_new0(GValue, 1); - g_value_init(value, G_TYPE_INT); - - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - g_value_set_int(value, 1); - gst_value_list_prepend_value(list, value); - g_value_set_int(value, 2); - gst_value_list_prepend_value(list, value); - gst_structure_set_value(structure, "mpegversion", list); - g_free(list); - - /* layer */ - GST_LOG_OBJECT(self, "setting mpeg layer"); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (mpeg->layer & BT_MPEG_LAYER_1) { - g_value_set_int(value, 1); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (mpeg->layer & BT_MPEG_LAYER_2) { - g_value_set_int(value, 2); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (mpeg->layer & BT_MPEG_LAYER_3) { - g_value_set_int(value, 3); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (list) { - gst_structure_set_value(structure, "layer", list); - g_free(list); - list = NULL; - } - - if (!valid_layer) { - gst_structure_free(structure); - g_free(value); - return NULL; - } - - /* rate */ - GST_LOG_OBJECT(self, "setting mpeg rate"); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) { - g_value_set_int(value, 48000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) { - g_value_set_int(value, 44100); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) { - g_value_set_int(value, 32000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) { - g_value_set_int(value, 24000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) { - g_value_set_int(value, 22050); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) { - g_value_set_int(value, 16000); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "rate", list); - g_free(list); - list = NULL; - } - - /* channels */ - GST_LOG_OBJECT(self, "setting mpeg channels"); - mono = FALSE; - stereo = FALSE; - if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - mono = TRUE; - if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || - (mpeg->channel_mode & - BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || - (mpeg->channel_mode & - BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, 1, 2); - } else { - g_value_init(value, G_TYPE_INT); - if (mono) - g_value_set_int(value, 1); - else if (stereo) - g_value_set_int(value, 2); - else { - GST_ERROR_OBJECT(self, - "Unexpected number of channels"); - g_value_set_int(value, 0); - } - } - gst_structure_set_value(structure, "channels", value); - g_free(value); - - return structure; -} - -static GstStructure *gst_avdtp_sink_parse_sbc_raw(GstAvdtpSink *self) -{ - a2dp_sbc_t *sbc = (a2dp_sbc_t *) self->data->config; - GstStructure *structure; - GValue *value; - GValue *list; - gboolean mono, stereo; - - structure = gst_structure_empty_new("audio/x-sbc"); - value = g_value_init(g_new0(GValue, 1), G_TYPE_STRING); - - /* mode */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { - g_value_set_static_string(value, "mono"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) { - g_value_set_static_string(value, "stereo"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) { - g_value_set_static_string(value, "dual"); - gst_value_list_prepend_value(list, value); - } - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) { - g_value_set_static_string(value, "joint"); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "mode", list); - g_free(list); - list = NULL; - } - - /* subbands */ - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - value = g_value_init(value, G_TYPE_INT); - if (sbc->subbands & BT_A2DP_SUBBANDS_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - if (sbc->subbands & BT_A2DP_SUBBANDS_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "subbands", list); - g_free(list); - list = NULL; - } - - /* blocks */ - value = g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_16) { - g_value_set_int(value, 16); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_12) { - g_value_set_int(value, 12); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_8) { - g_value_set_int(value, 8); - gst_value_list_prepend_value(list, value); - } - if (sbc->block_length & BT_A2DP_BLOCK_LENGTH_4) { - g_value_set_int(value, 4); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "blocks", list); - g_free(list); - list = NULL; - } - - /* allocation */ - g_value_init(value, G_TYPE_STRING); - list = g_value_init(g_new0(GValue,1), GST_TYPE_LIST); - if (sbc->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) { - g_value_set_static_string(value, "loudness"); - gst_value_list_prepend_value(list, value); - } - if (sbc->allocation_method & BT_A2DP_ALLOCATION_SNR) { - g_value_set_static_string(value, "snr"); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "allocation", list); - g_free(list); - list = NULL; - } - - /* rate */ - g_value_init(value, G_TYPE_INT); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_48000) { - g_value_set_int(value, 48000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_44100) { - g_value_set_int(value, 44100); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_32000) { - g_value_set_int(value, 32000); - gst_value_list_prepend_value(list, value); - } - if (sbc->frequency & BT_SBC_SAMPLING_FREQ_16000) { - g_value_set_int(value, 16000); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "rate", list); - g_free(list); - list = NULL; - } - - /* bitpool */ - value = g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, - MIN(sbc->min_bitpool, TEMPLATE_MAX_BITPOOL), - MIN(sbc->max_bitpool, TEMPLATE_MAX_BITPOOL)); - gst_structure_set_value(structure, "bitpool", value); - g_value_unset(value); - - /* channels */ - mono = FALSE; - stereo = FALSE; - if (sbc->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - mono = TRUE; - if ((sbc->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || - (sbc->channel_mode & - BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || - (sbc->channel_mode & - BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, 1, 2); - } else { - g_value_init(value, G_TYPE_INT); - if (mono) - g_value_set_int(value, 1); - else if (stereo) - g_value_set_int(value, 2); - else { - GST_ERROR_OBJECT(self, - "Unexpected number of channels"); - g_value_set_int(value, 0); - } - } - - gst_structure_set_value(structure, "channels", value); - g_free(value); - - return structure; -} - -static GstStructure *gst_avdtp_sink_parse_mpeg_raw(GstAvdtpSink *self) -{ - a2dp_mpeg_t *mpeg = (a2dp_mpeg_t *) self->data->config; - GstStructure *structure; - GValue *value; - GValue *list; - gboolean valid_layer = FALSE; - gboolean mono, stereo; - - GST_LOG_OBJECT(self, "parsing mpeg caps"); - - structure = gst_structure_empty_new("audio/mpeg"); - value = g_new0(GValue, 1); - g_value_init(value, G_TYPE_INT); - - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - g_value_set_int(value, 1); - gst_value_list_prepend_value(list, value); - g_value_set_int(value, 2); - gst_value_list_prepend_value(list, value); - gst_structure_set_value(structure, "mpegversion", list); - g_free(list); - - /* layer */ - GST_LOG_OBJECT(self, "setting mpeg layer"); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (mpeg->layer & BT_MPEG_LAYER_1) { - g_value_set_int(value, 1); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (mpeg->layer & BT_MPEG_LAYER_2) { - g_value_set_int(value, 2); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (mpeg->layer & BT_MPEG_LAYER_3) { - g_value_set_int(value, 3); - gst_value_list_prepend_value(list, value); - valid_layer = TRUE; - } - if (list) { - gst_structure_set_value(structure, "layer", list); - g_free(list); - list = NULL; - } - - if (!valid_layer) { - gst_structure_free(structure); - g_free(value); - return NULL; - } - - /* rate */ - GST_LOG_OBJECT(self, "setting mpeg rate"); - list = g_value_init(g_new0(GValue, 1), GST_TYPE_LIST); - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_48000) { - g_value_set_int(value, 48000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_44100) { - g_value_set_int(value, 44100); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_32000) { - g_value_set_int(value, 32000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_24000) { - g_value_set_int(value, 24000); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_22050) { - g_value_set_int(value, 22050); - gst_value_list_prepend_value(list, value); - } - if (mpeg->frequency & BT_MPEG_SAMPLING_FREQ_16000) { - g_value_set_int(value, 16000); - gst_value_list_prepend_value(list, value); - } - g_value_unset(value); - if (list) { - gst_structure_set_value(structure, "rate", list); - g_free(list); - list = NULL; - } - - /* channels */ - GST_LOG_OBJECT(self, "setting mpeg channels"); - mono = FALSE; - stereo = FALSE; - if (mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - mono = TRUE; - if ((mpeg->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) || - (mpeg->channel_mode & - BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) || - (mpeg->channel_mode & - BT_A2DP_CHANNEL_MODE_JOINT_STEREO)) - stereo = TRUE; - - if (mono && stereo) { - g_value_init(value, GST_TYPE_INT_RANGE); - gst_value_set_int_range(value, 1, 2); - } else { - g_value_init(value, G_TYPE_INT); - if (mono) - g_value_set_int(value, 1); - else if (stereo) - g_value_set_int(value, 2); - else { - GST_ERROR_OBJECT(self, - "Unexpected number of channels"); - g_value_set_int(value, 0); - } - } - gst_structure_set_value(structure, "channels", value); - g_free(value); - - return structure; -} - -static gboolean gst_avdtp_sink_update_config(GstAvdtpSink *self) -{ - GstStructure *structure; - gchar *tmp; - - switch (self->data->codec) { - case A2DP_CODEC_SBC: - structure = gst_avdtp_sink_parse_sbc_raw(self); - break; - case A2DP_CODEC_MPEG12: - structure = gst_avdtp_sink_parse_mpeg_raw(self); - break; - default: - GST_ERROR_OBJECT(self, "Unsupported configuration"); - return FALSE; - } - - if (structure == NULL) - return FALSE; - - if (self->dev_caps != NULL) - gst_caps_unref(self->dev_caps); - - self->dev_caps = gst_caps_new_full(structure, NULL); - - tmp = gst_caps_to_string(self->dev_caps); - GST_DEBUG_OBJECT(self, "Transport configuration: %s", tmp); - g_free(tmp); - - return TRUE; -} - -static gboolean gst_avdtp_sink_update_caps(GstAvdtpSink *self) -{ - sbc_capabilities_t *sbc; - mpeg_capabilities_t *mpeg; - GstStructure *sbc_structure; - GstStructure *mpeg_structure; - gchar *tmp; - - GST_LOG_OBJECT(self, "updating device caps"); - - if (self->data->config_size != 0 && self->data->config != NULL) - return gst_avdtp_sink_update_config(self); - - sbc = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK); - mpeg = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK); - - if (!sbc) { - GST_ERROR_OBJECT(self, "Failed to find mandatory SBC sink"); - return FALSE; - } - - sbc_structure = gst_avdtp_sink_parse_sbc_caps(self, sbc); - mpeg_structure = gst_avdtp_sink_parse_mpeg_caps(self, mpeg); - - if (self->dev_caps != NULL) - gst_caps_unref(self->dev_caps); - self->dev_caps = gst_caps_new_full(sbc_structure, NULL); - if (mpeg_structure != NULL) - gst_caps_append_structure(self->dev_caps, mpeg_structure); - - tmp = gst_caps_to_string(self->dev_caps); - GST_DEBUG_OBJECT(self, "Device capabilities: %s", tmp); - g_free(tmp); - - return TRUE; -} - -static gboolean gst_avdtp_sink_get_capabilities(GstAvdtpSink *self) -{ - gchar buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_get_capabilities_req *req = (void *) buf; - struct bt_get_capabilities_rsp *rsp = (void *) buf; - int err; - - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - - req->h.type = BT_REQUEST; - req->h.name = BT_GET_CAPABILITIES; - req->h.length = sizeof(*req); - - if (self->device == NULL) - return FALSE; - strncpy(req->destination, self->device, 18); - if (self->autoconnect) - req->flags |= BT_FLAG_AUTOCONNECT; - - err = gst_avdtp_sink_audioservice_send(self, &req->h); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error while asking device caps"); - return FALSE; - } - - rsp->h.length = 0; - err = gst_avdtp_sink_audioservice_expect(self, - &rsp->h, BT_GET_CAPABILITIES); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error while getting device caps"); - return FALSE; - } - - self->data->caps = g_malloc0(rsp->h.length); - memcpy(self->data->caps, rsp, rsp->h.length); - if (!gst_avdtp_sink_update_caps(self)) { - GST_WARNING_OBJECT(self, "failed to update capabilities"); - return FALSE; - } - - return TRUE; -} - -static gint gst_avdtp_sink_get_channel_mode(const gchar *mode) -{ - if (strcmp(mode, "stereo") == 0) - return BT_A2DP_CHANNEL_MODE_STEREO; - else if (strcmp(mode, "joint-stereo") == 0) - return BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (strcmp(mode, "dual-channel") == 0) - return BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (strcmp(mode, "mono") == 0) - return BT_A2DP_CHANNEL_MODE_MONO; - else - return -1; -} - -static void gst_avdtp_sink_tag(const GstTagList *taglist, - const gchar *tag, gpointer user_data) -{ - gboolean crc; - gchar *channel_mode = NULL; - GstAvdtpSink *self = GST_AVDTP_SINK(user_data); - - if (strcmp(tag, "has-crc") == 0) { - - if (!gst_tag_list_get_boolean(taglist, tag, &crc)) { - GST_WARNING_OBJECT(self, "failed to get crc tag"); - return; - } - - gst_avdtp_sink_set_crc(self, crc); - - } else if (strcmp(tag, "channel-mode") == 0) { - - if (!gst_tag_list_get_string(taglist, tag, &channel_mode)) { - GST_WARNING_OBJECT(self, - "failed to get channel-mode tag"); - return; - } - - self->channel_mode = gst_avdtp_sink_get_channel_mode( - channel_mode); - if (self->channel_mode == -1) - GST_WARNING_OBJECT(self, "Received invalid channel " - "mode: %s", channel_mode); - g_free(channel_mode); - - } else - GST_DEBUG_OBJECT(self, "received unused tag: %s", tag); -} - -static gboolean gst_avdtp_sink_event(GstBaseSink *basesink, - GstEvent *event) -{ - GstAvdtpSink *self = GST_AVDTP_SINK(basesink); - GstTagList *taglist = NULL; - - if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { - /* we check the tags, mp3 has tags that are importants and - * are outside caps */ - gst_event_parse_tag(event, &taglist); - gst_tag_list_foreach(taglist, gst_avdtp_sink_tag, self); - } - - return TRUE; -} - -static gboolean gst_avdtp_sink_transport_parse_property(GstAvdtpSink *self, - DBusMessageIter *i) -{ - const char *key; - DBusMessageIter variant_i; - - if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) { - GST_ERROR_OBJECT(self, "Property name not a string."); - return FALSE; - } - - dbus_message_iter_get_basic(i, &key); - - if (!dbus_message_iter_next(i)) { - GST_ERROR_OBJECT(self, "Property value missing"); - return FALSE; - } - - if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) { - GST_ERROR_OBJECT(self, "Property value not a variant."); - return FALSE; - } - - dbus_message_iter_recurse(i, &variant_i); - - switch (dbus_message_iter_get_arg_type(&variant_i)) { - case DBUS_TYPE_BYTE: { - uint8_t value; - dbus_message_iter_get_basic(&variant_i, &value); - - if (g_str_equal(key, "Codec") == TRUE) - self->data->codec = value; - - break; - } - case DBUS_TYPE_STRING: { - const char *value; - dbus_message_iter_get_basic(&variant_i, &value); - - if (g_str_equal(key, "UUID") == TRUE) { - g_free(self->data->uuid); - self->data->uuid = g_strdup(value); - } - - break; - } - case DBUS_TYPE_ARRAY: { - DBusMessageIter array_i; - char *value; - int size; - - dbus_message_iter_recurse(&variant_i, &array_i); - dbus_message_iter_get_fixed_array(&array_i, &value, &size); - - if (g_str_equal(key, "Configuration")) { - g_free(self->data->config); - self->data->config = g_new0(guint8, size); - self->data->config_size = size; - memcpy(self->data->config, value, size); - } - - break; - } - } - - return TRUE; -} - -static gboolean gst_avdtp_sink_transport_acquire(GstAvdtpSink *self) -{ - DBusMessage *msg, *reply; - DBusError err; - const char *access_type = "w"; - int fd; - uint16_t imtu, omtu; - - dbus_error_init(&err); - - if (self->data->conn == NULL) - self->data->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); - - msg = dbus_message_new_method_call("org.bluez", self->transport, - "org.bluez.MediaTransport", - "Acquire"); - - dbus_message_append_args(msg, DBUS_TYPE_STRING, &access_type, - DBUS_TYPE_INVALID); - - reply = dbus_connection_send_with_reply_and_block(self->data->conn, - msg, -1, &err); - - dbus_message_unref(msg); - - if (dbus_error_is_set(&err)) - goto fail; - - if (dbus_message_get_args(reply, &err, DBUS_TYPE_UNIX_FD, &fd, - DBUS_TYPE_UINT16, &imtu, - DBUS_TYPE_UINT16, &omtu, - DBUS_TYPE_INVALID) == FALSE) - goto fail; - - dbus_message_unref(reply); - - self->stream = g_io_channel_unix_new(fd); - g_io_channel_set_encoding(self->stream, NULL, NULL); - g_io_channel_set_close_on_unref(self->stream, TRUE); - self->data->link_mtu = omtu; - GST_DEBUG_OBJECT(self, "stream_fd=%d mtu=%d", fd, omtu); - - return TRUE; - -fail: - GST_ERROR_OBJECT(self, "Failed to acquire transport stream: %s", - err.message); - - dbus_error_free(&err); - - if (reply) - dbus_message_unref(reply); - - return FALSE; -} - -static gboolean gst_avdtp_sink_transport_get_properties(GstAvdtpSink *self) -{ - DBusMessage *msg, *reply; - DBusMessageIter arg_i, ele_i; - DBusError err; - - dbus_error_init(&err); - - /* Transport need to be acquire first to make sure the MTUs are - available */ - if (gst_avdtp_sink_transport_acquire(self) == FALSE) - return FALSE; - - msg = dbus_message_new_method_call("org.bluez", self->transport, - "org.bluez.MediaTransport", - "GetProperties"); - reply = dbus_connection_send_with_reply_and_block(self->data->conn, - msg, -1, &err); - - if (dbus_error_is_set(&err) || reply == NULL) { - GST_ERROR_OBJECT(self, "Failed to get transport properties: %s", - err.message); - goto fail; - } - - if (!dbus_message_iter_init(reply, &arg_i)) { - GST_ERROR_OBJECT(self, "GetProperties reply has no arguments."); - goto fail; - } - - if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) { - GST_ERROR_OBJECT(self, "GetProperties argument is not an array."); - goto fail; - } - - dbus_message_iter_recurse(&arg_i, &ele_i); - while (dbus_message_iter_get_arg_type(&ele_i) != DBUS_TYPE_INVALID) { - - if (dbus_message_iter_get_arg_type(&ele_i) == - DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter dict_i; - - dbus_message_iter_recurse(&ele_i, &dict_i); - - gst_avdtp_sink_transport_parse_property(self, &dict_i); - } - - if (!dbus_message_iter_next(&ele_i)) - break; - } - - return gst_avdtp_sink_update_caps(self); - -fail: - dbus_message_unref(msg); - dbus_message_unref(reply); - return FALSE; - -} - -static gboolean gst_avdtp_sink_start(GstBaseSink *basesink) -{ - GstAvdtpSink *self = GST_AVDTP_SINK(basesink); - gint sk; - gint err; - - GST_INFO_OBJECT(self, "start"); - - self->data = g_new0(struct bluetooth_data, 1); - - self->stream = NULL; - self->stream_caps = NULL; - self->mp3_using_crc = -1; - self->channel_mode = -1; - - if (self->transport != NULL) - return gst_avdtp_sink_transport_get_properties(self); - - self->watch_id = 0; - - sk = bt_audio_service_open(); - if (sk < 0) { - err = -errno; - GST_ERROR_OBJECT(self, "Cannot open connection to bt " - "audio service: %s %d", strerror(-err), -err); - return FALSE; - } - - self->server = g_io_channel_unix_new(sk); - g_io_channel_set_encoding(self->server, NULL, NULL); - self->watch_id = g_io_add_watch(self->server, G_IO_HUP | G_IO_ERR | - G_IO_NVAL, server_callback, self); - - if (!gst_avdtp_sink_get_capabilities(self)) { - GST_ERROR_OBJECT(self, "failed to get capabilities " - "from device"); - goto failed; - } - - return TRUE; - -failed: - bt_audio_service_close(sk); - return FALSE; -} - -static gboolean gst_avdtp_sink_stream_start(GstAvdtpSink *self) -{ - gchar buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_start_stream_req *req = (void *) buf; - struct bt_start_stream_rsp *rsp = (void *) buf; - struct bt_new_stream_ind *ind = (void *) buf; - int err; - - if (self->transport != NULL) - return gst_avdtp_sink_conf_recv_stream_fd(self); - - memset(req, 0, sizeof(buf)); - req->h.type = BT_REQUEST; - req->h.name = BT_START_STREAM; - req->h.length = sizeof(*req); - - err = gst_avdtp_sink_audioservice_send(self, &req->h); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error occurred while sending " - "start packet"); - return FALSE; - } - - rsp->h.length = sizeof(*rsp); - err = gst_avdtp_sink_audioservice_expect(self, &rsp->h, - BT_START_STREAM); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error while stream " - "start confirmation"); - return FALSE; - } - - ind->h.length = sizeof(*ind); - err = gst_avdtp_sink_audioservice_expect(self, &ind->h, - BT_NEW_STREAM); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error while receiving " - "stream filedescriptor"); - return FALSE; - } - - if (!gst_avdtp_sink_conf_recv_stream_fd(self)) - return FALSE; - - return TRUE; -} - -static gboolean gst_avdtp_sink_init_mp3_pkt_conf( - GstAvdtpSink *self, GstCaps *caps, - mpeg_capabilities_t *pkt) -{ - const GValue *value = NULL; - gint rate, layer; - const gchar *name; - GstStructure *structure = gst_caps_get_structure(caps, 0); - - name = gst_structure_get_name(structure); - - if (!(IS_MPEG_AUDIO(name))) { - GST_ERROR_OBJECT(self, "Unexpected format %s, " - "was expecting mp3", name); - return FALSE; - } - - /* layer */ - value = gst_structure_get_value(structure, "layer"); - layer = g_value_get_int(value); - if (layer == 1) - pkt->layer = BT_MPEG_LAYER_1; - else if (layer == 2) - pkt->layer = BT_MPEG_LAYER_2; - else if (layer == 3) - pkt->layer = BT_MPEG_LAYER_3; - else { - GST_ERROR_OBJECT(self, "Unexpected layer: %d", layer); - return FALSE; - } - - /* crc */ - if (self->mp3_using_crc != -1) - pkt->crc = self->mp3_using_crc; - else { - GST_ERROR_OBJECT(self, "No info about crc was received, " - " can't proceed"); - return FALSE; - } - - /* channel mode */ - if (self->channel_mode != -1) - pkt->channel_mode = self->channel_mode; - else { - GST_ERROR_OBJECT(self, "No info about channel mode " - "received, can't proceed"); - return FALSE; - } - - /* mpf - we will only use the mandatory one */ - pkt->mpf = 0; - - value = gst_structure_get_value(structure, "rate"); - rate = g_value_get_int(value); - if (rate == 44100) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_44100; - else if (rate == 48000) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_48000; - else if (rate == 32000) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_32000; - else if (rate == 24000) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_24000; - else if (rate == 22050) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_22050; - else if (rate == 16000) - pkt->frequency = BT_MPEG_SAMPLING_FREQ_16000; - else { - GST_ERROR_OBJECT(self, "Invalid rate while setting caps"); - return FALSE; - } - - /* vbr - we always say its vbr, we don't have how to know it */ - pkt->bitrate = 0x8000; - - return TRUE; -} - -static gboolean gst_avdtp_sink_configure(GstAvdtpSink *self, - GstCaps *caps) -{ - gchar buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_open_req *open_req = (void *) buf; - struct bt_open_rsp *open_rsp = (void *) buf; - struct bt_set_configuration_req *req = (void *) buf; - struct bt_set_configuration_rsp *rsp = (void *) buf; - gboolean ret; - gchar *temp; - GstStructure *structure; - codec_capabilities_t *codec = NULL; - int err; - - temp = gst_caps_to_string(caps); - GST_DEBUG_OBJECT(self, "configuring device with caps: %s", temp); - g_free(temp); - - /* Transport already configured */ - if (self->transport != NULL) - return TRUE; - - structure = gst_caps_get_structure(caps, 0); - - if (gst_structure_has_name(structure, "audio/x-sbc")) - codec = (void *) gst_avdtp_find_caps(self, BT_A2DP_SBC_SINK); - else if (gst_structure_has_name(structure, "audio/mpeg")) - codec = (void *) gst_avdtp_find_caps(self, BT_A2DP_MPEG12_SINK); - - if (codec == NULL) { - GST_ERROR_OBJECT(self, "Couldn't parse caps " - "to packet configuration"); - return FALSE; - } - - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - open_req->h.type = BT_REQUEST; - open_req->h.name = BT_OPEN; - open_req->h.length = sizeof(*open_req); - - strncpy(open_req->destination, self->device, 18); - open_req->seid = codec->seid; - open_req->lock = BT_WRITE_LOCK; - - err = gst_avdtp_sink_audioservice_send(self, &open_req->h); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error occurred while sending " - "open packet"); - return FALSE; - } - - open_rsp->h.length = sizeof(*open_rsp); - err = gst_avdtp_sink_audioservice_expect(self, &open_rsp->h, - BT_OPEN); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error while receiving device " - "confirmation"); - return FALSE; - } - - memset(req, 0, sizeof(buf)); - req->h.type = BT_REQUEST; - req->h.name = BT_SET_CONFIGURATION; - req->h.length = sizeof(*req); - memcpy(&req->codec, codec, sizeof(req->codec)); - - if (codec->type == BT_A2DP_SBC_SINK) - ret = gst_avdtp_sink_init_sbc_pkt_conf(self, caps, - (void *) &req->codec); - else - ret = gst_avdtp_sink_init_mp3_pkt_conf(self, caps, - (void *) &req->codec); - - if (!ret) { - GST_ERROR_OBJECT(self, "Couldn't parse caps " - "to packet configuration"); - return FALSE; - } - - req->h.length += req->codec.length - sizeof(req->codec); - err = gst_avdtp_sink_audioservice_send(self, &req->h); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error occurred while sending " - "configurarion packet"); - return FALSE; - } - - rsp->h.length = sizeof(*rsp); - err = gst_avdtp_sink_audioservice_expect(self, &rsp->h, - BT_SET_CONFIGURATION); - if (err < 0) { - GST_ERROR_OBJECT(self, "Error while receiving device " - "confirmation"); - return FALSE; - } - - self->data->link_mtu = rsp->link_mtu; - - return TRUE; -} - -static GstFlowReturn gst_avdtp_sink_preroll(GstBaseSink *basesink, - GstBuffer *buffer) -{ - GstAvdtpSink *sink = GST_AVDTP_SINK(basesink); - gboolean ret; - - GST_AVDTP_SINK_MUTEX_LOCK(sink); - - ret = gst_avdtp_sink_stream_start(sink); - - GST_AVDTP_SINK_MUTEX_UNLOCK(sink); - - if (!ret) - return GST_FLOW_ERROR; - - return GST_FLOW_OK; -} - -static GstFlowReturn gst_avdtp_sink_render(GstBaseSink *basesink, - GstBuffer *buffer) -{ - GstAvdtpSink *self = GST_AVDTP_SINK(basesink); - ssize_t ret; - int fd; - - fd = g_io_channel_unix_get_fd(self->stream); - - ret = write(fd, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer)); - if (ret < 0) { - GST_ERROR_OBJECT(self, "Error while writting to socket: %s", - strerror(errno)); - return GST_FLOW_ERROR; - } - - return GST_FLOW_OK; -} - -static gboolean gst_avdtp_sink_unlock(GstBaseSink *basesink) -{ - GstAvdtpSink *self = GST_AVDTP_SINK(basesink); - - if (self->stream != NULL) - g_io_channel_flush(self->stream, NULL); - - return TRUE; -} - -static GstFlowReturn gst_avdtp_sink_buffer_alloc(GstBaseSink *basesink, - guint64 offset, guint size, GstCaps *caps, - GstBuffer **buf) -{ - GstAvdtpSink *self = GST_AVDTP_SINK(basesink); - - *buf = gst_buffer_new_and_alloc(size); - if (!(*buf)) { - GST_ERROR_OBJECT(self, "buffer allocation failed"); - return GST_FLOW_ERROR; - } - - gst_buffer_set_caps(*buf, caps); - - GST_BUFFER_OFFSET(*buf) = offset; - - return GST_FLOW_OK; -} - -static void gst_avdtp_sink_class_init(GstAvdtpSinkClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GstBaseSinkClass *basesink_class = GST_BASE_SINK_CLASS(klass); - - parent_class = g_type_class_peek_parent(klass); - - object_class->finalize = GST_DEBUG_FUNCPTR( - gst_avdtp_sink_finalize); - object_class->set_property = GST_DEBUG_FUNCPTR( - gst_avdtp_sink_set_property); - object_class->get_property = GST_DEBUG_FUNCPTR( - gst_avdtp_sink_get_property); - - basesink_class->start = GST_DEBUG_FUNCPTR(gst_avdtp_sink_start); - basesink_class->stop = GST_DEBUG_FUNCPTR(gst_avdtp_sink_stop); - basesink_class->render = GST_DEBUG_FUNCPTR( - gst_avdtp_sink_render); - basesink_class->preroll = GST_DEBUG_FUNCPTR( - gst_avdtp_sink_preroll); - basesink_class->unlock = GST_DEBUG_FUNCPTR( - gst_avdtp_sink_unlock); - basesink_class->event = GST_DEBUG_FUNCPTR( - gst_avdtp_sink_event); - - basesink_class->buffer_alloc = - GST_DEBUG_FUNCPTR(gst_avdtp_sink_buffer_alloc); - - g_object_class_install_property(object_class, PROP_DEVICE, - g_param_spec_string("device", "Device", - "Bluetooth remote device address", - NULL, G_PARAM_READWRITE)); - - g_object_class_install_property(object_class, PROP_AUTOCONNECT, - g_param_spec_boolean("auto-connect", - "Auto-connect", - "Automatically attempt to connect " - "to device", DEFAULT_AUTOCONNECT, - G_PARAM_READWRITE)); - - g_object_class_install_property(object_class, PROP_TRANSPORT, - g_param_spec_string("transport", - "Transport", - "Use configured transport", - NULL, G_PARAM_READWRITE)); - - GST_DEBUG_CATEGORY_INIT(avdtp_sink_debug, "avdtpsink", 0, - "A2DP headset sink element"); -} - -static void gst_avdtp_sink_init(GstAvdtpSink *self, - GstAvdtpSinkClass *klass) -{ - self->device = NULL; - self->transport = NULL; - self->data = NULL; - - self->stream = NULL; - - self->dev_caps = NULL; - - self->autoconnect = DEFAULT_AUTOCONNECT; - - self->sink_lock = g_mutex_new(); - - /* FIXME this is for not synchronizing with clock, should be tested - * with devices to see the behaviour - gst_base_sink_set_sync(GST_BASE_SINK(self), FALSE); - */ -} - -static int gst_avdtp_sink_audioservice_send(GstAvdtpSink *self, - const bt_audio_msg_header_t *msg) -{ - ssize_t written; - const char *type, *name; - uint16_t length; - int fd, err; - - length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE; - - fd = g_io_channel_unix_get_fd(self->server); - - written = write(fd, msg, length); - if (written < 0) { - err = -errno; - GST_ERROR_OBJECT(self, "Error sending data to audio service:" - " %s", strerror(-err)); - return err; - } - - type = bt_audio_strtype(msg->type); - name = bt_audio_strname(msg->name); - - GST_DEBUG_OBJECT(self, "sent: %s -> %s", type, name); - - return 0; -} - -static int gst_avdtp_sink_audioservice_recv(GstAvdtpSink *self, - bt_audio_msg_header_t *inmsg) -{ - ssize_t bytes_read; - const char *type, *name; - uint16_t length; - int fd, err = 0; - - length = inmsg->length ? inmsg->length : BT_SUGGESTED_BUFFER_SIZE; - - fd = g_io_channel_unix_get_fd(self->server); - - bytes_read = read(fd, inmsg, length); - if (bytes_read < 0) { - err = -errno; - GST_ERROR_OBJECT(self, "Error receiving data from " - "audio service: %s", strerror(-err)); - return err; - } - - type = bt_audio_strtype(inmsg->type); - if (!type) { - err = -EINVAL; - GST_ERROR_OBJECT(self, "Bogus message type %d " - "received from audio service", - inmsg->type); - } - - name = bt_audio_strname(inmsg->name); - if (!name) { - err = -EINVAL; - GST_ERROR_OBJECT(self, "Bogus message name %d " - "received from audio service", - inmsg->name); - } - - if (inmsg->type == BT_ERROR) { - bt_audio_error_t *msg = (void *) inmsg; - err = -EINVAL; - GST_ERROR_OBJECT(self, "%s failed : " - "%s(%d)", - name, - strerror(msg->posix_errno), - msg->posix_errno); - } - - GST_DEBUG_OBJECT(self, "received: %s <- %s", type, name); - - return err; -} - -static int gst_avdtp_sink_audioservice_expect(GstAvdtpSink *self, - bt_audio_msg_header_t *outmsg, - guint8 expected_name) -{ - int err; - - err = gst_avdtp_sink_audioservice_recv(self, outmsg); - if (err < 0) - return err; - - if (outmsg->name != expected_name) - return -EINVAL; - - return 0; -} - -gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin) -{ - return gst_element_register(plugin, "avdtpsink", GST_RANK_NONE, - GST_TYPE_AVDTP_SINK); -} - - -/* public functions */ -GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink) -{ - if (sink->dev_caps == NULL) - return NULL; - - return gst_caps_copy(sink->dev_caps); -} - -gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *self, - GstCaps *caps) -{ - gboolean ret; - - GST_DEBUG_OBJECT(self, "setting device caps"); - GST_AVDTP_SINK_MUTEX_LOCK(self); - ret = gst_avdtp_sink_configure(self, caps); - - if (self->stream_caps) - gst_caps_unref(self->stream_caps); - self->stream_caps = gst_caps_ref(caps); - - GST_AVDTP_SINK_MUTEX_UNLOCK(self); - - return ret; -} - -guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink) -{ - return sink->data->link_mtu; -} - -void gst_avdtp_sink_set_device(GstAvdtpSink *self, const gchar *dev) -{ - if (self->device != NULL) - g_free(self->device); - - GST_LOG_OBJECT(self, "Setting device: %s", dev); - self->device = g_strdup(dev); -} - -void gst_avdtp_sink_set_transport(GstAvdtpSink *self, const gchar *trans) -{ - if (self->transport != NULL) - g_free(self->transport); - - GST_LOG_OBJECT(self, "Setting transport: %s", trans); - self->transport = g_strdup(trans); -} - -gchar *gst_avdtp_sink_get_device(GstAvdtpSink *self) -{ - return g_strdup(self->device); -} - -gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *self) -{ - return g_strdup(self->transport); -} - -void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc) -{ - gint new_crc; - - new_crc = crc ? CRC_PROTECTED : CRC_UNPROTECTED; - - /* test if we already received a different crc */ - if (self->mp3_using_crc != -1 && new_crc != self->mp3_using_crc) { - GST_WARNING_OBJECT(self, "crc changed during stream"); - return; - } - self->mp3_using_crc = new_crc; - -} - -void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self, - const gchar *mode) -{ - gint new_mode; - - new_mode = gst_avdtp_sink_get_channel_mode(mode); - - if (self->channel_mode != -1 && new_mode != self->channel_mode) { - GST_WARNING_OBJECT(self, "channel mode changed during stream"); - return; - } - - self->channel_mode = new_mode; - if (self->channel_mode == -1) - GST_WARNING_OBJECT(self, "Received invalid channel " - "mode: %s", mode); -} diff -Nru bluez-4.101/audio/gstavdtpsink.h bluez-5.23/audio/gstavdtpsink.h --- bluez-4.101/audio/gstavdtpsink.h 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/audio/gstavdtpsink.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 - * - */ - -#ifndef __GST_AVDTP_SINK_H -#define __GST_AVDTP_SINK_H - -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_AVDTP_SINK \ - (gst_avdtp_sink_get_type()) -#define GST_AVDTP_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AVDTP_SINK,\ - GstAvdtpSink)) -#define GST_AVDTP_SINK_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AVDTP_SINK,\ - GstAvdtpSinkClass)) -#define GST_IS_AVDTP_SINK(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AVDTP_SINK)) -#define GST_IS_AVDTP_SINK_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AVDTP_SINK)) - -typedef struct _GstAvdtpSink GstAvdtpSink; -typedef struct _GstAvdtpSinkClass GstAvdtpSinkClass; - -struct bluetooth_data; - -struct _GstAvdtpSink { - GstBaseSink sink; - - gchar *device; - gchar *transport; - GIOChannel *stream; - - struct bluetooth_data *data; - gboolean autoconnect; - GIOChannel *server; - - /* mp3 stream data (outside caps data)*/ - gint mp3_using_crc; - gint channel_mode; - - /* stream connection data */ - GstCaps *stream_caps; - - GstCaps *dev_caps; - - GMutex *sink_lock; - - guint watch_id; -}; - -struct _GstAvdtpSinkClass { - GstBaseSinkClass parent_class; -}; - -GType gst_avdtp_sink_get_type(void); - -GstCaps *gst_avdtp_sink_get_device_caps(GstAvdtpSink *sink); -gboolean gst_avdtp_sink_set_device_caps(GstAvdtpSink *sink, - GstCaps *caps); - -guint gst_avdtp_sink_get_link_mtu(GstAvdtpSink *sink); - -void gst_avdtp_sink_set_device(GstAvdtpSink *sink, - const gchar* device); - -void gst_avdtp_sink_set_transport(GstAvdtpSink *sink, - const gchar *transport); - -gchar *gst_avdtp_sink_get_device(GstAvdtpSink *sink); - -gchar *gst_avdtp_sink_get_transport(GstAvdtpSink *sink); - -gboolean gst_avdtp_sink_plugin_init(GstPlugin *plugin); - -void gst_avdtp_sink_set_crc(GstAvdtpSink *self, gboolean crc); - -void gst_avdtp_sink_set_channel_mode(GstAvdtpSink *self, - const gchar *mode); - - -G_END_DECLS - -#endif /* __GST_AVDTP_SINK_H */ diff -Nru bluez-4.101/audio/gstbluetooth.c bluez-5.23/audio/gstbluetooth.c --- bluez-4.101/audio/gstbluetooth.c 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/audio/gstbluetooth.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include - -#include - -#include "gstsbcutil.h" -#include - -#include "gstsbcenc.h" -#include "gstsbcdec.h" -#include "gstsbcparse.h" -#include "gstavdtpsink.h" -#include "gsta2dpsink.h" -#include "gstrtpsbcpay.h" - -static GstStaticCaps sbc_caps = GST_STATIC_CAPS("audio/x-sbc"); - -#define SBC_CAPS (gst_static_caps_get(&sbc_caps)) - -static void sbc_typefind(GstTypeFind *tf, gpointer ignore) -{ - GstCaps *caps; - guint8 *aux; - sbc_t sbc; - guint8 *data = gst_type_find_peek(tf, 0, 32); - - if (data == NULL) - return; - - if (sbc_init(&sbc, 0) < 0) - return; - - aux = g_new(guint8, 32); - memcpy(aux, data, 32); - if (sbc_parse(&sbc, aux, 32) < 0) - goto done; - - caps = gst_sbc_parse_caps_from_sbc(&sbc); - gst_type_find_suggest(tf, GST_TYPE_FIND_POSSIBLE, caps); - gst_caps_unref(caps); - -done: - g_free(aux); - sbc_finish(&sbc); -} - -static gchar *sbc_exts[] = { "sbc", NULL }; - -static gboolean plugin_init(GstPlugin *plugin) -{ - GST_INFO("Bluetooth plugin %s", VERSION); - - if (gst_type_find_register(plugin, "sbc", - GST_RANK_PRIMARY, sbc_typefind, sbc_exts, - SBC_CAPS, NULL, NULL) == FALSE) - return FALSE; - - if (!gst_sbc_enc_plugin_init(plugin)) - return FALSE; - - if (!gst_sbc_dec_plugin_init(plugin)) - return FALSE; - - if (!gst_sbc_parse_plugin_init(plugin)) - return FALSE; - - if (!gst_avdtp_sink_plugin_init(plugin)) - return FALSE; - - if (!gst_a2dp_sink_plugin_init(plugin)) - return FALSE; - - if (!gst_rtp_sbc_pay_plugin_init(plugin)) - return FALSE; - - return TRUE; -} - -extern GstPluginDesc gst_plugin_desc __attribute__ ((visibility("default"))); - -GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, GST_VERSION_MINOR, - "bluetooth", "Bluetooth plugin library", - plugin_init, VERSION, "LGPL", "BlueZ", "http://www.bluez.org/") diff -Nru bluez-4.101/audio/gstpragma.h bluez-5.23/audio/gstpragma.h --- bluez-4.101/audio/gstpragma.h 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/audio/gstpragma.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 - * - */ - -/* #pragma GCC diagnostic warning "-Wmissing-declarations" */ diff -Nru bluez-4.101/audio/gstrtpsbcpay.c bluez-5.23/audio/gstrtpsbcpay.c --- bluez-4.101/audio/gstrtpsbcpay.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gstrtpsbcpay.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,351 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include "gstpragma.h" -#include "gstrtpsbcpay.h" -#include -#include - -#define RTP_SBC_PAYLOAD_HEADER_SIZE 1 -#define DEFAULT_MIN_FRAMES 0 -#define RTP_SBC_HEADER_TOTAL (12 + RTP_SBC_PAYLOAD_HEADER_SIZE) - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct rtp_payload { - guint8 frame_count:4; - guint8 rfa0:1; - guint8 is_last_fragment:1; - guint8 is_first_fragment:1; - guint8 is_fragmented:1; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct rtp_payload { - guint8 is_fragmented:1; - guint8 is_first_fragment:1; - guint8 is_last_fragment:1; - guint8 rfa0:1; - guint8 frame_count:4; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif - -enum { - PROP_0, - PROP_MIN_FRAMES -}; - -GST_DEBUG_CATEGORY_STATIC(gst_rtp_sbc_pay_debug); -#define GST_CAT_DEFAULT gst_rtp_sbc_pay_debug - -GST_BOILERPLATE(GstRtpSBCPay, gst_rtp_sbc_pay, GstBaseRTPPayload, - GST_TYPE_BASE_RTP_PAYLOAD); - -static const GstElementDetails gst_rtp_sbc_pay_details = - GST_ELEMENT_DETAILS("RTP packet payloader", - "Codec/Payloader/Network", - "Payload SBC audio as RTP packets", - "Thiago Sousa Santos " - ""); - -static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory = - GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc, " - "rate = (int) { 16000, 32000, 44100, 48000 }, " - "channels = (int) [ 1, 2 ], " - "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, " - "blocks = (int) { 4, 8, 12, 16 }, " - "subbands = (int) { 4, 8 }, " - "allocation = (string) { \"snr\", \"loudness\" }, " - "bitpool = (int) [ 2, 64 ]") - ); - -static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory = - GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS( - "application/x-rtp, " - "media = (string) \"audio\"," - "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " - "clock-rate = (int) { 16000, 32000, 44100, 48000 }," - "encoding-name = (string) \"SBC\"") - ); - -static void gst_rtp_sbc_pay_set_property(GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec); -static void gst_rtp_sbc_pay_get_property(GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec); - -static gint gst_rtp_sbc_pay_get_frame_len(gint subbands, gint channels, - gint blocks, gint bitpool, const gchar *channel_mode) -{ - gint len; - gint join; - - len = 4 + (4 * subbands * channels)/8; - - if (strcmp(channel_mode, "mono") == 0 || - strcmp(channel_mode, "dual") == 0) - len += ((blocks * channels * bitpool) + 7) / 8; - else { - join = strcmp(channel_mode, "joint") == 0 ? 1 : 0; - len += ((join * subbands + blocks * bitpool) + 7) / 8; - } - - return len; -} - -static gboolean gst_rtp_sbc_pay_set_caps(GstBaseRTPPayload *payload, - GstCaps *caps) -{ - GstRtpSBCPay *sbcpay; - gint rate, subbands, channels, blocks, bitpool; - gint frame_len; - const gchar *channel_mode; - GstStructure *structure; - - sbcpay = GST_RTP_SBC_PAY(payload); - - structure = gst_caps_get_structure(caps, 0); - if (!gst_structure_get_int(structure, "rate", &rate)) - return FALSE; - if (!gst_structure_get_int(structure, "channels", &channels)) - return FALSE; - if (!gst_structure_get_int(structure, "blocks", &blocks)) - return FALSE; - if (!gst_structure_get_int(structure, "bitpool", &bitpool)) - return FALSE; - if (!gst_structure_get_int(structure, "subbands", &subbands)) - return FALSE; - - channel_mode = gst_structure_get_string(structure, "mode"); - if (!channel_mode) - return FALSE; - - frame_len = gst_rtp_sbc_pay_get_frame_len(subbands, channels, blocks, - bitpool, channel_mode); - - sbcpay->frame_length = frame_len; - - gst_basertppayload_set_options(payload, "audio", TRUE, "SBC", rate); - - GST_DEBUG_OBJECT(payload, "calculated frame length: %d ", frame_len); - - return gst_basertppayload_set_outcaps(payload, NULL); -} - -static GstFlowReturn gst_rtp_sbc_pay_flush_buffers(GstRtpSBCPay *sbcpay) -{ - guint available; - guint max_payload; - GstBuffer *outbuf; - guint8 *payload_data; - guint frame_count; - guint payload_length; - struct rtp_payload *payload; - - if (sbcpay->frame_length == 0) { - GST_ERROR_OBJECT(sbcpay, "Frame length is 0"); - return GST_FLOW_ERROR; - } - - available = gst_adapter_available(sbcpay->adapter); - - max_payload = gst_rtp_buffer_calc_payload_len( - GST_BASE_RTP_PAYLOAD_MTU(sbcpay) - RTP_SBC_PAYLOAD_HEADER_SIZE, - 0, 0); - - max_payload = MIN(max_payload, available); - frame_count = max_payload / sbcpay->frame_length; - payload_length = frame_count * sbcpay->frame_length; - if (payload_length == 0) /* Nothing to send */ - return GST_FLOW_OK; - - outbuf = gst_rtp_buffer_new_allocate(payload_length + - RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); - - gst_rtp_buffer_set_payload_type(outbuf, - GST_BASE_RTP_PAYLOAD_PT(sbcpay)); - - payload_data = gst_rtp_buffer_get_payload(outbuf); - payload = (struct rtp_payload *) payload_data; - memset(payload, 0, sizeof(struct rtp_payload)); - payload->frame_count = frame_count; - - gst_adapter_copy(sbcpay->adapter, payload_data + - RTP_SBC_PAYLOAD_HEADER_SIZE, 0, payload_length); - gst_adapter_flush(sbcpay->adapter, payload_length); - - GST_BUFFER_TIMESTAMP(outbuf) = sbcpay->timestamp; - GST_DEBUG_OBJECT(sbcpay, "Pushing %d bytes", payload_length); - - return gst_basertppayload_push(GST_BASE_RTP_PAYLOAD(sbcpay), outbuf); -} - -static GstFlowReturn gst_rtp_sbc_pay_handle_buffer(GstBaseRTPPayload *payload, - GstBuffer *buffer) -{ - GstRtpSBCPay *sbcpay; - guint available; - - /* FIXME check for negotiation */ - - sbcpay = GST_RTP_SBC_PAY(payload); - sbcpay->timestamp = GST_BUFFER_TIMESTAMP(buffer); - - gst_adapter_push(sbcpay->adapter, buffer); - - available = gst_adapter_available(sbcpay->adapter); - if (available + RTP_SBC_HEADER_TOTAL >= - GST_BASE_RTP_PAYLOAD_MTU(sbcpay) || - (available > - (sbcpay->min_frames * sbcpay->frame_length))) - return gst_rtp_sbc_pay_flush_buffers(sbcpay); - - return GST_FLOW_OK; -} - -static gboolean gst_rtp_sbc_pay_handle_event(GstPad *pad, - GstEvent *event) -{ - GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(GST_PAD_PARENT(pad)); - - switch (GST_EVENT_TYPE(event)) { - case GST_EVENT_EOS: - gst_rtp_sbc_pay_flush_buffers(sbcpay); - break; - default: - break; - } - - return FALSE; -} - -static void gst_rtp_sbc_pay_base_init(gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&gst_rtp_sbc_pay_sink_factory)); - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&gst_rtp_sbc_pay_src_factory)); - - gst_element_class_set_details(element_class, &gst_rtp_sbc_pay_details); -} - -static void gst_rtp_sbc_pay_finalize(GObject *object) -{ - GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY(object); - g_object_unref(sbcpay->adapter); - - GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object)); -} - -static void gst_rtp_sbc_pay_class_init(GstRtpSBCPayClass *klass) -{ - GObjectClass *gobject_class; - GstBaseRTPPayloadClass *payload_class = - GST_BASE_RTP_PAYLOAD_CLASS(klass); - - gobject_class = G_OBJECT_CLASS(klass); - parent_class = g_type_class_peek_parent(klass); - - gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_finalize); - gobject_class->set_property = GST_DEBUG_FUNCPTR( - gst_rtp_sbc_pay_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR( - gst_rtp_sbc_pay_get_property); - - payload_class->set_caps = GST_DEBUG_FUNCPTR(gst_rtp_sbc_pay_set_caps); - payload_class->handle_buffer = GST_DEBUG_FUNCPTR( - gst_rtp_sbc_pay_handle_buffer); - payload_class->handle_event = GST_DEBUG_FUNCPTR( - gst_rtp_sbc_pay_handle_event); - - /* properties */ - g_object_class_install_property(G_OBJECT_CLASS(klass), - PROP_MIN_FRAMES, - g_param_spec_int("min-frames", "minimum frame number", - "Minimum quantity of frames to send in one packet " - "(-1 for maximum allowed by the mtu)", - -1, G_MAXINT, DEFAULT_MIN_FRAMES, G_PARAM_READWRITE)); - - GST_DEBUG_CATEGORY_INIT(gst_rtp_sbc_pay_debug, "rtpsbcpay", 0, - "RTP SBC payloader"); -} - -static void gst_rtp_sbc_pay_set_property(GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - GstRtpSBCPay *sbcpay; - - sbcpay = GST_RTP_SBC_PAY(object); - - switch (prop_id) { - case PROP_MIN_FRAMES: - sbcpay->min_frames = g_value_get_int(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void gst_rtp_sbc_pay_get_property(GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - GstRtpSBCPay *sbcpay; - - sbcpay = GST_RTP_SBC_PAY(object); - - switch (prop_id) { - case PROP_MIN_FRAMES: - g_value_set_int(value, sbcpay->min_frames); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void gst_rtp_sbc_pay_init(GstRtpSBCPay *self, GstRtpSBCPayClass *klass) -{ - self->adapter = gst_adapter_new(); - self->frame_length = 0; - self->timestamp = 0; - - self->min_frames = DEFAULT_MIN_FRAMES; -} - -gboolean gst_rtp_sbc_pay_plugin_init(GstPlugin *plugin) -{ - return gst_element_register(plugin, "rtpsbcpay", GST_RANK_NONE, - GST_TYPE_RTP_SBC_PAY); -} diff -Nru bluez-4.101/audio/gstrtpsbcpay.h bluez-5.23/audio/gstrtpsbcpay.h --- bluez-4.101/audio/gstrtpsbcpay.h 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/audio/gstrtpsbcpay.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#include -#include -#include - -G_BEGIN_DECLS - -#define GST_TYPE_RTP_SBC_PAY \ - (gst_rtp_sbc_pay_get_type()) -#define GST_RTP_SBC_PAY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SBC_PAY,\ - GstRtpSBCPay)) -#define GST_RTP_SBC_PAY_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SBC_PAY,\ - GstRtpSBCPayClass)) -#define GST_IS_RTP_SBC_PAY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SBC_PAY)) -#define GST_IS_RTP_SBC_PAY_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SBC_PAY)) - -typedef struct _GstRtpSBCPay GstRtpSBCPay; -typedef struct _GstRtpSBCPayClass GstRtpSBCPayClass; - -struct _GstRtpSBCPay { - GstBaseRTPPayload base; - - GstAdapter *adapter; - GstClockTime timestamp; - - guint frame_length; - - guint min_frames; -}; - -struct _GstRtpSBCPayClass { - GstBaseRTPPayloadClass parent_class; -}; - -GType gst_rtp_sbc_pay_get_type(void); - -gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin); - -G_END_DECLS diff -Nru bluez-4.101/audio/gstsbcdec.c bluez-5.23/audio/gstsbcdec.c --- bluez-4.101/audio/gstsbcdec.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gstsbcdec.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,221 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include - -#include "gstpragma.h" -#include "gstsbcutil.h" -#include "gstsbcdec.h" - -GST_DEBUG_CATEGORY_STATIC(sbc_dec_debug); -#define GST_CAT_DEFAULT sbc_dec_debug - -GST_BOILERPLATE(GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT); - -static const GstElementDetails sbc_dec_details = - GST_ELEMENT_DETAILS("Bluetooth SBC decoder", - "Codec/Decoder/Audio", - "Decode a SBC audio stream", - "Marcel Holtmann "); - -static GstStaticPadTemplate sbc_dec_sink_factory = - GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc")); - -static GstStaticPadTemplate sbc_dec_src_factory = - GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-raw-int, " - "rate = (int) { 16000, 32000, 44100, 48000 }, " - "channels = (int) [ 1, 2 ], " - "endianness = (int) BYTE_ORDER, " - "signed = (boolean) true, " - "width = (int) 16, " - "depth = (int) 16")); - -static GstFlowReturn sbc_dec_chain(GstPad *pad, GstBuffer *buffer) -{ - GstSbcDec *dec = GST_SBC_DEC(gst_pad_get_parent(pad)); - GstFlowReturn res = GST_FLOW_OK; - guint size, codesize, offset = 0; - guint8 *data; - - codesize = sbc_get_codesize(&dec->sbc); - - if (dec->buffer) { - GstBuffer *temp = buffer; - buffer = gst_buffer_span(dec->buffer, 0, buffer, - GST_BUFFER_SIZE(dec->buffer) + GST_BUFFER_SIZE(buffer)); - gst_buffer_unref(temp); - gst_buffer_unref(dec->buffer); - dec->buffer = NULL; - } - - data = GST_BUFFER_DATA(buffer); - size = GST_BUFFER_SIZE(buffer); - - while (offset < size) { - GstBuffer *output; - GstPadTemplate *template; - GstCaps *caps; - int consumed; - - res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad, - GST_BUFFER_OFFSET_NONE, - codesize, NULL, &output); - - if (res != GST_FLOW_OK) - goto done; - - consumed = sbc_decode(&dec->sbc, data + offset, size - offset, - GST_BUFFER_DATA(output), codesize, - NULL); - if (consumed <= 0) - break; - - /* we will reuse the same caps object */ - if (dec->outcaps == NULL) { - caps = gst_caps_new_simple("audio/x-raw-int", - "rate", G_TYPE_INT, - gst_sbc_parse_rate_from_sbc( - dec->sbc.frequency), - "channels", G_TYPE_INT, - gst_sbc_get_channel_number( - dec->sbc.mode), - NULL); - - template = gst_static_pad_template_get(&sbc_dec_src_factory); - - dec->outcaps = gst_caps_intersect(caps, - gst_pad_template_get_caps(template)); - - gst_caps_unref(caps); - } - - gst_buffer_set_caps(output, dec->outcaps); - - /* FIXME get a real timestamp */ - GST_BUFFER_TIMESTAMP(output) = GST_CLOCK_TIME_NONE; - - res = gst_pad_push(dec->srcpad, output); - if (res != GST_FLOW_OK) - goto done; - - offset += consumed; - } - - if (offset < size) - dec->buffer = gst_buffer_create_sub(buffer, - offset, size - offset); - -done: - gst_buffer_unref(buffer); - gst_object_unref(dec); - - return res; -} - -static GstStateChangeReturn sbc_dec_change_state(GstElement *element, - GstStateChange transition) -{ - GstSbcDec *dec = GST_SBC_DEC(element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_DEBUG("Setup subband codec"); - if (dec->buffer) { - gst_buffer_unref(dec->buffer); - dec->buffer = NULL; - } - sbc_init(&dec->sbc, 0); - dec->outcaps = NULL; - break; - - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG("Finish subband codec"); - if (dec->buffer) { - gst_buffer_unref(dec->buffer); - dec->buffer = NULL; - } - sbc_finish(&dec->sbc); - if (dec->outcaps) { - gst_caps_unref(dec->outcaps); - dec->outcaps = NULL; - } - break; - - default: - break; - } - - return parent_class->change_state(element, transition); -} - -static void gst_sbc_dec_base_init(gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&sbc_dec_sink_factory)); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&sbc_dec_src_factory)); - - gst_element_class_set_details(element_class, &sbc_dec_details); -} - -static void gst_sbc_dec_class_init(GstSbcDecClass *klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(klass); - - parent_class = g_type_class_peek_parent(klass); - - element_class->change_state = GST_DEBUG_FUNCPTR(sbc_dec_change_state); - - GST_DEBUG_CATEGORY_INIT(sbc_dec_debug, "sbcdec", 0, - "SBC decoding element"); -} - -static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass) -{ - self->sinkpad = gst_pad_new_from_static_template( - &sbc_dec_sink_factory, "sink"); - gst_pad_set_chain_function(self->sinkpad, GST_DEBUG_FUNCPTR( - sbc_dec_chain)); - gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); - - self->srcpad = gst_pad_new_from_static_template( - &sbc_dec_src_factory, "src"); - gst_element_add_pad(GST_ELEMENT(self), self->srcpad); - - self->outcaps = NULL; -} - -gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin) -{ - return gst_element_register(plugin, "sbcdec", GST_RANK_PRIMARY, - GST_TYPE_SBC_DEC); -} diff -Nru bluez-4.101/audio/gstsbcdec.h bluez-5.23/audio/gstsbcdec.h --- bluez-4.101/audio/gstsbcdec.h 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/audio/gstsbcdec.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 - -#include "sbc.h" - -G_BEGIN_DECLS - -#define GST_TYPE_SBC_DEC \ - (gst_sbc_dec_get_type()) -#define GST_SBC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_DEC,GstSbcDec)) -#define GST_SBC_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_DEC,GstSbcDecClass)) -#define GST_IS_SBC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_DEC)) -#define GST_IS_SBC_DEC_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_DEC)) - -typedef struct _GstSbcDec GstSbcDec; -typedef struct _GstSbcDecClass GstSbcDecClass; - -struct _GstSbcDec { - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; - - GstBuffer *buffer; - - /* caps for outgoing buffers */ - GstCaps *outcaps; - - sbc_t sbc; -}; - -struct _GstSbcDecClass { - GstElementClass parent_class; -}; - -GType gst_sbc_dec_get_type(void); - -gboolean gst_sbc_dec_plugin_init(GstPlugin *plugin); - -G_END_DECLS diff -Nru bluez-4.101/audio/gstsbcenc.c bluez-5.23/audio/gstsbcenc.c --- bluez-4.101/audio/gstsbcenc.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gstsbcenc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,601 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include - -#include "gstpragma.h" -#include "gstsbcutil.h" -#include "gstsbcenc.h" - -#define SBC_ENC_DEFAULT_MODE SBC_MODE_AUTO -#define SBC_ENC_DEFAULT_BLOCKS 0 -#define SBC_ENC_DEFAULT_SUB_BANDS 0 -#define SBC_ENC_DEFAULT_ALLOCATION SBC_AM_AUTO -#define SBC_ENC_DEFAULT_RATE 0 -#define SBC_ENC_DEFAULT_CHANNELS 0 - -#define SBC_ENC_BITPOOL_AUTO 1 -#define SBC_ENC_BITPOOL_MIN 2 -#define SBC_ENC_BITPOOL_MIN_STR "2" -#define SBC_ENC_BITPOOL_MAX 64 -#define SBC_ENC_BITPOOL_MAX_STR "64" - -GST_DEBUG_CATEGORY_STATIC(sbc_enc_debug); -#define GST_CAT_DEFAULT sbc_enc_debug - -#define GST_TYPE_SBC_MODE (gst_sbc_mode_get_type()) - -static GType gst_sbc_mode_get_type(void) -{ - static GType sbc_mode_type = 0; - static GEnumValue sbc_modes[] = { - { SBC_MODE_MONO, "Mono", "mono" }, - { SBC_MODE_DUAL_CHANNEL, "Dual Channel", "dual" }, - { SBC_MODE_STEREO, "Stereo", "stereo"}, - { SBC_MODE_JOINT_STEREO, "Joint Stereo", "joint" }, - { SBC_MODE_AUTO, "Auto", "auto" }, - { -1, NULL, NULL} - }; - - if (!sbc_mode_type) - sbc_mode_type = g_enum_register_static("GstSbcMode", sbc_modes); - - return sbc_mode_type; -} - -#define GST_TYPE_SBC_ALLOCATION (gst_sbc_allocation_get_type()) - -static GType gst_sbc_allocation_get_type(void) -{ - static GType sbc_allocation_type = 0; - static GEnumValue sbc_allocations[] = { - { SBC_AM_LOUDNESS, "Loudness", "loudness" }, - { SBC_AM_SNR, "SNR", "snr" }, - { SBC_AM_AUTO, "Auto", "auto" }, - { -1, NULL, NULL} - }; - - if (!sbc_allocation_type) - sbc_allocation_type = g_enum_register_static( - "GstSbcAllocation", sbc_allocations); - - return sbc_allocation_type; -} - -#define GST_TYPE_SBC_BLOCKS (gst_sbc_blocks_get_type()) - -static GType gst_sbc_blocks_get_type(void) -{ - static GType sbc_blocks_type = 0; - static GEnumValue sbc_blocks[] = { - { 0, "Auto", "auto" }, - { 4, "4", "4" }, - { 8, "8", "8" }, - { 12, "12", "12" }, - { 16, "16", "16" }, - { -1, NULL, NULL} - }; - - if (!sbc_blocks_type) - sbc_blocks_type = g_enum_register_static( - "GstSbcBlocks", sbc_blocks); - - return sbc_blocks_type; -} - -#define GST_TYPE_SBC_SUBBANDS (gst_sbc_subbands_get_type()) - -static GType gst_sbc_subbands_get_type(void) -{ - static GType sbc_subbands_type = 0; - static GEnumValue sbc_subbands[] = { - { 0, "Auto", "auto" }, - { 4, "4 subbands", "4" }, - { 8, "8 subbands", "8" }, - { -1, NULL, NULL} - }; - - if (!sbc_subbands_type) - sbc_subbands_type = g_enum_register_static( - "GstSbcSubbands", sbc_subbands); - - return sbc_subbands_type; -} - -enum { - PROP_0, - PROP_MODE, - PROP_ALLOCATION, - PROP_BLOCKS, - PROP_SUBBANDS, - PROP_BITPOOL -}; - -GST_BOILERPLATE(GstSbcEnc, gst_sbc_enc, GstElement, GST_TYPE_ELEMENT); - -static const GstElementDetails sbc_enc_details = - GST_ELEMENT_DETAILS("Bluetooth SBC encoder", - "Codec/Encoder/Audio", - "Encode a SBC audio stream", - "Marcel Holtmann "); - -static GstStaticPadTemplate sbc_enc_sink_factory = - GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-raw-int, " - "rate = (int) { 16000, 32000, 44100, 48000 }, " - "channels = (int) [ 1, 2 ], " - "endianness = (int) BYTE_ORDER, " - "signed = (boolean) true, " - "width = (int) 16, " - "depth = (int) 16")); - -static GstStaticPadTemplate sbc_enc_src_factory = - GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc, " - "rate = (int) { 16000, 32000, 44100, 48000 }, " - "channels = (int) [ 1, 2 ], " - "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, " - "blocks = (int) { 4, 8, 12, 16 }, " - "subbands = (int) { 4, 8 }, " - "allocation = (string) { \"snr\", \"loudness\" }, " - "bitpool = (int) [ " SBC_ENC_BITPOOL_MIN_STR - ", " SBC_ENC_BITPOOL_MAX_STR " ]")); - -gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps); - -static GstCaps *sbc_enc_generate_srcpad_caps(GstSbcEnc *enc) -{ - GstCaps *src_caps; - GstStructure *structure; - GEnumValue *enum_value; - GEnumClass *enum_class; - GValue *value; - - src_caps = gst_caps_copy(gst_pad_get_pad_template_caps(enc->srcpad)); - structure = gst_caps_get_structure(src_caps, 0); - - value = g_new0(GValue, 1); - - if (enc->rate != 0) - gst_sbc_util_set_structure_int_param(structure, "rate", - enc->rate, value); - - if (enc->channels != 0) - gst_sbc_util_set_structure_int_param(structure, "channels", - enc->channels, value); - - if (enc->subbands != 0) - gst_sbc_util_set_structure_int_param(structure, "subbands", - enc->subbands, value); - - if (enc->blocks != 0) - gst_sbc_util_set_structure_int_param(structure, "blocks", - enc->blocks, value); - - if (enc->bitpool != SBC_ENC_BITPOOL_AUTO) - gst_sbc_util_set_structure_int_param(structure, "bitpool", - enc->bitpool, value); - - if (enc->mode != SBC_ENC_DEFAULT_MODE) { - enum_class = g_type_class_ref(GST_TYPE_SBC_MODE); - enum_value = g_enum_get_value(enum_class, enc->mode); - gst_sbc_util_set_structure_string_param(structure, "mode", - enum_value->value_nick, value); - g_type_class_unref(enum_class); - } - - if (enc->allocation != SBC_AM_AUTO) { - enum_class = g_type_class_ref(GST_TYPE_SBC_ALLOCATION); - enum_value = g_enum_get_value(enum_class, enc->allocation); - gst_sbc_util_set_structure_string_param(structure, "allocation", - enum_value->value_nick, value); - g_type_class_unref(enum_class); - } - - g_free(value); - - return src_caps; -} - -static GstCaps *sbc_enc_src_getcaps(GstPad *pad) -{ - GstSbcEnc *enc; - - enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); - - return sbc_enc_generate_srcpad_caps(enc); -} - -static gboolean sbc_enc_src_setcaps(GstPad *pad, GstCaps *caps) -{ - GstSbcEnc *enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); - - GST_LOG_OBJECT(enc, "setting srcpad caps"); - - return gst_sbc_enc_fill_sbc_params(enc, caps); -} - -static GstCaps *sbc_enc_src_caps_fixate(GstSbcEnc *enc, GstCaps *caps) -{ - gchar *error_message = NULL; - GstCaps *result; - - result = gst_sbc_util_caps_fixate(caps, &error_message); - - if (!result) { - GST_WARNING_OBJECT(enc, "Invalid input caps caused parsing " - "error: %s", error_message); - g_free(error_message); - return NULL; - } - - return result; -} - -static GstCaps *sbc_enc_get_fixed_srcpad_caps(GstSbcEnc *enc) -{ - GstCaps *caps; - gboolean res = TRUE; - GstCaps *result_caps = NULL; - - caps = gst_pad_get_allowed_caps(enc->srcpad); - if (caps == NULL) - caps = sbc_enc_src_getcaps(enc->srcpad); - - if (caps == GST_CAPS_NONE || gst_caps_is_empty(caps)) { - res = FALSE; - goto done; - } - - result_caps = sbc_enc_src_caps_fixate(enc, caps); - -done: - gst_caps_unref(caps); - - if (!res) - return NULL; - - return result_caps; -} - -static gboolean sbc_enc_sink_setcaps(GstPad *pad, GstCaps *caps) -{ - GstSbcEnc *enc; - GstStructure *structure; - GstCaps *src_caps; - gint rate, channels; - gboolean res; - - enc = GST_SBC_ENC(GST_PAD_PARENT(pad)); - structure = gst_caps_get_structure(caps, 0); - - if (!gst_structure_get_int(structure, "rate", &rate)) - return FALSE; - if (!gst_structure_get_int(structure, "channels", &channels)) - return FALSE; - - enc->rate = rate; - enc->channels = channels; - - src_caps = sbc_enc_get_fixed_srcpad_caps(enc); - if (!src_caps) - return FALSE; - res = gst_pad_set_caps(enc->srcpad, src_caps); - gst_caps_unref(src_caps); - - return res; -} - -gboolean gst_sbc_enc_fill_sbc_params(GstSbcEnc *enc, GstCaps *caps) -{ - if (!gst_caps_is_fixed(caps)) { - GST_DEBUG_OBJECT(enc, "didn't receive fixed caps, " - "returning false"); - return FALSE; - } - - if (!gst_sbc_util_fill_sbc_params(&enc->sbc, caps)) - return FALSE; - - if (enc->rate != 0 && gst_sbc_parse_rate_from_sbc(enc->sbc.frequency) - != enc->rate) - goto fail; - - if (enc->channels != 0 && gst_sbc_get_channel_number(enc->sbc.mode) - != enc->channels) - goto fail; - - if (enc->blocks != 0 && gst_sbc_parse_blocks_from_sbc(enc->sbc.blocks) - != enc->blocks) - goto fail; - - if (enc->subbands != 0 && gst_sbc_parse_subbands_from_sbc( - enc->sbc.subbands) != enc->subbands) - goto fail; - - if (enc->mode != SBC_ENC_DEFAULT_MODE && enc->sbc.mode != enc->mode) - goto fail; - - if (enc->allocation != SBC_AM_AUTO && - enc->sbc.allocation != enc->allocation) - goto fail; - - if (enc->bitpool != SBC_ENC_BITPOOL_AUTO && - enc->sbc.bitpool != enc->bitpool) - goto fail; - - enc->codesize = sbc_get_codesize(&enc->sbc); - enc->frame_length = sbc_get_frame_length(&enc->sbc); - enc->frame_duration = sbc_get_frame_duration(&enc->sbc); - - GST_DEBUG_OBJECT(enc, "codesize: %d, frame_length: %d, frame_duration:" - " %d", enc->codesize, enc->frame_length, - enc->frame_duration); - - return TRUE; - -fail: - memset(&enc->sbc, 0, sizeof(sbc_t)); - return FALSE; -} - -static GstFlowReturn sbc_enc_chain(GstPad *pad, GstBuffer *buffer) -{ - GstSbcEnc *enc = GST_SBC_ENC(gst_pad_get_parent(pad)); - GstAdapter *adapter = enc->adapter; - GstFlowReturn res = GST_FLOW_OK; - - gst_adapter_push(adapter, buffer); - - while (gst_adapter_available(adapter) >= enc->codesize && - res == GST_FLOW_OK) { - GstBuffer *output; - GstCaps *caps; - const guint8 *data; - gint consumed; - - caps = GST_PAD_CAPS(enc->srcpad); - res = gst_pad_alloc_buffer_and_set_caps(enc->srcpad, - GST_BUFFER_OFFSET_NONE, - enc->frame_length, caps, - &output); - if (res != GST_FLOW_OK) - goto done; - - data = gst_adapter_peek(adapter, enc->codesize); - - consumed = sbc_encode(&enc->sbc, (gpointer) data, - enc->codesize, - GST_BUFFER_DATA(output), - GST_BUFFER_SIZE(output), NULL); - if (consumed <= 0) { - GST_DEBUG_OBJECT(enc, "comsumed < 0, codesize: %d", - enc->codesize); - break; - } - gst_adapter_flush(adapter, consumed); - - GST_BUFFER_TIMESTAMP(output) = GST_BUFFER_TIMESTAMP(buffer); - /* we have only 1 frame */ - GST_BUFFER_DURATION(output) = enc->frame_duration; - - res = gst_pad_push(enc->srcpad, output); - - if (res != GST_FLOW_OK) - goto done; - } - -done: - gst_object_unref(enc); - - return res; -} - -static GstStateChangeReturn sbc_enc_change_state(GstElement *element, - GstStateChange transition) -{ - GstSbcEnc *enc = GST_SBC_ENC(element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_DEBUG("Setup subband codec"); - sbc_init(&enc->sbc, 0); - break; - - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG("Finish subband codec"); - sbc_finish(&enc->sbc); - break; - - default: - break; - } - - return parent_class->change_state(element, transition); -} - -static void gst_sbc_enc_dispose(GObject *object) -{ - GstSbcEnc *enc = GST_SBC_ENC(object); - - if (enc->adapter != NULL) - g_object_unref(G_OBJECT(enc->adapter)); - - enc->adapter = NULL; -} - -static void gst_sbc_enc_base_init(gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&sbc_enc_sink_factory)); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&sbc_enc_src_factory)); - - gst_element_class_set_details(element_class, &sbc_enc_details); -} - -static void gst_sbc_enc_set_property(GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - GstSbcEnc *enc = GST_SBC_ENC(object); - - /* changes to those properties will only happen on the next caps - * negotiation */ - - switch (prop_id) { - case PROP_MODE: - enc->mode = g_value_get_enum(value); - break; - case PROP_ALLOCATION: - enc->allocation = g_value_get_enum(value); - break; - case PROP_BLOCKS: - enc->blocks = g_value_get_enum(value); - break; - case PROP_SUBBANDS: - enc->subbands = g_value_get_enum(value); - break; - case PROP_BITPOOL: - enc->bitpool = g_value_get_int(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void gst_sbc_enc_get_property(GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - GstSbcEnc *enc = GST_SBC_ENC(object); - - switch (prop_id) { - case PROP_MODE: - g_value_set_enum(value, enc->mode); - break; - case PROP_ALLOCATION: - g_value_set_enum(value, enc->allocation); - break; - case PROP_BLOCKS: - g_value_set_enum(value, enc->blocks); - break; - case PROP_SUBBANDS: - g_value_set_enum(value, enc->subbands); - break; - case PROP_BITPOOL: - g_value_set_int(value, enc->bitpool); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); - break; - } -} - -static void gst_sbc_enc_class_init(GstSbcEncClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - GstElementClass *element_class = GST_ELEMENT_CLASS(klass); - - parent_class = g_type_class_peek_parent(klass); - - object_class->set_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_set_property); - object_class->get_property = GST_DEBUG_FUNCPTR(gst_sbc_enc_get_property); - object_class->dispose = GST_DEBUG_FUNCPTR(gst_sbc_enc_dispose); - - element_class->change_state = GST_DEBUG_FUNCPTR(sbc_enc_change_state); - - g_object_class_install_property(object_class, PROP_MODE, - g_param_spec_enum("mode", "Mode", - "Encoding mode", GST_TYPE_SBC_MODE, - SBC_ENC_DEFAULT_MODE, G_PARAM_READWRITE)); - - g_object_class_install_property(object_class, PROP_ALLOCATION, - g_param_spec_enum("allocation", "Allocation", - "Allocation method", GST_TYPE_SBC_ALLOCATION, - SBC_ENC_DEFAULT_ALLOCATION, G_PARAM_READWRITE)); - - g_object_class_install_property(object_class, PROP_BLOCKS, - g_param_spec_enum("blocks", "Blocks", - "Blocks", GST_TYPE_SBC_BLOCKS, - SBC_ENC_DEFAULT_BLOCKS, G_PARAM_READWRITE)); - - g_object_class_install_property(object_class, PROP_SUBBANDS, - g_param_spec_enum("subbands", "Sub bands", - "Number of sub bands", GST_TYPE_SBC_SUBBANDS, - SBC_ENC_DEFAULT_SUB_BANDS, G_PARAM_READWRITE)); - - g_object_class_install_property(object_class, PROP_BITPOOL, - g_param_spec_int("bitpool", "Bitpool", - "Bitpool (use 1 for automatic selection)", - SBC_ENC_BITPOOL_AUTO, SBC_ENC_BITPOOL_MAX, - SBC_ENC_BITPOOL_AUTO, G_PARAM_READWRITE)); - - GST_DEBUG_CATEGORY_INIT(sbc_enc_debug, "sbcenc", 0, - "SBC encoding element"); -} - -static void gst_sbc_enc_init(GstSbcEnc *self, GstSbcEncClass *klass) -{ - self->sinkpad = gst_pad_new_from_static_template( - &sbc_enc_sink_factory, "sink"); - gst_pad_set_setcaps_function(self->sinkpad, - GST_DEBUG_FUNCPTR(sbc_enc_sink_setcaps)); - gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); - - self->srcpad = gst_pad_new_from_static_template( - &sbc_enc_src_factory, "src"); - gst_pad_set_getcaps_function(self->srcpad, - GST_DEBUG_FUNCPTR(sbc_enc_src_getcaps)); - gst_pad_set_setcaps_function(self->srcpad, - GST_DEBUG_FUNCPTR(sbc_enc_src_setcaps)); - gst_element_add_pad(GST_ELEMENT(self), self->srcpad); - - gst_pad_set_chain_function(self->sinkpad, - GST_DEBUG_FUNCPTR(sbc_enc_chain)); - - self->subbands = SBC_ENC_DEFAULT_SUB_BANDS; - self->blocks = SBC_ENC_DEFAULT_BLOCKS; - self->mode = SBC_ENC_DEFAULT_MODE; - self->allocation = SBC_ENC_DEFAULT_ALLOCATION; - self->rate = SBC_ENC_DEFAULT_RATE; - self->channels = SBC_ENC_DEFAULT_CHANNELS; - self->bitpool = SBC_ENC_BITPOOL_AUTO; - - self->frame_length = 0; - self->frame_duration = 0; - - self->adapter = gst_adapter_new(); -} - -gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin) -{ - return gst_element_register(plugin, "sbcenc", - GST_RANK_NONE, GST_TYPE_SBC_ENC); -} diff -Nru bluez-4.101/audio/gstsbcenc.h bluez-5.23/audio/gstsbcenc.h --- bluez-4.101/audio/gstsbcenc.h 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/audio/gstsbcenc.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#include - -#include "sbc.h" - -G_BEGIN_DECLS - -#define GST_TYPE_SBC_ENC \ - (gst_sbc_enc_get_type()) -#define GST_SBC_ENC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_ENC,GstSbcEnc)) -#define GST_SBC_ENC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_ENC,GstSbcEncClass)) -#define GST_IS_SBC_ENC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_ENC)) -#define GST_IS_SBC_ENC_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_ENC)) - -typedef struct _GstSbcEnc GstSbcEnc; -typedef struct _GstSbcEncClass GstSbcEncClass; - -struct _GstSbcEnc { - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; - GstAdapter *adapter; - - gint rate; - gint channels; - gint mode; - gint blocks; - gint allocation; - gint subbands; - gint bitpool; - - guint codesize; - gint frame_length; - gint frame_duration; - - sbc_t sbc; -}; - -struct _GstSbcEncClass { - GstElementClass parent_class; -}; - -GType gst_sbc_enc_get_type(void); - -gboolean gst_sbc_enc_plugin_init(GstPlugin *plugin); - -G_END_DECLS diff -Nru bluez-4.101/audio/gstsbcparse.c bluez-5.23/audio/gstsbcparse.c --- bluez-4.101/audio/gstsbcparse.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gstsbcparse.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,219 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include - -#include "gstpragma.h" -#include "gstsbcutil.h" -#include "gstsbcparse.h" - -GST_DEBUG_CATEGORY_STATIC(sbc_parse_debug); -#define GST_CAT_DEFAULT sbc_parse_debug - -GST_BOILERPLATE(GstSbcParse, gst_sbc_parse, GstElement, GST_TYPE_ELEMENT); - -static const GstElementDetails sbc_parse_details = - GST_ELEMENT_DETAILS("Bluetooth SBC parser", - "Codec/Parser/Audio", - "Parse a SBC audio stream", - "Marcel Holtmann "); - -static GstStaticPadTemplate sbc_parse_sink_factory = - GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc," - "parsed = (boolean) false")); - -static GstStaticPadTemplate sbc_parse_src_factory = - GST_STATIC_PAD_TEMPLATE("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS("audio/x-sbc, " - "rate = (int) { 16000, 32000, 44100, 48000 }, " - "channels = (int) [ 1, 2 ], " - "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, " - "blocks = (int) { 4, 8, 12, 16 }, " - "subbands = (int) { 4, 8 }, " - "allocation = (string) { \"snr\", \"loudness\" }," - "bitpool = (int) [ 2, 64 ]," - "parsed = (boolean) true")); - -static GstFlowReturn sbc_parse_chain(GstPad *pad, GstBuffer *buffer) -{ - GstSbcParse *parse = GST_SBC_PARSE(gst_pad_get_parent(pad)); - GstFlowReturn res = GST_FLOW_OK; - guint size, offset = 0; - guint8 *data; - - /* FIXME use a gstadpter */ - if (parse->buffer) { - GstBuffer *temp; - temp = buffer; - buffer = gst_buffer_span(parse->buffer, 0, buffer, - GST_BUFFER_SIZE(parse->buffer) - + GST_BUFFER_SIZE(buffer)); - gst_buffer_unref(parse->buffer); - gst_buffer_unref(temp); - parse->buffer = NULL; - } - - data = GST_BUFFER_DATA(buffer); - size = GST_BUFFER_SIZE(buffer); - - while (offset < size) { - GstBuffer *output; - int consumed; - - consumed = sbc_parse(&parse->new_sbc, data + offset, - size - offset); - if (consumed <= 0) - break; - - if (parse->first_parsing || (memcmp(&parse->sbc, - &parse->new_sbc, sizeof(sbc_t)) != 0)) { - - memcpy(&parse->sbc, &parse->new_sbc, sizeof(sbc_t)); - if (parse->outcaps != NULL) - gst_caps_unref(parse->outcaps); - - parse->outcaps = gst_sbc_parse_caps_from_sbc( - &parse->sbc); - - parse->first_parsing = FALSE; - } - - res = gst_pad_alloc_buffer_and_set_caps(parse->srcpad, - GST_BUFFER_OFFSET_NONE, - consumed, parse->outcaps, &output); - - if (res != GST_FLOW_OK) - goto done; - - memcpy(GST_BUFFER_DATA(output), data + offset, consumed); - - res = gst_pad_push(parse->srcpad, output); - if (res != GST_FLOW_OK) - goto done; - - offset += consumed; - } - - if (offset < size) - parse->buffer = gst_buffer_create_sub(buffer, - offset, size - offset); - -done: - gst_buffer_unref(buffer); - gst_object_unref(parse); - - return res; -} - -static GstStateChangeReturn sbc_parse_change_state(GstElement *element, - GstStateChange transition) -{ - GstSbcParse *parse = GST_SBC_PARSE(element); - - switch (transition) { - case GST_STATE_CHANGE_READY_TO_PAUSED: - GST_DEBUG("Setup subband codec"); - - parse->channels = -1; - parse->rate = -1; - parse->first_parsing = TRUE; - - sbc_init(&parse->sbc, 0); - break; - - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG("Finish subband codec"); - - if (parse->buffer) { - gst_buffer_unref(parse->buffer); - parse->buffer = NULL; - } - if (parse->outcaps != NULL) { - gst_caps_unref(parse->outcaps); - parse->outcaps = NULL; - } - - sbc_finish(&parse->sbc); - break; - - default: - break; - } - - return parent_class->change_state(element, transition); -} - -static void gst_sbc_parse_base_init(gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(g_class); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&sbc_parse_sink_factory)); - - gst_element_class_add_pad_template(element_class, - gst_static_pad_template_get(&sbc_parse_src_factory)); - - gst_element_class_set_details(element_class, &sbc_parse_details); -} - -static void gst_sbc_parse_class_init(GstSbcParseClass *klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS(klass); - - parent_class = g_type_class_peek_parent(klass); - - element_class->change_state = GST_DEBUG_FUNCPTR(sbc_parse_change_state); - - GST_DEBUG_CATEGORY_INIT(sbc_parse_debug, "sbcparse", 0, - "SBC parsing element"); -} - -static void gst_sbc_parse_init(GstSbcParse *self, GstSbcParseClass *klass) -{ - self->sinkpad = gst_pad_new_from_static_template( - &sbc_parse_sink_factory, "sink"); - gst_pad_set_chain_function(self->sinkpad, - GST_DEBUG_FUNCPTR(sbc_parse_chain)); - gst_element_add_pad(GST_ELEMENT(self), self->sinkpad); - - self->srcpad = gst_pad_new_from_static_template( - &sbc_parse_src_factory, "src"); - gst_element_add_pad(GST_ELEMENT(self), self->srcpad); - - self->outcaps = NULL; - self->buffer = NULL; - self->channels = -1; - self->rate = -1; - self->first_parsing = TRUE; -} - -gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin) -{ - return gst_element_register(plugin, "sbcparse", GST_RANK_NONE, - GST_TYPE_SBC_PARSE); -} diff -Nru bluez-4.101/audio/gstsbcparse.h bluez-5.23/audio/gstsbcparse.h --- bluez-4.101/audio/gstsbcparse.h 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/audio/gstsbcparse.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 - -#include "sbc.h" - -G_BEGIN_DECLS - -#define GST_TYPE_SBC_PARSE \ - (gst_sbc_parse_get_type()) -#define GST_SBC_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SBC_PARSE,GstSbcParse)) -#define GST_SBC_PARSE_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SBC_PARSE,GstSbcParseClass)) -#define GST_IS_SBC_PARSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SBC_PARSE)) -#define GST_IS_SBC_PARSE_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SBC_PARSE)) - -typedef struct _GstSbcParse GstSbcParse; -typedef struct _GstSbcParseClass GstSbcParseClass; - -struct _GstSbcParse { - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; - - GstBuffer *buffer; - - sbc_t sbc; - sbc_t new_sbc; - GstCaps *outcaps; - gboolean first_parsing; - - gint channels; - gint rate; -}; - -struct _GstSbcParseClass { - GstElementClass parent_class; -}; - -GType gst_sbc_parse_get_type(void); - -gboolean gst_sbc_parse_plugin_init(GstPlugin *plugin); - -G_END_DECLS diff -Nru bluez-4.101/audio/gstsbcutil.c bluez-5.23/audio/gstsbcutil.c --- bluez-4.101/audio/gstsbcutil.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gstsbcutil.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,520 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include -#include "gstsbcutil.h" - -/* - * Selects one rate from a list of possible rates - * TODO - use a better approach to this (it is selecting the last element) - */ -gint gst_sbc_select_rate_from_list(const GValue *value) -{ - guint size = gst_value_list_get_size(value); - return g_value_get_int(gst_value_list_get_value(value, size-1)); -} - -/* - * Selects one number of channels option from a range of possible numbers - * TODO - use a better approach to this (it is selecting the maximum value) - */ -gint gst_sbc_select_channels_from_range(const GValue *value) -{ - return gst_value_get_int_range_max(value); -} - -/* - * Selects one number of blocks from a list of possible blocks - * TODO - use a better approach to this (it is selecting the last element) - */ -gint gst_sbc_select_blocks_from_list(const GValue *value) -{ - guint size = gst_value_list_get_size(value); - return g_value_get_int(gst_value_list_get_value(value, size-1)); -} - -/* - * Selects one number of subbands from a list - * TODO - use a better approach to this (it is selecting the last element) - */ -gint gst_sbc_select_subbands_from_list(const GValue *value) -{ - guint size = gst_value_list_get_size(value); - return g_value_get_int(gst_value_list_get_value(value, size-1)); -} - -/* - * Selects one bitpool option from a range - * TODO - use a better approach to this (it is selecting the maximum value) - */ -gint gst_sbc_select_bitpool_from_range(const GValue *value) -{ - return gst_value_get_int_range_max(value); -} - -/* - * Selects one allocation mode from the ones on the list - * TODO - use a better approach - */ -const gchar *gst_sbc_get_allocation_from_list(const GValue *value) -{ - guint size = gst_value_list_get_size(value); - return g_value_get_string(gst_value_list_get_value(value, size-1)); -} - -/* - * Selects one mode from the ones on the list - */ -const gchar *gst_sbc_get_mode_from_list(const GValue *list, gint channels) -{ - unsigned int i; - const GValue *value; - const gchar *aux; - gboolean joint, stereo, dual, mono; - guint size = gst_value_list_get_size(list); - - joint = stereo = dual = mono = FALSE; - - for (i = 0; i < size; i++) { - value = gst_value_list_get_value(list, i); - aux = g_value_get_string(value); - if (strcmp("joint", aux) == 0) - joint = TRUE; - else if (strcmp("stereo", aux) == 0) - stereo = TRUE; - else if (strcmp("dual", aux) == 0) - dual = TRUE; - else if (strcmp("mono", aux) == 0) - mono = TRUE; - } - - if (channels == 1 && mono) - return "mono"; - else if (channels == 2) { - if (joint) - return "joint"; - else if (stereo) - return "stereo"; - else if (dual) - return "dual"; - } - - return NULL; -} - -gint gst_sbc_parse_rate_from_sbc(gint frequency) -{ - switch (frequency) { - case SBC_FREQ_16000: - return 16000; - case SBC_FREQ_32000: - return 32000; - case SBC_FREQ_44100: - return 44100; - case SBC_FREQ_48000: - return 48000; - default: - return 0; - } -} - -gint gst_sbc_parse_rate_to_sbc(gint rate) -{ - switch (rate) { - case 16000: - return SBC_FREQ_16000; - case 32000: - return SBC_FREQ_32000; - case 44100: - return SBC_FREQ_44100; - case 48000: - return SBC_FREQ_48000; - default: - return -1; - } -} - -gint gst_sbc_get_channel_number(gint mode) -{ - switch (mode) { - case SBC_MODE_JOINT_STEREO: - case SBC_MODE_STEREO: - case SBC_MODE_DUAL_CHANNEL: - return 2; - case SBC_MODE_MONO: - return 1; - default: - return 0; - } -} - -gint gst_sbc_parse_subbands_from_sbc(gint subbands) -{ - switch (subbands) { - case SBC_SB_4: - return 4; - case SBC_SB_8: - return 8; - default: - return 0; - } -} - -gint gst_sbc_parse_subbands_to_sbc(gint subbands) -{ - switch (subbands) { - case 4: - return SBC_SB_4; - case 8: - return SBC_SB_8; - default: - return -1; - } -} - -gint gst_sbc_parse_blocks_from_sbc(gint blocks) -{ - switch (blocks) { - case SBC_BLK_4: - return 4; - case SBC_BLK_8: - return 8; - case SBC_BLK_12: - return 12; - case SBC_BLK_16: - return 16; - default: - return 0; - } -} - -gint gst_sbc_parse_blocks_to_sbc(gint blocks) -{ - switch (blocks) { - case 4: - return SBC_BLK_4; - case 8: - return SBC_BLK_8; - case 12: - return SBC_BLK_12; - case 16: - return SBC_BLK_16; - default: - return -1; - } -} - -const gchar *gst_sbc_parse_mode_from_sbc(gint mode) -{ - switch (mode) { - case SBC_MODE_MONO: - return "mono"; - case SBC_MODE_DUAL_CHANNEL: - return "dual"; - case SBC_MODE_STEREO: - return "stereo"; - case SBC_MODE_JOINT_STEREO: - case SBC_MODE_AUTO: - return "joint"; - default: - return NULL; - } -} - -gint gst_sbc_parse_mode_to_sbc(const gchar *mode) -{ - if (g_ascii_strcasecmp(mode, "joint") == 0) - return SBC_MODE_JOINT_STEREO; - else if (g_ascii_strcasecmp(mode, "stereo") == 0) - return SBC_MODE_STEREO; - else if (g_ascii_strcasecmp(mode, "dual") == 0) - return SBC_MODE_DUAL_CHANNEL; - else if (g_ascii_strcasecmp(mode, "mono") == 0) - return SBC_MODE_MONO; - else if (g_ascii_strcasecmp(mode, "auto") == 0) - return SBC_MODE_JOINT_STEREO; - else - return -1; -} - -const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc) -{ - switch (alloc) { - case SBC_AM_LOUDNESS: - return "loudness"; - case SBC_AM_SNR: - return "snr"; - case SBC_AM_AUTO: - return "loudness"; - default: - return NULL; - } -} - -gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation) -{ - if (g_ascii_strcasecmp(allocation, "loudness") == 0) - return SBC_AM_LOUDNESS; - else if (g_ascii_strcasecmp(allocation, "snr") == 0) - return SBC_AM_SNR; - else - return SBC_AM_LOUDNESS; -} - -GstCaps *gst_sbc_parse_caps_from_sbc(sbc_t *sbc) -{ - GstCaps *caps; - const gchar *mode_str; - const gchar *allocation_str; - - mode_str = gst_sbc_parse_mode_from_sbc(sbc->mode); - allocation_str = gst_sbc_parse_allocation_from_sbc(sbc->allocation); - caps = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, - gst_sbc_parse_rate_from_sbc(sbc->frequency), - "channels", G_TYPE_INT, - gst_sbc_get_channel_number(sbc->mode), - "mode", G_TYPE_STRING, mode_str, - "subbands", G_TYPE_INT, - gst_sbc_parse_subbands_from_sbc(sbc->subbands), - "blocks", G_TYPE_INT, - gst_sbc_parse_blocks_from_sbc(sbc->blocks), - "allocation", G_TYPE_STRING, allocation_str, - "bitpool", G_TYPE_INT, sbc->bitpool, - NULL); - - return caps; -} - -/* - * Given a GstCaps, this will return a fixed GstCaps on successful conversion. - * If an error occurs, it will return NULL and error_message will contain the - * error message. - * - * error_message must be passed NULL, if an error occurs, the caller has the - * ownership of the error_message, it must be freed after use. - */ -GstCaps *gst_sbc_util_caps_fixate(GstCaps *caps, gchar **error_message) -{ - GstCaps *result; - GstStructure *structure; - const GValue *value; - gboolean error = FALSE; - gint temp, rate, channels, blocks, subbands, bitpool; - const gchar *allocation = NULL; - const gchar *mode = NULL; - - g_assert(*error_message == NULL); - - structure = gst_caps_get_structure(caps, 0); - - if (!gst_structure_has_field(structure, "rate")) { - error = TRUE; - *error_message = g_strdup("no rate"); - goto error; - } else { - value = gst_structure_get_value(structure, "rate"); - if (GST_VALUE_HOLDS_LIST(value)) - temp = gst_sbc_select_rate_from_list(value); - else - temp = g_value_get_int(value); - rate = temp; - } - - if (!gst_structure_has_field(structure, "channels")) { - error = TRUE; - *error_message = g_strdup("no channels"); - goto error; - } else { - value = gst_structure_get_value(structure, "channels"); - if (GST_VALUE_HOLDS_INT_RANGE(value)) - temp = gst_sbc_select_channels_from_range(value); - else - temp = g_value_get_int(value); - channels = temp; - } - - if (!gst_structure_has_field(structure, "blocks")) { - error = TRUE; - *error_message = g_strdup("no blocks."); - goto error; - } else { - value = gst_structure_get_value(structure, "blocks"); - if (GST_VALUE_HOLDS_LIST(value)) - temp = gst_sbc_select_blocks_from_list(value); - else - temp = g_value_get_int(value); - blocks = temp; - } - - if (!gst_structure_has_field(structure, "subbands")) { - error = TRUE; - *error_message = g_strdup("no subbands"); - goto error; - } else { - value = gst_structure_get_value(structure, "subbands"); - if (GST_VALUE_HOLDS_LIST(value)) - temp = gst_sbc_select_subbands_from_list(value); - else - temp = g_value_get_int(value); - subbands = temp; - } - - if (!gst_structure_has_field(structure, "bitpool")) { - error = TRUE; - *error_message = g_strdup("no bitpool"); - goto error; - } else { - value = gst_structure_get_value(structure, "bitpool"); - if (GST_VALUE_HOLDS_INT_RANGE(value)) - temp = gst_sbc_select_bitpool_from_range(value); - else - temp = g_value_get_int(value); - bitpool = temp; - } - - if (!gst_structure_has_field(structure, "allocation")) { - error = TRUE; - *error_message = g_strdup("no allocation"); - goto error; - } else { - value = gst_structure_get_value(structure, "allocation"); - if (GST_VALUE_HOLDS_LIST(value)) - allocation = gst_sbc_get_allocation_from_list(value); - else - allocation = g_value_get_string(value); - } - - if (!gst_structure_has_field(structure, "mode")) { - error = TRUE; - *error_message = g_strdup("no mode"); - goto error; - } else { - value = gst_structure_get_value(structure, "mode"); - if (GST_VALUE_HOLDS_LIST(value)) { - mode = gst_sbc_get_mode_from_list(value, channels); - } else - mode = g_value_get_string(value); - } - - /* perform validation - * if channels is 1, we must have channel mode = mono - * if channels is 2, we can't have channel mode = mono */ - if ( (channels == 1 && (strcmp(mode, "mono") != 0) ) || - ( channels == 2 && ( strcmp(mode, "mono") == 0))) { - *error_message = g_strdup_printf("Invalid combination of " - "channels (%d) and channel mode (%s)", - channels, mode); - error = TRUE; - } - -error: - if (error) - return NULL; - - result = gst_caps_new_simple("audio/x-sbc", - "rate", G_TYPE_INT, rate, - "channels", G_TYPE_INT, channels, - "mode", G_TYPE_STRING, mode, - "blocks", G_TYPE_INT, blocks, - "subbands", G_TYPE_INT, subbands, - "allocation", G_TYPE_STRING, allocation, - "bitpool", G_TYPE_INT, bitpool, - NULL); - - return result; -} - -/** - * Sets the int field_value to the param "field" on the structure. - * value is used to do the operation, it must be a uninitialized (zero-filled) - * GValue, it will be left unitialized at the end of the function. - */ -void gst_sbc_util_set_structure_int_param(GstStructure *structure, - const gchar *field, gint field_value, - GValue *value) -{ - value = g_value_init(value, G_TYPE_INT); - g_value_set_int(value, field_value); - gst_structure_set_value(structure, field, value); - g_value_unset(value); -} - -/** - * Sets the string field_value to the param "field" on the structure. - * value is used to do the operation, it must be a uninitialized (zero-filled) - * GValue, it will be left unitialized at the end of the function. - */ -void gst_sbc_util_set_structure_string_param(GstStructure *structure, - const gchar *field, const gchar *field_value, - GValue *value) -{ - value = g_value_init(value, G_TYPE_STRING); - g_value_set_string(value, field_value); - gst_structure_set_value(structure, field, value); - g_value_unset(value); -} - -gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps) -{ - GstStructure *structure; - gint rate, channels, subbands, blocks, bitpool; - const gchar *mode; - const gchar *allocation; - - g_assert(gst_caps_is_fixed(caps)); - - structure = gst_caps_get_structure(caps, 0); - - if (!gst_structure_get_int(structure, "rate", &rate)) - return FALSE; - if (!gst_structure_get_int(structure, "channels", &channels)) - return FALSE; - if (!gst_structure_get_int(structure, "subbands", &subbands)) - return FALSE; - if (!gst_structure_get_int(structure, "blocks", &blocks)) - return FALSE; - if (!gst_structure_get_int(structure, "bitpool", &bitpool)) - return FALSE; - - if (!(mode = gst_structure_get_string(structure, "mode"))) - return FALSE; - if (!(allocation = gst_structure_get_string(structure, "allocation"))) - return FALSE; - - if (channels == 1 && strcmp(mode, "mono") != 0) - return FALSE; - - sbc->frequency = gst_sbc_parse_rate_to_sbc(rate); - sbc->blocks = gst_sbc_parse_blocks_to_sbc(blocks); - sbc->subbands = gst_sbc_parse_subbands_to_sbc(subbands); - sbc->bitpool = bitpool; - sbc->mode = gst_sbc_parse_mode_to_sbc(mode); - sbc->allocation = gst_sbc_parse_allocation_to_sbc(allocation); - - return TRUE; -} diff -Nru bluez-4.101/audio/gstsbcutil.h bluez-5.23/audio/gstsbcutil.h --- bluez-4.101/audio/gstsbcutil.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/gstsbcutil.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 - -#include "sbc.h" -#include - -#define SBC_AM_AUTO 0x02 -#define SBC_MODE_AUTO 0x04 - -gint gst_sbc_select_rate_from_list(const GValue *value); - -gint gst_sbc_select_channels_from_range(const GValue *value); - -gint gst_sbc_select_blocks_from_list(const GValue *value); - -gint gst_sbc_select_subbands_from_list(const GValue *value); - -gint gst_sbc_select_bitpool_from_range(const GValue *value); - -const gchar *gst_sbc_get_allocation_from_list(const GValue *value); - -const gchar *gst_sbc_get_mode_from_list(const GValue *value, gint channels); - -gint gst_sbc_get_channel_number(gint mode); -gint gst_sbc_parse_rate_from_sbc(gint frequency); -gint gst_sbc_parse_rate_to_sbc(gint rate); - -gint gst_sbc_parse_subbands_from_sbc(gint subbands); -gint gst_sbc_parse_subbands_to_sbc(gint subbands); - -gint gst_sbc_parse_blocks_from_sbc(gint blocks); -gint gst_sbc_parse_blocks_to_sbc(gint blocks); - -const gchar *gst_sbc_parse_mode_from_sbc(gint mode); -gint gst_sbc_parse_mode_to_sbc(const gchar *mode); - -const gchar *gst_sbc_parse_allocation_from_sbc(gint alloc); -gint gst_sbc_parse_allocation_to_sbc(const gchar *allocation); - -GstCaps* gst_sbc_parse_caps_from_sbc(sbc_t *sbc); - -GstCaps* gst_sbc_util_caps_fixate(GstCaps *caps, gchar** error_message); - -void gst_sbc_util_set_structure_int_param(GstStructure *structure, - const gchar* field, gint field_value, - GValue *value); - -void gst_sbc_util_set_structure_string_param(GstStructure *structure, - const gchar* field, const gchar* field_value, - GValue *value); - -gboolean gst_sbc_util_fill_sbc_params(sbc_t *sbc, GstCaps *caps); diff -Nru bluez-4.101/audio/headset.c bluez-5.23/audio/headset.c --- bluez-4.101/audio/headset.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/headset.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,3007 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "log.h" -#include "device.h" -#include "manager.h" -#include "error.h" -#include "telephony.h" -#include "headset.h" -#include "sdp-client.h" -#include "btio.h" -#include "dbus-common.h" -#include "../src/adapter.h" -#include "../src/device.h" - -#define DC_TIMEOUT 3 - -#define RING_INTERVAL 3 - -#define BUF_SIZE 1024 - -#define HEADSET_GAIN_SPEAKER 'S' -#define HEADSET_GAIN_MICROPHONE 'M' - -static struct { - gboolean telephony_ready; /* Telephony plugin initialized */ - uint32_t features; /* HFP AG features */ - const struct indicator *indicators; /* Available HFP indicators */ - int er_mode; /* Event reporting mode */ - int er_ind; /* Event reporting for indicators */ - int rh; /* Response and Hold state */ - char *number; /* Incoming phone number */ - int number_type; /* Incoming number type */ - guint ring_timer; /* For incoming call indication */ - const char *chld; /* Response to AT+CHLD=? */ -} ag = { - .telephony_ready = FALSE, - .features = 0, - .er_mode = 3, - .er_ind = 0, - .rh = BTRH_NOT_SUPPORTED, - .number = NULL, - .number_type = 0, - .ring_timer = 0, -}; - -static gboolean sco_hci = TRUE; -static gboolean fast_connectable = FALSE; - -static GSList *active_devices = NULL; - -static char *str_state[] = { - "HEADSET_STATE_DISCONNECTED", - "HEADSET_STATE_CONNECTING", - "HEADSET_STATE_CONNECTED", - "HEADSET_STATE_PLAY_IN_PROGRESS", - "HEADSET_STATE_PLAYING", -}; - -struct headset_state_callback { - headset_state_cb cb; - void *user_data; - unsigned int id; -}; - -struct headset_nrec_callback { - unsigned int id; - headset_nrec_cb cb; - void *user_data; -}; - -struct connect_cb { - unsigned int id; - headset_stream_cb_t cb; - void *cb_data; -}; - -struct pending_connect { - DBusMessage *msg; - DBusPendingCall *call; - GIOChannel *io; - int err; - headset_state_t target_state; - GSList *callbacks; - uint16_t svclass; -}; - -struct headset_slc { - char buf[BUF_SIZE]; - int data_start; - int data_length; - - gboolean cli_active; - gboolean cme_enabled; - gboolean cwa_enabled; - gboolean pending_ring; - gboolean inband_ring; - gboolean nrec; - gboolean nrec_req; - - int sp_gain; - int mic_gain; - - unsigned int hf_features; -}; - -struct headset { - uint32_t hsp_handle; - uint32_t hfp_handle; - - int rfcomm_ch; - - GIOChannel *rfcomm; - GIOChannel *tmp_rfcomm; - GIOChannel *sco; - guint sco_id; - - gboolean auto_dc; - - guint dc_timer; - - gboolean hfp_active; - gboolean search_hfp; - gboolean rfcomm_initiator; - - headset_state_t state; - struct pending_connect *pending; - - headset_lock_t lock; - struct headset_slc *slc; - GSList *nrec_cbs; -}; - -struct event { - const char *cmd; - int (*callback) (struct audio_device *device, const char *buf); -}; - -static GSList *headset_callbacks = NULL; - -static void error_connect_failed(DBusConnection *conn, DBusMessage *msg, - int err) -{ - DBusMessage *reply = btd_error_failed(msg, - err < 0 ? strerror(-err) : "Connect failed"); - g_dbus_send_message(conn, reply); -} - -static int rfcomm_connect(struct audio_device *device, headset_stream_cb_t cb, - void *user_data, unsigned int *cb_id); -static int get_records(struct audio_device *device, headset_stream_cb_t cb, - void *user_data, unsigned int *cb_id); - -static void print_ag_features(uint32_t features) -{ - GString *gstr; - char *str; - - if (features == 0) { - DBG("HFP AG features: (none)"); - return; - } - - gstr = g_string_new("HFP AG features: "); - - if (features & AG_FEATURE_THREE_WAY_CALLING) - g_string_append(gstr, "\"Three-way calling\" "); - if (features & AG_FEATURE_EC_ANDOR_NR) - g_string_append(gstr, "\"EC and/or NR function\" "); - if (features & AG_FEATURE_VOICE_RECOGNITION) - g_string_append(gstr, "\"Voice recognition function\" "); - if (features & AG_FEATURE_INBAND_RINGTONE) - g_string_append(gstr, "\"In-band ring tone capability\" "); - if (features & AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG) - g_string_append(gstr, "\"Attach a number to a voice tag\" "); - if (features & AG_FEATURE_REJECT_A_CALL) - g_string_append(gstr, "\"Ability to reject a call\" "); - if (features & AG_FEATURE_ENHANCED_CALL_STATUS) - g_string_append(gstr, "\"Enhanced call status\" "); - if (features & AG_FEATURE_ENHANCED_CALL_CONTROL) - g_string_append(gstr, "\"Enhanced call control\" "); - if (features & AG_FEATURE_EXTENDED_ERROR_RESULT_CODES) - g_string_append(gstr, "\"Extended Error Result Codes\" "); - - str = g_string_free(gstr, FALSE); - - DBG("%s", str); - - g_free(str); -} - -static void print_hf_features(uint32_t features) -{ - GString *gstr; - char *str; - - if (features == 0) { - DBG("HFP HF features: (none)"); - return; - } - - gstr = g_string_new("HFP HF features: "); - - if (features & HF_FEATURE_EC_ANDOR_NR) - g_string_append(gstr, "\"EC and/or NR function\" "); - if (features & HF_FEATURE_CALL_WAITING_AND_3WAY) - g_string_append(gstr, "\"Call waiting and 3-way calling\" "); - if (features & HF_FEATURE_CLI_PRESENTATION) - g_string_append(gstr, "\"CLI presentation capability\" "); - if (features & HF_FEATURE_VOICE_RECOGNITION) - g_string_append(gstr, "\"Voice recognition activation\" "); - if (features & HF_FEATURE_REMOTE_VOLUME_CONTROL) - g_string_append(gstr, "\"Remote volume control\" "); - if (features & HF_FEATURE_ENHANCED_CALL_STATUS) - g_string_append(gstr, "\"Enhanced call status\" "); - if (features & HF_FEATURE_ENHANCED_CALL_CONTROL) - g_string_append(gstr, "\"Enhanced call control\" "); - - str = g_string_free(gstr, FALSE); - - DBG("%s", str); - - g_free(str); -} - -static const char *state2str(headset_state_t state) -{ - switch (state) { - case HEADSET_STATE_DISCONNECTED: - return "disconnected"; - case HEADSET_STATE_CONNECTING: - return "connecting"; - case HEADSET_STATE_CONNECTED: - case HEADSET_STATE_PLAY_IN_PROGRESS: - return "connected"; - case HEADSET_STATE_PLAYING: - return "playing"; - } - - return NULL; -} - -static int headset_send_valist(struct headset *hs, char *format, va_list ap) -{ - char rsp[BUF_SIZE]; - ssize_t total_written, count; - int fd; - - count = vsnprintf(rsp, sizeof(rsp), format, ap); - - if (count < 0) - return -EINVAL; - - if (!hs->rfcomm) { - error("headset_send: the headset is not connected"); - return -EIO; - } - - total_written = 0; - fd = g_io_channel_unix_get_fd(hs->rfcomm); - - while (total_written < count) { - ssize_t written; - - written = write(fd, rsp + total_written, - count - total_written); - if (written < 0) - return -errno; - - total_written += written; - } - - return 0; -} - -static int __attribute__((format(printf, 2, 3))) - headset_send(struct headset *hs, char *format, ...) -{ - va_list ap; - int ret; - - va_start(ap, format); - ret = headset_send_valist(hs, format, ap); - va_end(ap); - - return ret; -} - -static int supported_features(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - int err; - - if (strlen(buf) < 9) - return -EINVAL; - - slc->hf_features = strtoul(&buf[8], NULL, 10); - - print_hf_features(slc->hf_features); - - err = headset_send(hs, "\r\n+BRSF: %u\r\n", ag.features); - if (err < 0) - return err; - - return headset_send(hs, "\r\nOK\r\n"); -} - -static char *indicator_ranges(const struct indicator *indicators) -{ - int i; - GString *gstr; - - gstr = g_string_new("\r\n+CIND: "); - - for (i = 0; indicators[i].desc != NULL; i++) { - if (i == 0) - g_string_append_printf(gstr, "(\"%s\",(%s))", - indicators[i].desc, - indicators[i].range); - else - g_string_append_printf(gstr, ",(\"%s\",(%s))", - indicators[i].desc, - indicators[i].range); - } - - g_string_append(gstr, "\r\n"); - - return g_string_free(gstr, FALSE); -} - -static char *indicator_values(const struct indicator *indicators) -{ - int i; - GString *gstr; - - gstr = g_string_new("\r\n+CIND: "); - - for (i = 0; indicators[i].desc != NULL; i++) { - if (i == 0) - g_string_append_printf(gstr, "%d", indicators[i].val); - else - g_string_append_printf(gstr, ",%d", indicators[i].val); - } - - g_string_append(gstr, "\r\n"); - - return g_string_free(gstr, FALSE); -} - -static int report_indicators(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - int err; - char *str; - - if (strlen(buf) < 8) - return -EINVAL; - - if (ag.indicators == NULL) { - error("HFP AG indicators not initialized"); - return headset_send(hs, "\r\nERROR\r\n"); - } - - if (buf[7] == '=') - str = indicator_ranges(ag.indicators); - else - str = indicator_values(ag.indicators); - - err = headset_send(hs, "%s", str); - - g_free(str); - - if (err < 0) - return err; - - return headset_send(hs, "\r\nOK\r\n"); -} - -static void pending_connect_complete(struct connect_cb *cb, struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - if (hs->pending->err < 0) - cb->cb(NULL, cb->cb_data); - else - cb->cb(dev, cb->cb_data); -} - -static void pending_connect_finalize(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - struct pending_connect *p = hs->pending; - - if (p == NULL) - return; - - if (p->svclass) - bt_cancel_discovery(&dev->src, &dev->dst); - - g_slist_foreach(p->callbacks, (GFunc) pending_connect_complete, dev); - - g_slist_free_full(p->callbacks, g_free); - - if (p->io) { - g_io_channel_shutdown(p->io, TRUE, NULL); - g_io_channel_unref(p->io); - } - - if (p->msg) - dbus_message_unref(p->msg); - - if (p->call) { - dbus_pending_call_cancel(p->call); - dbus_pending_call_unref(p->call); - } - - g_free(p); - - hs->pending = NULL; -} - -static void pending_connect_init(struct headset *hs, headset_state_t target_state) -{ - if (hs->pending) { - if (hs->pending->target_state < target_state) - hs->pending->target_state = target_state; - return; - } - - hs->pending = g_new0(struct pending_connect, 1); - hs->pending->target_state = target_state; -} - -static unsigned int connect_cb_new(struct headset *hs, - headset_state_t target_state, - headset_stream_cb_t func, - void *user_data) -{ - struct connect_cb *cb; - static unsigned int free_cb_id = 1; - - pending_connect_init(hs, target_state); - - if (!func) - return 0; - - cb = g_new(struct connect_cb, 1); - - cb->cb = func; - cb->cb_data = user_data; - cb->id = free_cb_id++; - - hs->pending->callbacks = g_slist_append(hs->pending->callbacks, - cb); - - return cb->id; -} - -static void __attribute__((format(printf, 3, 4))) - send_foreach_headset(GSList *devices, - int (*cmp) (struct headset *hs), - char *format, ...) -{ - GSList *l; - va_list ap; - - for (l = devices; l != NULL; l = l->next) { - struct audio_device *device = l->data; - struct headset *hs = device->headset; - int ret; - - assert(hs != NULL); - - if (cmp && cmp(hs) != 0) - continue; - - va_start(ap, format); - ret = headset_send_valist(hs, format, ap); - if (ret < 0) - error("Failed to send to headset: %s (%d)", - strerror(-ret), -ret); - va_end(ap); - } -} - -static int cli_cmp(struct headset *hs) -{ - struct headset_slc *slc = hs->slc; - - if (!hs->hfp_active) - return -1; - - if (slc->cli_active) - return 0; - else - return -1; -} - -static gboolean ring_timer_cb(gpointer data) -{ - send_foreach_headset(active_devices, NULL, "\r\nRING\r\n"); - - if (ag.number) - send_foreach_headset(active_devices, cli_cmp, - "\r\n+CLIP: \"%s\",%d\r\n", - ag.number, ag.number_type); - - return TRUE; -} - -static void sco_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) -{ - int sk; - struct audio_device *dev = user_data; - struct headset *hs = dev->headset; - struct headset_slc *slc = hs->slc; - struct pending_connect *p = hs->pending; - - if (err) { - error("%s", err->message); - - if (p != NULL) { - p->err = -errno; - if (p->msg) - error_connect_failed(dev->conn, p->msg, p->err); - pending_connect_finalize(dev); - } - - if (hs->rfcomm) - headset_set_state(dev, HEADSET_STATE_CONNECTED); - else - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); - - return; - } - - DBG("SCO socket opened for headset %s", dev->path); - - sk = g_io_channel_unix_get_fd(chan); - - DBG("SCO fd=%d", sk); - - if (p) { - p->io = NULL; - if (p->msg) { - DBusMessage *reply; - reply = dbus_message_new_method_return(p->msg); - g_dbus_send_message(dev->conn, reply); - } - - pending_connect_finalize(dev); - } - - fcntl(sk, F_SETFL, 0); - - headset_set_state(dev, HEADSET_STATE_PLAYING); - - if (slc->pending_ring) { - ring_timer_cb(NULL); - ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, - ring_timer_cb, - NULL); - slc->pending_ring = FALSE; - } -} - -static int sco_connect(struct audio_device *dev, headset_stream_cb_t cb, - void *user_data, unsigned int *cb_id) -{ - struct headset *hs = dev->headset; - GError *err = NULL; - GIOChannel *io; - - if (hs->state != HEADSET_STATE_CONNECTED) - return -EINVAL; - - io = bt_io_connect(BT_IO_SCO, sco_connect_cb, dev, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &dev->src, - BT_IO_OPT_DEST_BDADDR, &dev->dst, - BT_IO_OPT_INVALID); - if (!io) { - error("%s", err->message); - g_error_free(err); - return -EIO; - } - - hs->sco = io; - - headset_set_state(dev, HEADSET_STATE_PLAY_IN_PROGRESS); - - pending_connect_init(hs, HEADSET_STATE_PLAYING); - - if (cb) { - unsigned int id = connect_cb_new(hs, HEADSET_STATE_PLAYING, - cb, user_data); - if (cb_id) - *cb_id = id; - } - - return 0; -} - -static int hfp_cmp(struct headset *hs) -{ - if (hs->hfp_active) - return 0; - else - return -1; -} - -static void hfp_slc_complete(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - struct pending_connect *p = hs->pending; - - DBG("HFP Service Level Connection established"); - - headset_set_state(dev, HEADSET_STATE_CONNECTED); - - if (p == NULL) - return; - - if (p->target_state == HEADSET_STATE_CONNECTED) { - if (p->msg) { - DBusMessage *reply = dbus_message_new_method_return(p->msg); - g_dbus_send_message(dev->conn, reply); - } - pending_connect_finalize(dev); - return; - } - - p->err = sco_connect(dev, NULL, NULL, NULL); - if (p->err < 0) { - if (p->msg) - error_connect_failed(dev->conn, p->msg, p->err); - pending_connect_finalize(dev); - } -} - -static int telephony_generic_rsp(struct audio_device *device, cme_error_t err) -{ - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - - if ((err != CME_ERROR_NONE) && slc->cme_enabled) - return headset_send(hs, "\r\n+CME ERROR: %d\r\n", err); - - switch (err) { - case CME_ERROR_NONE: - return headset_send(hs, "\r\nOK\r\n"); - case CME_ERROR_NO_NETWORK_SERVICE: - return headset_send(hs, "\r\nNO CARRIER\r\n"); - default: - return headset_send(hs, "\r\nERROR\r\n"); - } -} - -int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err) -{ - struct audio_device *device = telephony_device; - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - int ret; - - if (err != CME_ERROR_NONE) - return telephony_generic_rsp(telephony_device, err); - - ret = headset_send(hs, "\r\nOK\r\n"); - if (ret < 0) - return ret; - - if (hs->state != HEADSET_STATE_CONNECTING) - return 0; - - if (slc->hf_features & HF_FEATURE_CALL_WAITING_AND_3WAY && - ag.features & AG_FEATURE_THREE_WAY_CALLING) - return 0; - - hfp_slc_complete(device); - - return 0; -} - -static int event_reporting(struct audio_device *dev, const char *buf) -{ - char **tokens; /* , , , , */ - - if (strlen(buf) < 13) - return -EINVAL; - - tokens = g_strsplit(&buf[8], ",", 5); - if (g_strv_length(tokens) < 4) { - g_strfreev(tokens); - return -EINVAL; - } - - ag.er_mode = atoi(tokens[0]); - ag.er_ind = atoi(tokens[3]); - - g_strfreev(tokens); - tokens = NULL; - - DBG("Event reporting (CMER): mode=%d, ind=%d", - ag.er_mode, ag.er_ind); - - switch (ag.er_ind) { - case 0: - case 1: - telephony_event_reporting_req(dev, ag.er_ind); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int call_hold(struct audio_device *dev, const char *buf) -{ - struct headset *hs = dev->headset; - int err; - - if (strlen(buf) < 9) - return -EINVAL; - - if (buf[8] != '?') { - telephony_call_hold_req(dev, &buf[8]); - return 0; - } - - err = headset_send(hs, "\r\n+CHLD: (%s)\r\n", ag.chld); - if (err < 0) - return err; - - err = headset_send(hs, "\r\nOK\r\n"); - if (err < 0) - return err; - - if (hs->state != HEADSET_STATE_CONNECTING) - return 0; - - hfp_slc_complete(dev); - - return 0; -} - -int telephony_key_press_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -static int key_press(struct audio_device *device, const char *buf) -{ - if (strlen(buf) < 9) - return -EINVAL; - - g_dbus_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, "AnswerRequested", - DBUS_TYPE_INVALID); - - if (ag.ring_timer) { - g_source_remove(ag.ring_timer); - ag.ring_timer = 0; - } - - telephony_key_press_req(device, &buf[8]); - - return 0; -} - -int telephony_answer_call_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -static int answer_call(struct audio_device *device, const char *buf) -{ - if (ag.ring_timer) { - g_source_remove(ag.ring_timer); - ag.ring_timer = 0; - } - - if (ag.number) { - g_free(ag.number); - ag.number = NULL; - } - - telephony_answer_call_req(device); - - return 0; -} - -int telephony_terminate_call_rsp(void *telephony_device, - cme_error_t err) -{ - struct audio_device *device = telephony_device; - struct headset *hs = device->headset; - - if (err != CME_ERROR_NONE) - return telephony_generic_rsp(telephony_device, err); - - g_dbus_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, "CallTerminated", - DBUS_TYPE_INVALID); - - return headset_send(hs, "\r\nOK\r\n"); -} - -static int terminate_call(struct audio_device *device, const char *buf) -{ - if (ag.number) { - g_free(ag.number); - ag.number = NULL; - } - - if (ag.ring_timer) { - g_source_remove(ag.ring_timer); - ag.ring_timer = 0; - } - - telephony_terminate_call_req(device); - - return 0; -} - -static int cli_notification(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - - if (strlen(buf) < 9) - return -EINVAL; - - slc->cli_active = buf[8] == '1' ? TRUE : FALSE; - - return headset_send(hs, "\r\nOK\r\n"); -} - -int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -static int response_and_hold(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - - if (strlen(buf) < 8) - return -EINVAL; - - if (ag.rh == BTRH_NOT_SUPPORTED) - return telephony_generic_rsp(device, CME_ERROR_NOT_SUPPORTED); - - if (buf[7] == '=') { - telephony_response_and_hold_req(device, atoi(&buf[8]) < 0); - return 0; - } - - if (ag.rh >= 0) - headset_send(hs, "\r\n+BTRH: %d\r\n", ag.rh); - - return headset_send(hs, "\r\nOK\r\n"); -} - -int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -static int last_dialed_number(struct audio_device *device, const char *buf) -{ - telephony_last_dialed_number_req(device); - - return 0; -} - -int telephony_dial_number_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -static int dial_number(struct audio_device *device, const char *buf) -{ - char number[BUF_SIZE]; - size_t buf_len; - - buf_len = strlen(buf); - - if (buf[buf_len - 1] != ';') { - DBG("Rejecting non-voice call dial request"); - return -EINVAL; - } - - memset(number, 0, sizeof(number)); - strncpy(number, &buf[3], buf_len - 4); - - telephony_dial_number_req(device, number); - - return 0; -} - -static int headset_set_gain(struct audio_device *device, uint16_t gain, char type) -{ - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - const char *name, *property; - - if (gain > 15) { - error("Invalid gain value: %u", gain); - return -EINVAL; - } - - switch (type) { - case HEADSET_GAIN_SPEAKER: - if (slc->sp_gain == gain) { - DBG("Ignoring no-change in speaker gain"); - return -EALREADY; - } - name = "SpeakerGainChanged"; - property = "SpeakerGain"; - slc->sp_gain = gain; - break; - case HEADSET_GAIN_MICROPHONE: - if (slc->mic_gain == gain) { - DBG("Ignoring no-change in microphone gain"); - return -EALREADY; - } - name = "MicrophoneGainChanged"; - property = "MicrophoneGain"; - slc->mic_gain = gain; - break; - default: - error("Unknown gain setting"); - return -EINVAL; - } - - g_dbus_emit_signal(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, name, - DBUS_TYPE_UINT16, &gain, - DBUS_TYPE_INVALID); - - emit_property_changed(device->conn, device->path, - AUDIO_HEADSET_INTERFACE, property, - DBUS_TYPE_UINT16, &gain); - - return 0; -} - -static int signal_gain_setting(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - dbus_uint16_t gain; - int err; - - if (strlen(buf) < 8) { - error("Too short string for Gain setting"); - return -EINVAL; - } - - gain = (dbus_uint16_t) strtol(&buf[7], NULL, 10); - - err = headset_set_gain(device, gain, buf[5]); - if (err < 0 && err != -EALREADY) - return err; - - return headset_send(hs, "\r\nOK\r\n"); -} - -int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -static int dtmf_tone(struct audio_device *device, const char *buf) -{ - char tone; - - if (strlen(buf) < 8) { - error("Too short string for DTMF tone"); - return -EINVAL; - } - - tone = buf[7]; - if (tone >= '#' && tone <= 'D') - telephony_transmit_dtmf_req(device, tone); - else - return -EINVAL; - - return 0; -} - -int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -static int subscriber_number(struct audio_device *device, const char *buf) -{ - telephony_subscriber_number_req(device); - - return 0; -} - -int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -static int list_current_calls(struct audio_device *device, const char *buf) -{ - telephony_list_current_calls_req(device); - - return 0; -} - -static int extended_errors(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - - if (strlen(buf) < 9) - return -EINVAL; - - if (buf[8] == '1') { - slc->cme_enabled = TRUE; - DBG("CME errors enabled for headset %p", hs); - } else { - slc->cme_enabled = FALSE; - DBG("CME errors disabled for headset %p", hs); - } - - return headset_send(hs, "\r\nOK\r\n"); -} - -static int call_waiting_notify(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - - if (strlen(buf) < 9) - return -EINVAL; - - if (buf[8] == '1') { - slc->cwa_enabled = TRUE; - DBG("Call waiting notification enabled for headset %p", hs); - } else { - slc->cwa_enabled = FALSE; - DBG("Call waiting notification disabled for headset %p", hs); - } - - return headset_send(hs, "\r\nOK\r\n"); -} - -int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -int telephony_call_hold_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -int telephony_nr_and_ec_rsp(void *telephony_device, cme_error_t err) -{ - struct audio_device *device = telephony_device; - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - - if (err == CME_ERROR_NONE) { - GSList *l; - - for (l = hs->nrec_cbs; l; l = l->next) { - struct headset_nrec_callback *nrec_cb = l->data; - - nrec_cb->cb(device, slc->nrec_req, nrec_cb->user_data); - } - - slc->nrec = hs->slc->nrec_req; - } - - return telephony_generic_rsp(telephony_device, err); -} - -int telephony_voice_dial_rsp(void *telephony_device, cme_error_t err) -{ - return telephony_generic_rsp(telephony_device, err); -} - -int telephony_operator_selection_ind(int mode, const char *oper) -{ - if (!active_devices) - return -ENODEV; - - send_foreach_headset(active_devices, hfp_cmp, - "\r\n+COPS: %d,0,\"%s\"\r\n", - mode, oper); - return 0; -} - -static int operator_selection(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - - if (strlen(buf) < 8) - return -EINVAL; - - switch (buf[7]) { - case '?': - telephony_operator_selection_req(device); - break; - case '=': - return headset_send(hs, "\r\nOK\r\n"); - default: - return -EINVAL; - } - - return 0; -} - -static int nr_and_ec(struct audio_device *device, const char *buf) -{ - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - - if (strlen(buf) < 9) - return -EINVAL; - - if (buf[8] == '0') - slc->nrec_req = FALSE; - else - slc->nrec_req = TRUE; - - telephony_nr_and_ec_req(device, slc->nrec_req); - - return 0; -} - -static int voice_dial(struct audio_device *device, const char *buf) -{ - gboolean enable; - - if (strlen(buf) < 9) - return -EINVAL; - - if (buf[8] == '0') - enable = FALSE; - else - enable = TRUE; - - telephony_voice_dial_req(device, enable); - - return 0; -} - -static int apple_command(struct audio_device *device, const char *buf) -{ - DBG("Got Apple command: %s", buf); - - return telephony_generic_rsp(device, CME_ERROR_NONE); -} - -static struct event event_callbacks[] = { - { "ATA", answer_call }, - { "ATD", dial_number }, - { "AT+VG", signal_gain_setting }, - { "AT+BRSF", supported_features }, - { "AT+CIND", report_indicators }, - { "AT+CMER", event_reporting }, - { "AT+CHLD", call_hold }, - { "AT+CHUP", terminate_call }, - { "AT+CKPD", key_press }, - { "AT+CLIP", cli_notification }, - { "AT+BTRH", response_and_hold }, - { "AT+BLDN", last_dialed_number }, - { "AT+VTS", dtmf_tone }, - { "AT+CNUM", subscriber_number }, - { "AT+CLCC", list_current_calls }, - { "AT+CMEE", extended_errors }, - { "AT+CCWA", call_waiting_notify }, - { "AT+COPS", operator_selection }, - { "AT+NREC", nr_and_ec }, - { "AT+BVRA", voice_dial }, - { "AT+XAPL", apple_command }, - { "AT+IPHONEACCEV", apple_command }, - { 0 } -}; - -static int handle_event(struct audio_device *device, const char *buf) -{ - struct event *ev; - - DBG("Received %s", buf); - - for (ev = event_callbacks; ev->cmd; ev++) { - if (!strncmp(buf, ev->cmd, strlen(ev->cmd))) - return ev->callback(device, buf); - } - - return -EINVAL; -} - -static void close_sco(struct audio_device *device) -{ - struct headset *hs = device->headset; - - if (hs->sco) { - int sock = g_io_channel_unix_get_fd(hs->sco); - shutdown(sock, SHUT_RDWR); - g_io_channel_shutdown(hs->sco, TRUE, NULL); - g_io_channel_unref(hs->sco); - hs->sco = NULL; - } - - if (hs->sco_id) { - g_source_remove(hs->sco_id); - hs->sco_id = 0; - } -} - -static gboolean rfcomm_io_cb(GIOChannel *chan, GIOCondition cond, - struct audio_device *device) -{ - struct headset *hs; - struct headset_slc *slc; - unsigned char buf[BUF_SIZE]; - ssize_t bytes_read; - size_t free_space; - int fd; - - if (cond & G_IO_NVAL) - return FALSE; - - hs = device->headset; - slc = hs->slc; - - if (cond & (G_IO_ERR | G_IO_HUP)) { - DBG("ERR or HUP on RFCOMM socket"); - goto failed; - } - - fd = g_io_channel_unix_get_fd(chan); - - bytes_read = read(fd, buf, sizeof(buf) - 1); - if (bytes_read < 0) - return TRUE; - - free_space = sizeof(slc->buf) - slc->data_start - - slc->data_length - 1; - - if (free_space < (size_t) bytes_read) { - /* Very likely that the HS is sending us garbage so - * just ignore the data and disconnect */ - error("Too much data to fit incoming buffer"); - goto failed; - } - - memcpy(&slc->buf[slc->data_start], buf, bytes_read); - slc->data_length += bytes_read; - - /* Make sure the data is null terminated so we can use string - * functions */ - slc->buf[slc->data_start + slc->data_length] = '\0'; - - while (slc->data_length > 0) { - char *cr; - int err; - off_t cmd_len; - - cr = strchr(&slc->buf[slc->data_start], '\r'); - if (!cr) - break; - - cmd_len = 1 + (off_t) cr - (off_t) &slc->buf[slc->data_start]; - *cr = '\0'; - - if (cmd_len > 1) - err = handle_event(device, &slc->buf[slc->data_start]); - else - /* Silently skip empty commands */ - err = 0; - - if (err == -EINVAL) { - error("Badly formated or unrecognized command: %s", - &slc->buf[slc->data_start]); - err = telephony_generic_rsp(device, - CME_ERROR_NOT_SUPPORTED); - if (err < 0) - goto failed; - } else if (err < 0) - error("Error handling command %s: %s (%d)", - &slc->buf[slc->data_start], - strerror(-err), -err); - - slc->data_start += cmd_len; - slc->data_length -= cmd_len; - - if (!slc->data_length) - slc->data_start = 0; - } - - return TRUE; - -failed: - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - - return FALSE; -} - -static gboolean sco_cb(GIOChannel *chan, GIOCondition cond, - struct audio_device *device) -{ - if (cond & G_IO_NVAL) - return FALSE; - - error("Audio connection got disconnected"); - - pending_connect_finalize(device); - headset_set_state(device, HEADSET_STATE_CONNECTED); - - return FALSE; -} - -void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) -{ - struct audio_device *dev = user_data; - struct headset *hs = dev->headset; - struct pending_connect *p = hs->pending; - char hs_address[18]; - - if (err) { - error("%s", err->message); - goto failed; - } - - /* For HFP telephony isn't ready just disconnect */ - if (hs->hfp_active && !ag.telephony_ready) { - error("Unable to accept HFP connection since the telephony " - "subsystem isn't initialized"); - goto failed; - } - - hs->rfcomm = hs->tmp_rfcomm; - hs->tmp_rfcomm = NULL; - - ba2str(&dev->dst, hs_address); - - if (p) - p->io = NULL; - else - hs->auto_dc = FALSE; - - g_io_add_watch(chan, G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL, - (GIOFunc) rfcomm_io_cb, dev); - - DBG("%s: Connected to %s", dev->path, hs_address); - - hs->slc = g_new0(struct headset_slc, 1); - hs->slc->sp_gain = 15; - hs->slc->mic_gain = 15; - hs->slc->nrec = TRUE; - - /* In HFP mode wait for Service Level Connection */ - if (hs->hfp_active) - return; - - headset_set_state(dev, HEADSET_STATE_CONNECTED); - - if (p && p->target_state == HEADSET_STATE_PLAYING) { - p->err = sco_connect(dev, NULL, NULL, NULL); - if (p->err < 0) - goto failed; - return; - } - - if (p && p->msg) { - DBusMessage *reply = dbus_message_new_method_return(p->msg); - g_dbus_send_message(dev->conn, reply); - } - - pending_connect_finalize(dev); - - return; - -failed: - if (p && p->msg) - error_connect_failed(dev->conn, p->msg, p->err); - pending_connect_finalize(dev); - if (hs->rfcomm) - headset_set_state(dev, HEADSET_STATE_CONNECTED); - else - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); -} - -static int headset_set_channel(struct headset *headset, - const sdp_record_t *record, uint16_t svc) -{ - int ch; - sdp_list_t *protos; - - if (sdp_get_access_protos(record, &protos) < 0) { - error("Unable to get access protos from headset record"); - return -1; - } - - ch = sdp_get_proto_port(protos, RFCOMM_UUID); - - sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); - sdp_list_free(protos, NULL); - - if (ch <= 0) { - error("Unable to get RFCOMM channel from Headset record"); - return -1; - } - - headset->rfcomm_ch = ch; - - if (svc == HANDSFREE_SVCLASS_ID) { - headset->hfp_handle = record->handle; - headset->hsp_handle = 0; - DBG("Discovered Handsfree service on channel %d", ch); - } else { - headset->hsp_handle = record->handle; - headset->hfp_handle = 0; - DBG("Discovered Headset service on channel %d", ch); - } - - return 0; -} - -static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) -{ - struct audio_device *dev = user_data; - struct headset *hs = dev->headset; - struct pending_connect *p = hs->pending; - sdp_record_t *record = NULL; - sdp_list_t *r; - uuid_t uuid; - - assert(hs->pending != NULL); - - if (err < 0) { - error("Unable to get service record: %s (%d)", - strerror(-err), -err); - p->err = -err; - if (p->msg) - error_connect_failed(dev->conn, p->msg, p->err); - goto failed; - } - - if (!recs || !recs->data) { - error("No records found"); - goto failed_not_supported; - } - - sdp_uuid16_create(&uuid, p->svclass); - - for (r = recs; r != NULL; r = r->next) { - sdp_list_t *classes; - uuid_t class; - - record = r->data; - - if (sdp_get_service_classes(record, &classes) < 0) { - error("Unable to get service classes from record"); - continue; - } - - memcpy(&class, classes->data, sizeof(uuid)); - - sdp_list_free(classes, free); - - if (sdp_uuid_cmp(&class, &uuid) == 0) - break; - } - - if (r == NULL) { - error("No record found with UUID 0x%04x", p->svclass); - goto failed_not_supported; - } - - if (headset_set_channel(hs, record, p->svclass) < 0) { - error("Unable to extract RFCOMM channel from service record"); - goto failed_not_supported; - } - - /* Set svclass to 0 so we can easily check that SDP is no-longer - * going on (to know if bt_cancel_discovery needs to be called) */ - p->svclass = 0; - - err = rfcomm_connect(dev, NULL, NULL, NULL); - if (err < 0) { - error("Unable to connect: %s (%d)", strerror(-err), -err); - p->err = -err; - if (p->msg != NULL) - error_connect_failed(dev->conn, p->msg, p->err); - goto failed; - } - - return; - -failed_not_supported: - if (p->svclass == HANDSFREE_SVCLASS_ID && - get_records(dev, NULL, NULL, NULL) == 0) - return; - if (p->msg) { - DBusMessage *reply = btd_error_not_supported(p->msg); - g_dbus_send_message(dev->conn, reply); - } -failed: - p->svclass = 0; - pending_connect_finalize(dev); - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); -} - -static int get_records(struct audio_device *device, headset_stream_cb_t cb, - void *user_data, unsigned int *cb_id) -{ - struct headset *hs = device->headset; - uint16_t svclass; - uuid_t uuid; - int err; - - if (hs->pending && hs->pending->svclass == HANDSFREE_SVCLASS_ID) - svclass = HEADSET_SVCLASS_ID; - else - svclass = hs->search_hfp ? HANDSFREE_SVCLASS_ID : - HEADSET_SVCLASS_ID; - - sdp_uuid16_create(&uuid, svclass); - - err = bt_search_service(&device->src, &device->dst, &uuid, - get_record_cb, device, NULL); - if (err < 0) - return err; - - if (hs->pending) { - hs->pending->svclass = svclass; - return 0; - } - - headset_set_state(device, HEADSET_STATE_CONNECTING); - - pending_connect_init(hs, HEADSET_STATE_CONNECTED); - - hs->pending->svclass = svclass; - - if (cb) { - unsigned int id; - id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, - cb, user_data); - if (cb_id) - *cb_id = id; - } - - return 0; -} - -static int rfcomm_connect(struct audio_device *dev, headset_stream_cb_t cb, - void *user_data, unsigned int *cb_id) -{ - struct headset *hs = dev->headset; - char address[18]; - GError *err = NULL; - - if (!manager_allow_headset_connection(dev)) - return -ECONNREFUSED; - - if (hs->rfcomm_ch < 0) - return get_records(dev, cb, user_data, cb_id); - - ba2str(&dev->dst, address); - - DBG("%s: Connecting to %s channel %d", dev->path, address, - hs->rfcomm_ch); - - hs->tmp_rfcomm = bt_io_connect(BT_IO_RFCOMM, headset_connect_cb, dev, - NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &dev->src, - BT_IO_OPT_DEST_BDADDR, &dev->dst, - BT_IO_OPT_CHANNEL, hs->rfcomm_ch, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_INVALID); - - hs->rfcomm_ch = -1; - - if (!hs->tmp_rfcomm) { - error("%s", err->message); - g_error_free(err); - return -EIO; - } - - hs->hfp_active = hs->hfp_handle != 0 ? TRUE : FALSE; - hs->rfcomm_initiator = FALSE; - - headset_set_state(dev, HEADSET_STATE_CONNECTING); - - pending_connect_init(hs, HEADSET_STATE_CONNECTED); - - if (cb) { - unsigned int id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, - cb, user_data); - if (cb_id) - *cb_id = id; - } - - return 0; -} - -static DBusMessage *hs_stop(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - DBusMessage *reply = NULL; - - if (hs->state < HEADSET_STATE_PLAY_IN_PROGRESS) - return btd_error_not_connected(msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - headset_set_state(device, HEADSET_STATE_CONNECTED); - - return reply; -} - -static DBusMessage *hs_is_playing(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - DBusMessage *reply; - dbus_bool_t playing; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - playing = (hs->state == HEADSET_STATE_PLAYING); - - dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &playing, - DBUS_TYPE_INVALID); - - return reply; -} - -static DBusMessage *hs_disconnect(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - char hs_address[18]; - - if (hs->state == HEADSET_STATE_DISCONNECTED) - return btd_error_not_connected(msg); - - headset_shutdown(device); - ba2str(&device->dst, hs_address); - info("Disconnected from %s, %s", hs_address, device->path); - - return dbus_message_new_method_return(msg); - -} - -static DBusMessage *hs_is_connected(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - DBusMessage *reply; - dbus_bool_t connected; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - connected = (device->headset->state >= HEADSET_STATE_CONNECTED); - - dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, - DBUS_TYPE_INVALID); - - return reply; -} - -static DBusMessage *hs_connect(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - int err; - - if (hs->state == HEADSET_STATE_CONNECTING) - return btd_error_in_progress(msg); - else if (hs->state > HEADSET_STATE_CONNECTING) - return btd_error_already_connected(msg); - - if (hs->hfp_handle && !ag.telephony_ready) - return btd_error_not_ready(msg); - - device->auto_connect = FALSE; - - err = rfcomm_connect(device, NULL, NULL, NULL); - if (err < 0) - return btd_error_failed(msg, strerror(-err)); - - hs->auto_dc = FALSE; - - hs->pending->msg = dbus_message_ref(msg); - - return NULL; -} - -static DBusMessage *hs_ring(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - DBusMessage *reply = NULL; - int err; - - if (hs->state < HEADSET_STATE_CONNECTED) - return btd_error_not_connected(msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - if (ag.ring_timer) { - DBG("IndicateCall received when already indicating"); - return reply; - } - - err = headset_send(hs, "\r\nRING\r\n"); - if (err < 0) { - dbus_message_unref(reply); - return btd_error_failed(msg, strerror(-err)); - } - - ring_timer_cb(NULL); - ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb, - NULL); - - return reply; -} - -static DBusMessage *hs_cancel_call(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - DBusMessage *reply = NULL; - - if (hs->state < HEADSET_STATE_CONNECTED) - return btd_error_not_connected(msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - if (ag.ring_timer) { - g_source_remove(ag.ring_timer); - ag.ring_timer = 0; - } else - DBG("Got CancelCall method call but no call is active"); - - return reply; -} - -static DBusMessage *hs_play(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - int err; - - if (sco_hci) { - error("Refusing Headset.Play() because SCO HCI routing " - "is enabled"); - return btd_error_not_available(msg); - } - - switch (hs->state) { - case HEADSET_STATE_DISCONNECTED: - case HEADSET_STATE_CONNECTING: - return btd_error_not_connected(msg); - case HEADSET_STATE_PLAY_IN_PROGRESS: - if (hs->pending && hs->pending->msg == NULL) { - hs->pending->msg = dbus_message_ref(msg); - return NULL; - } - return btd_error_busy(msg); - case HEADSET_STATE_PLAYING: - return btd_error_already_connected(msg); - case HEADSET_STATE_CONNECTED: - default: - break; - } - - err = sco_connect(device, NULL, NULL, NULL); - if (err < 0) - return btd_error_failed(msg, strerror(-err)); - - hs->pending->msg = dbus_message_ref(msg); - - return NULL; -} - -static DBusMessage *hs_get_speaker_gain(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - DBusMessage *reply; - dbus_uint16_t gain; - - if (hs->state < HEADSET_STATE_CONNECTED) - return btd_error_not_available(msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - gain = (dbus_uint16_t) slc->sp_gain; - - dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, - DBUS_TYPE_INVALID); - - return reply; -} - -static DBusMessage *hs_get_mic_gain(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - struct headset_slc *slc = hs->slc; - DBusMessage *reply; - dbus_uint16_t gain; - - if (hs->state < HEADSET_STATE_CONNECTED || slc == NULL) - return btd_error_not_available(msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - gain = (dbus_uint16_t) slc->mic_gain; - - dbus_message_append_args(reply, DBUS_TYPE_UINT16, &gain, - DBUS_TYPE_INVALID); - - return reply; -} - -static DBusMessage *hs_set_gain(DBusConnection *conn, - DBusMessage *msg, - void *data, uint16_t gain, - char type) -{ - struct audio_device *device = data; - struct headset *hs = device->headset; - DBusMessage *reply; - int err; - - if (hs->state < HEADSET_STATE_CONNECTED) - return btd_error_not_connected(msg); - - err = headset_set_gain(device, gain, type); - if (err < 0) - return btd_error_invalid_args(msg); - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - if (hs->state == HEADSET_STATE_PLAYING) { - err = headset_send(hs, "\r\n+VG%c=%u\r\n", type, gain); - if (err < 0) { - dbus_message_unref(reply); - return btd_error_failed(msg, strerror(-err)); - } - } - - return reply; -} - -static DBusMessage *hs_set_speaker_gain(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - uint16_t gain; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain, - DBUS_TYPE_INVALID)) - return NULL; - - return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_SPEAKER); -} - -static DBusMessage *hs_set_mic_gain(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - uint16_t gain; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &gain, - DBUS_TYPE_INVALID)) - return NULL; - - return hs_set_gain(conn, msg, data, gain, HEADSET_GAIN_MICROPHONE); -} - -static DBusMessage *hs_get_properties(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter dict; - gboolean value; - const char *state; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - - /* Playing */ - value = (device->headset->state == HEADSET_STATE_PLAYING); - dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value); - - /* State */ - state = state2str(device->headset->state); - if (state) - dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state); - - /* Connected */ - value = (device->headset->state >= HEADSET_STATE_CONNECTED); - dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value); - - if (!value) - goto done; - - /* SpeakerGain */ - dict_append_entry(&dict, "SpeakerGain", - DBUS_TYPE_UINT16, - &device->headset->slc->sp_gain); - - /* MicrophoneGain */ - dict_append_entry(&dict, "MicrophoneGain", - DBUS_TYPE_UINT16, - &device->headset->slc->mic_gain); - -done: - dbus_message_iter_close_container(&iter, &dict); - - return reply; -} - -static DBusMessage *hs_set_property(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *property; - DBusMessageIter iter; - DBusMessageIter sub; - uint16_t gain; - - if (!dbus_message_iter_init(msg, &iter)) - return btd_error_invalid_args(msg); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - return btd_error_invalid_args(msg); - - dbus_message_iter_get_basic(&iter, &property); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) - return btd_error_invalid_args(msg); - dbus_message_iter_recurse(&iter, &sub); - - if (g_str_equal("SpeakerGain", property)) { - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16) - return btd_error_invalid_args(msg); - - dbus_message_iter_get_basic(&sub, &gain); - return hs_set_gain(conn, msg, data, gain, - HEADSET_GAIN_SPEAKER); - } else if (g_str_equal("MicrophoneGain", property)) { - if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT16) - return btd_error_invalid_args(msg); - - dbus_message_iter_get_basic(&sub, &gain); - return hs_set_gain(conn, msg, data, gain, - HEADSET_GAIN_MICROPHONE); - } - - return btd_error_invalid_args(msg); -} - -static const GDBusMethodTable headset_methods[] = { - { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, hs_connect) }, - { GDBUS_METHOD("Disconnect", NULL, NULL, hs_disconnect) }, - { GDBUS_METHOD("IsConnected", - NULL, GDBUS_ARGS({ "connected", "b" }), - hs_is_connected) }, - { GDBUS_METHOD("IndicateCall", NULL, NULL, hs_ring) }, - { GDBUS_METHOD("CancelCall", NULL, NULL, hs_cancel_call) }, - { GDBUS_DEPRECATED_ASYNC_METHOD("Play", NULL, NULL, hs_play) }, - { GDBUS_METHOD("Stop", NULL, NULL, hs_stop) }, - { GDBUS_DEPRECATED_METHOD("IsPlaying", - NULL, GDBUS_ARGS({ "playing", "b" }), - hs_is_playing) }, - { GDBUS_DEPRECATED_METHOD("GetSpeakerGain", - NULL, GDBUS_ARGS({ "gain", "q" }), - hs_get_speaker_gain) }, - { GDBUS_DEPRECATED_METHOD("GetMicrophoneGain", - NULL, GDBUS_ARGS({ "gain", "q" }), - hs_get_mic_gain) }, - { GDBUS_DEPRECATED_METHOD("SetSpeakerGain", - GDBUS_ARGS({ "gain", "q" }), NULL, - hs_set_speaker_gain) }, - { GDBUS_DEPRECATED_METHOD("SetMicrophoneGain", - GDBUS_ARGS({ "gain", "q" }), NULL, - hs_set_mic_gain) }, - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - hs_get_properties) }, - { GDBUS_METHOD("SetProperty", - GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL, - hs_set_property) }, - { } -}; - -static const GDBusSignalTable headset_signals[] = { - { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("AnswerRequested", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("Stopped", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("Playing", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("SpeakerGainChanged", - GDBUS_ARGS({ "gain", "q" })) }, - { GDBUS_DEPRECATED_SIGNAL("MicrophoneGainChanged", - GDBUS_ARGS({ "gain", "q" })) }, - { GDBUS_SIGNAL("CallTerminated", NULL) }, - { GDBUS_SIGNAL("PropertyChanged", - GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { } -}; - -void headset_update(struct audio_device *dev, uint16_t svc, - const char *uuidstr) -{ - struct headset *headset = dev->headset; - const sdp_record_t *record; - - record = btd_device_get_record(dev->btd_dev, uuidstr); - if (!record) - return; - - switch (svc) { - case HANDSFREE_SVCLASS_ID: - if (headset->hfp_handle && - (headset->hfp_handle != record->handle)) { - error("More than one HFP record found on device"); - return; - } - - headset->hfp_handle = record->handle; - break; - - case HEADSET_SVCLASS_ID: - if (headset->hsp_handle && - (headset->hsp_handle != record->handle)) { - error("More than one HSP record found on device"); - return; - } - - headset->hsp_handle = record->handle; - - /* Ignore this record if we already have access to HFP */ - if (headset->hfp_handle) - return; - - break; - - default: - DBG("Invalid record passed to headset_update"); - return; - } -} - -static int headset_close_rfcomm(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - GIOChannel *rfcomm = hs->tmp_rfcomm ? hs->tmp_rfcomm : hs->rfcomm; - - if (rfcomm) { - g_io_channel_shutdown(rfcomm, TRUE, NULL); - g_io_channel_unref(rfcomm); - hs->tmp_rfcomm = NULL; - hs->rfcomm = NULL; - } - - g_free(hs->slc); - hs->slc = NULL; - - return 0; -} - -static void headset_free(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - if (hs->dc_timer) { - g_source_remove(hs->dc_timer); - hs->dc_timer = 0; - } - - close_sco(dev); - - headset_close_rfcomm(dev); - - g_slist_free_full(hs->nrec_cbs, g_free); - - g_free(hs); - dev->headset = NULL; -} - -static void path_unregister(void *data) -{ - struct audio_device *dev = data; - struct headset *hs = dev->headset; - - if (hs->state > HEADSET_STATE_DISCONNECTED) { - DBG("Headset unregistered while device was connected!"); - headset_shutdown(dev); - } - - DBG("Unregistered interface %s on path %s", - AUDIO_HEADSET_INTERFACE, dev->path); - - headset_free(dev); -} - -void headset_unregister(struct audio_device *dev) -{ - g_dbus_unregister_interface(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE); -} - -struct headset *headset_init(struct audio_device *dev, uint16_t svc, - const char *uuidstr) -{ - struct headset *hs; - const sdp_record_t *record; - - hs = g_new0(struct headset, 1); - hs->rfcomm_ch = -1; - hs->search_hfp = server_is_enabled(&dev->src, HANDSFREE_SVCLASS_ID); - - record = btd_device_get_record(dev->btd_dev, uuidstr); - if (!record) - goto register_iface; - - switch (svc) { - case HANDSFREE_SVCLASS_ID: - hs->hfp_handle = record->handle; - break; - - case HEADSET_SVCLASS_ID: - hs->hsp_handle = record->handle; - break; - - default: - DBG("Invalid record passed to headset_init"); - g_free(hs); - return NULL; - } - -register_iface: - if (!g_dbus_register_interface(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, - headset_methods, headset_signals, NULL, - dev, path_unregister)) { - g_free(hs); - return NULL; - } - - DBG("Registered interface %s on path %s", - AUDIO_HEADSET_INTERFACE, dev->path); - - return hs; -} - -uint32_t headset_config_init(GKeyFile *config) -{ - GError *err = NULL; - char *str; - - /* Use the default values if there is no config file */ - if (config == NULL) - return ag.features; - - str = g_key_file_get_string(config, "General", "SCORouting", - &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else { - if (strcmp(str, "PCM") == 0) - sco_hci = FALSE; - else if (strcmp(str, "HCI") == 0) - sco_hci = TRUE; - else - error("Invalid Headset Routing value: %s", str); - g_free(str); - } - - /* Init fast connectable option */ - str = g_key_file_get_string(config, "Headset", "FastConnectable", - &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else { - fast_connectable = strcmp(str, "true") == 0; - if (fast_connectable) - manager_set_fast_connectable(FALSE); - g_free(str); - } - - return ag.features; -} - -static gboolean hs_dc_timeout(struct audio_device *dev) -{ - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); - return FALSE; -} - -gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id) -{ - struct headset *hs = dev->headset; - struct pending_connect *p = hs->pending; - GSList *l; - struct connect_cb *cb = NULL; - - if (!p) - return FALSE; - - for (l = p->callbacks; l != NULL; l = l->next) { - struct connect_cb *tmp = l->data; - - if (tmp->id == id) { - cb = tmp; - break; - } - } - - if (!cb) - return FALSE; - - p->callbacks = g_slist_remove(p->callbacks, cb); - g_free(cb); - - if (p->callbacks || p->msg) - return TRUE; - - if (hs->auto_dc) { - if (hs->rfcomm) - hs->dc_timer = g_timeout_add_seconds(DC_TIMEOUT, - (GSourceFunc) hs_dc_timeout, - dev); - else - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); - } - - return TRUE; -} - -static gboolean dummy_connect_complete(struct audio_device *dev) -{ - pending_connect_finalize(dev); - return FALSE; -} - -unsigned int headset_request_stream(struct audio_device *dev, - headset_stream_cb_t cb, - void *user_data) -{ - struct headset *hs = dev->headset; - unsigned int id; - - if (hs->state == HEADSET_STATE_PLAYING) { - id = connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data); - g_idle_add((GSourceFunc) dummy_connect_complete, dev); - return id; - } - - if (hs->dc_timer) { - g_source_remove(hs->dc_timer); - hs->dc_timer = 0; - } - - if (hs->state == HEADSET_STATE_CONNECTING || - hs->state == HEADSET_STATE_PLAY_IN_PROGRESS) - return connect_cb_new(hs, HEADSET_STATE_PLAYING, cb, user_data); - - if (hs->rfcomm == NULL) { - if (rfcomm_connect(dev, cb, user_data, &id) < 0) - return 0; - hs->auto_dc = TRUE; - } else if (sco_connect(dev, cb, user_data, &id) < 0) - return 0; - - hs->pending->target_state = HEADSET_STATE_PLAYING; - - return id; -} - -unsigned int headset_config_stream(struct audio_device *dev, - gboolean auto_dc, - headset_stream_cb_t cb, - void *user_data) -{ - struct headset *hs = dev->headset; - unsigned int id = 0; - - if (hs->dc_timer) { - g_source_remove(hs->dc_timer); - hs->dc_timer = 0; - } - - if (hs->state == HEADSET_STATE_CONNECTING) - return connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, - user_data); - - if (hs->rfcomm) - goto done; - - if (rfcomm_connect(dev, cb, user_data, &id) < 0) - return 0; - - hs->auto_dc = auto_dc; - hs->pending->target_state = HEADSET_STATE_CONNECTED; - - return id; - -done: - id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data); - g_idle_add((GSourceFunc) dummy_connect_complete, dev); - return id; -} - -unsigned int headset_suspend_stream(struct audio_device *dev, - headset_stream_cb_t cb, - void *user_data) -{ - struct headset *hs = dev->headset; - unsigned int id; - int sock; - - if (hs->state == HEADSET_STATE_DISCONNECTED || - hs->state == HEADSET_STATE_CONNECTING) - return 0; - - if (hs->dc_timer) { - g_source_remove(hs->dc_timer); - hs->dc_timer = 0; - } - - if (hs->sco) { - sock = g_io_channel_unix_get_fd(hs->sco); - - /* shutdown but leave the socket open and wait for hup */ - shutdown(sock, SHUT_RDWR); - } else { - headset_set_state(dev, HEADSET_STATE_CONNECTED); - - g_idle_add((GSourceFunc) dummy_connect_complete, dev); - } - - id = connect_cb_new(hs, HEADSET_STATE_CONNECTED, cb, user_data); - - return id; -} - -gboolean headset_get_hfp_active(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - return hs->hfp_active; -} - -void headset_set_hfp_active(struct audio_device *dev, gboolean active) -{ - struct headset *hs = dev->headset; - - hs->hfp_active = active; -} - -gboolean headset_get_rfcomm_initiator(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - return hs->rfcomm_initiator; -} - -void headset_set_rfcomm_initiator(struct audio_device *dev, - gboolean initiator) -{ - struct headset *hs = dev->headset; - - hs->rfcomm_initiator = initiator; -} - -GIOChannel *headset_get_rfcomm(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - return hs->tmp_rfcomm; -} - -int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *io) -{ - struct headset *hs = dev->headset; - - if (hs->tmp_rfcomm) - return -EALREADY; - - hs->tmp_rfcomm = g_io_channel_ref(io); - - return 0; -} - -int headset_connect_sco(struct audio_device *dev, GIOChannel *io) -{ - struct headset *hs = dev->headset; - struct headset_slc *slc = hs->slc; - - if (hs->sco) - return -EISCONN; - - hs->sco = g_io_channel_ref(io); - - if (slc->pending_ring) { - ring_timer_cb(NULL); - ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, - ring_timer_cb, - NULL); - slc->pending_ring = FALSE; - } - - return 0; -} - -void headset_set_state(struct audio_device *dev, headset_state_t state) -{ - struct headset *hs = dev->headset; - struct headset_slc *slc = hs->slc; - gboolean value; - const char *state_str; - headset_state_t old_state = hs->state; - GSList *l; - - if (old_state == state) - return; - - state_str = state2str(state); - - switch (state) { - case HEADSET_STATE_DISCONNECTED: - value = FALSE; - close_sco(dev); - headset_close_rfcomm(dev); - emit_property_changed(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, "State", - DBUS_TYPE_STRING, &state_str); - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, - "Disconnected", - DBUS_TYPE_INVALID); - if (hs->state > HEADSET_STATE_CONNECTING) { - emit_property_changed(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, "Connected", - DBUS_TYPE_BOOLEAN, &value); - telephony_device_disconnected(dev); - } - active_devices = g_slist_remove(active_devices, dev); - break; - case HEADSET_STATE_CONNECTING: - emit_property_changed(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, "State", - DBUS_TYPE_STRING, &state_str); - break; - case HEADSET_STATE_CONNECTED: - close_sco(dev); - if (hs->state != HEADSET_STATE_PLAY_IN_PROGRESS) - emit_property_changed(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, "State", - DBUS_TYPE_STRING, &state_str); - if (hs->state < state) { - if (ag.features & AG_FEATURE_INBAND_RINGTONE) - slc->inband_ring = TRUE; - else - slc->inband_ring = FALSE; - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, - "Connected", - DBUS_TYPE_INVALID); - value = TRUE; - emit_property_changed(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, - "Connected", - DBUS_TYPE_BOOLEAN, &value); - active_devices = g_slist_append(active_devices, dev); - telephony_device_connected(dev); - } else if (hs->state == HEADSET_STATE_PLAYING) { - value = FALSE; - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, - "Stopped", - DBUS_TYPE_INVALID); - emit_property_changed(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, - "Playing", - DBUS_TYPE_BOOLEAN, &value); - } - break; - case HEADSET_STATE_PLAY_IN_PROGRESS: - break; - case HEADSET_STATE_PLAYING: - value = TRUE; - emit_property_changed(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, "State", - DBUS_TYPE_STRING, &state_str); - - /* Do not watch HUP since we need to know when the link is - really disconnected */ - hs->sco_id = g_io_add_watch(hs->sco, - G_IO_ERR | G_IO_NVAL, - (GIOFunc) sco_cb, dev); - - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, "Playing", - DBUS_TYPE_INVALID); - emit_property_changed(dev->conn, dev->path, - AUDIO_HEADSET_INTERFACE, "Playing", - DBUS_TYPE_BOOLEAN, &value); - - if (slc->sp_gain >= 0) - headset_send(hs, "\r\n+VGS=%u\r\n", slc->sp_gain); - if (slc->mic_gain >= 0) - headset_send(hs, "\r\n+VGM=%u\r\n", slc->mic_gain); - break; - } - - hs->state = state; - - DBG("State changed %s: %s -> %s", dev->path, str_state[old_state], - str_state[state]); - - for (l = headset_callbacks; l != NULL; l = l->next) { - struct headset_state_callback *cb = l->data; - cb->cb(dev, old_state, state, cb->user_data); - } -} - -headset_state_t headset_get_state(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - return hs->state; -} - -int headset_get_channel(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - return hs->rfcomm_ch; -} - -gboolean headset_is_active(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - if (hs->state != HEADSET_STATE_DISCONNECTED) - return TRUE; - - return FALSE; -} - -headset_lock_t headset_get_lock(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - return hs->lock; -} - -gboolean headset_lock(struct audio_device *dev, headset_lock_t lock) -{ - struct headset *hs = dev->headset; - - if (hs->lock & lock) - return FALSE; - - hs->lock |= lock; - - return TRUE; -} - -gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock) -{ - struct headset *hs = dev->headset; - - if (!(hs->lock & lock)) - return FALSE; - - hs->lock &= ~lock; - - if (hs->lock) - return TRUE; - - if (hs->state == HEADSET_STATE_PLAYING) - headset_set_state(dev, HEADSET_STATE_CONNECTED); - - if (hs->auto_dc) { - if (hs->state == HEADSET_STATE_CONNECTED) - hs->dc_timer = g_timeout_add_seconds(DC_TIMEOUT, - (GSourceFunc) hs_dc_timeout, - dev); - else - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); - } - - return TRUE; -} - -gboolean headset_suspend(struct audio_device *dev, void *data) -{ - return TRUE; -} - -gboolean headset_play(struct audio_device *dev, void *data) -{ - return TRUE; -} - -int headset_get_sco_fd(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - if (!hs->sco) - return -1; - - return g_io_channel_unix_get_fd(hs->sco); -} - -gboolean headset_get_nrec(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - if (!hs->slc) - return TRUE; - - return hs->slc->nrec; -} - -unsigned int headset_add_nrec_cb(struct audio_device *dev, - headset_nrec_cb cb, void *user_data) -{ - struct headset *hs = dev->headset; - struct headset_nrec_callback *nrec_cb; - static unsigned int id = 0; - - nrec_cb = g_new(struct headset_nrec_callback, 1); - nrec_cb->cb = cb; - nrec_cb->user_data = user_data; - nrec_cb->id = ++id; - - hs->nrec_cbs = g_slist_prepend(hs->nrec_cbs, nrec_cb); - - return nrec_cb->id; -} - -gboolean headset_remove_nrec_cb(struct audio_device *dev, unsigned int id) -{ - struct headset *hs = dev->headset; - GSList *l; - - for (l = hs->nrec_cbs; l != NULL; l = l->next) { - struct headset_nrec_callback *cb = l->data; - if (cb && cb->id == id) { - hs->nrec_cbs = g_slist_remove(hs->nrec_cbs, cb); - g_free(cb); - return TRUE; - } - } - - return FALSE; -} - -gboolean headset_get_inband(struct audio_device *dev) -{ - struct headset *hs = dev->headset; - - if (!hs->slc) - return TRUE; - - return hs->slc->inband_ring; -} - -gboolean headset_get_sco_hci(struct audio_device *dev) -{ - return sco_hci; -} - -void headset_shutdown(struct audio_device *dev) -{ - struct pending_connect *p = dev->headset->pending; - - if (p && p->msg) - error_connect_failed(dev->conn, p->msg, ECANCELED); - - pending_connect_finalize(dev); - headset_set_state(dev, HEADSET_STATE_DISCONNECTED); -} - -int telephony_event_ind(int index) -{ - if (!active_devices) - return -ENODEV; - - if (!ag.er_ind) { - DBG("telephony_report_event called but events are disabled"); - return -EINVAL; - } - - send_foreach_headset(active_devices, hfp_cmp, - "\r\n+CIEV: %d,%d\r\n", index + 1, - ag.indicators[index].val); - - return 0; -} - -int telephony_response_and_hold_ind(int rh) -{ - if (!active_devices) - return -ENODEV; - - ag.rh = rh; - - /* If we aren't in any response and hold state don't send anything */ - if (ag.rh < 0) - return 0; - - send_foreach_headset(active_devices, hfp_cmp, "\r\n+BTRH: %d\r\n", - ag.rh); - - return 0; -} - -int telephony_incoming_call_ind(const char *number, int type) -{ - struct audio_device *dev; - struct headset *hs; - struct headset_slc *slc; - - if (fast_connectable) - manager_set_fast_connectable(TRUE); - - if (!active_devices) - return -ENODEV; - - /* Get the latest connected device */ - dev = active_devices->data; - hs = dev->headset; - slc = hs->slc; - - if (ag.ring_timer) { - DBG("telephony_incoming_call_ind: already calling"); - return -EBUSY; - } - - /* With HSP 1.2 the RING messages should *not* be sent if inband - * ringtone is being used */ - if (!hs->hfp_active && slc->inband_ring) - return 0; - - g_free(ag.number); - ag.number = g_strdup(number); - ag.number_type = type; - - if (slc->inband_ring && hs->hfp_active && - hs->state != HEADSET_STATE_PLAYING) { - slc->pending_ring = TRUE; - return 0; - } - - ring_timer_cb(NULL); - ag.ring_timer = g_timeout_add_seconds(RING_INTERVAL, ring_timer_cb, - NULL); - - return 0; -} - -int telephony_calling_stopped_ind(void) -{ - struct audio_device *dev; - - if (fast_connectable) - manager_set_fast_connectable(FALSE); - - if (ag.ring_timer) { - g_source_remove(ag.ring_timer); - ag.ring_timer = 0; - } - - if (!active_devices) - return 0; - - /* In case SCO isn't fully up yet */ - dev = active_devices->data; - - if (!dev->headset->slc->pending_ring && !ag.ring_timer) - return -EINVAL; - - dev->headset->slc->pending_ring = FALSE; - - return 0; -} - -int telephony_ready_ind(uint32_t features, - const struct indicator *indicators, int rh, - const char *chld) -{ - ag.telephony_ready = TRUE; - ag.features = features; - ag.indicators = indicators; - ag.rh = rh; - ag.chld = chld; - - DBG("Telephony plugin initialized"); - - print_ag_features(ag.features); - - return 0; -} - -int telephony_deinit(void) -{ - g_free(ag.number); - - memset(&ag, 0, sizeof(ag)); - - ag.er_mode = 3; - ag.rh = BTRH_NOT_SUPPORTED; - - DBG("Telephony deinitialized"); - - return 0; -} - -int telephony_list_current_call_ind(int idx, int dir, int status, int mode, - int mprty, const char *number, - int type) -{ - if (!active_devices) - return -ENODEV; - - if (number && strlen(number) > 0) - send_foreach_headset(active_devices, hfp_cmp, - "\r\n+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n", - idx, dir, status, mode, mprty, number, type); - else - send_foreach_headset(active_devices, hfp_cmp, - "\r\n+CLCC: %d,%d,%d,%d,%d\r\n", - idx, dir, status, mode, mprty); - - return 0; -} - -int telephony_subscriber_number_ind(const char *number, int type, int service) -{ - if (!active_devices) - return -ENODEV; - - send_foreach_headset(active_devices, hfp_cmp, - "\r\n+CNUM: ,%s,%d,,%d\r\n", - number, type, service); - - return 0; -} - -static int cwa_cmp(struct headset *hs) -{ - if (!hs->hfp_active) - return -1; - - if (hs->slc->cwa_enabled) - return 0; - else - return -1; -} - -int telephony_call_waiting_ind(const char *number, int type) -{ - if (!active_devices) - return -ENODEV; - - send_foreach_headset(active_devices, cwa_cmp, - "\r\n+CCWA: \"%s\",%d\r\n", - number, type); - - return 0; -} - -unsigned int headset_add_state_cb(headset_state_cb cb, void *user_data) -{ - struct headset_state_callback *state_cb; - static unsigned int id = 0; - - state_cb = g_new(struct headset_state_callback, 1); - state_cb->cb = cb; - state_cb->user_data = user_data; - state_cb->id = ++id; - - headset_callbacks = g_slist_append(headset_callbacks, state_cb); - - return state_cb->id; -} - -gboolean headset_remove_state_cb(unsigned int id) -{ - GSList *l; - - for (l = headset_callbacks; l != NULL; l = l->next) { - struct headset_state_callback *cb = l->data; - if (cb && cb->id == id) { - headset_callbacks = g_slist_remove(headset_callbacks, cb); - g_free(cb); - return TRUE; - } - } - - return FALSE; -} diff -Nru bluez-4.101/audio/headset.h bluez-5.23/audio/headset.h --- bluez-4.101/audio/headset.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/headset.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define AUDIO_HEADSET_INTERFACE "org.bluez.Headset" - -#define DEFAULT_HS_AG_CHANNEL 12 -#define DEFAULT_HF_AG_CHANNEL 13 - -typedef enum { - HEADSET_STATE_DISCONNECTED, - HEADSET_STATE_CONNECTING, - HEADSET_STATE_CONNECTED, - HEADSET_STATE_PLAY_IN_PROGRESS, - HEADSET_STATE_PLAYING -} headset_state_t; - -typedef enum { - HEADSET_LOCK_READ = 1, - HEADSET_LOCK_WRITE = 1 << 1, -} headset_lock_t; - -typedef void (*headset_state_cb) (struct audio_device *dev, - headset_state_t old_state, - headset_state_t new_state, - void *user_data); -typedef void (*headset_nrec_cb) (struct audio_device *dev, - gboolean nrec, - void *user_data); - -unsigned int headset_add_state_cb(headset_state_cb cb, void *user_data); -gboolean headset_remove_state_cb(unsigned int id); - -typedef void (*headset_stream_cb_t) (struct audio_device *dev, void *user_data); - -void headset_connect_cb(GIOChannel *chan, GError *err, gpointer user_data); - -GIOChannel *headset_get_rfcomm(struct audio_device *dev); - -struct headset *headset_init(struct audio_device *dev, uint16_t svc, - const char *uuidstr); - -void headset_unregister(struct audio_device *dev); - -uint32_t headset_config_init(GKeyFile *config); - -void headset_update(struct audio_device *dev, uint16_t svc, - const char *uuidstr); - -unsigned int headset_config_stream(struct audio_device *dev, - gboolean auto_dc, - headset_stream_cb_t cb, - void *user_data); -unsigned int headset_request_stream(struct audio_device *dev, - headset_stream_cb_t cb, - void *user_data); -unsigned int headset_suspend_stream(struct audio_device *dev, - headset_stream_cb_t cb, - void *user_data); -gboolean headset_cancel_stream(struct audio_device *dev, unsigned int id); - -gboolean headset_get_hfp_active(struct audio_device *dev); -void headset_set_hfp_active(struct audio_device *dev, gboolean active); - -gboolean headset_get_rfcomm_initiator(struct audio_device *dev); -void headset_set_rfcomm_initiator(struct audio_device *dev, - gboolean initiator); - -int headset_connect_rfcomm(struct audio_device *dev, GIOChannel *chan); -int headset_connect_sco(struct audio_device *dev, GIOChannel *io); - -headset_state_t headset_get_state(struct audio_device *dev); -void headset_set_state(struct audio_device *dev, headset_state_t state); - -int headset_get_channel(struct audio_device *dev); - -int headset_get_sco_fd(struct audio_device *dev); -gboolean headset_get_nrec(struct audio_device *dev); -unsigned int headset_add_nrec_cb(struct audio_device *dev, - headset_nrec_cb cb, void *user_data); -gboolean headset_remove_nrec_cb(struct audio_device *dev, unsigned int id); -gboolean headset_get_inband(struct audio_device *dev); -gboolean headset_get_sco_hci(struct audio_device *dev); - -gboolean headset_is_active(struct audio_device *dev); - -headset_lock_t headset_get_lock(struct audio_device *dev); -gboolean headset_lock(struct audio_device *dev, headset_lock_t lock); -gboolean headset_unlock(struct audio_device *dev, headset_lock_t lock); -gboolean headset_suspend(struct audio_device *dev, void *data); -gboolean headset_play(struct audio_device *dev, void *data); -void headset_shutdown(struct audio_device *dev); diff -Nru bluez-4.101/audio/ipc.c bluez-5.23/audio/ipc.c --- bluez-4.101/audio/ipc.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/ipc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 "ipc.h" - -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -/* This table contains the string representation for messages types */ -static const char *strtypes[] = { - "BT_REQUEST", - "BT_RESPONSE", - "BT_INDICATION", - "BT_ERROR", -}; - -/* This table contains the string representation for messages names */ -static const char *strnames[] = { - "BT_GET_CAPABILITIES", - "BT_OPEN", - "BT_SET_CONFIGURATION", - "BT_NEW_STREAM", - "BT_START_STREAM", - "BT_STOP_STREAM", - "BT_SUSPEND_STREAM", - "BT_RESUME_STREAM", - "BT_CONTROL", -}; - -int bt_audio_service_open(void) -{ - int sk; - int err; - struct sockaddr_un addr = { - AF_UNIX, BT_IPC_SOCKET_NAME - }; - - sk = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) { - err = -errno; - fprintf(stderr, "%s: Cannot open socket: %s (%d)\n", - __FUNCTION__, strerror(-err), -err); - errno = -err; - return -1; - } - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = -errno; - fprintf(stderr, "%s: connect() failed: %s (%d)\n", - __FUNCTION__, strerror(-err), -err); - close(sk); - errno = -err; - return -1; - } - - return sk; -} - -int bt_audio_service_close(int sk) -{ - return close(sk); -} - -int bt_audio_service_get_data_fd(int sk) -{ - char cmsg_b[CMSG_SPACE(sizeof(int))], m; - int err, ret; - struct iovec iov = { &m, sizeof(m) }; - struct msghdr msgh; - struct cmsghdr *cmsg; - - memset(&msgh, 0, sizeof(msgh)); - msgh.msg_iov = &iov; - msgh.msg_iovlen = 1; - msgh.msg_control = &cmsg_b; - msgh.msg_controllen = CMSG_LEN(sizeof(int)); - - ret = recvmsg(sk, &msgh, 0); - if (ret < 0) { - err = -errno; - fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n", - __FUNCTION__, strerror(-err), -err); - errno = -err; - return -1; - } - - /* Receive auxiliary data in msgh */ - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET - && cmsg->cmsg_type == SCM_RIGHTS) { - memcpy(&ret, CMSG_DATA(cmsg), sizeof(int)); - return ret; - } - } - - errno = EINVAL; - return -1; -} - -const char *bt_audio_strtype(uint8_t type) -{ - if (type >= ARRAY_SIZE(strtypes)) - return NULL; - - return strtypes[type]; -} - -const char *bt_audio_strname(uint8_t name) -{ - if (name >= ARRAY_SIZE(strnames)) - return NULL; - - return strnames[name]; -} diff -Nru bluez-4.101/audio/ipc.h bluez-5.23/audio/ipc.h --- bluez-4.101/audio/ipc.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/ipc.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,361 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 - * - */ - -/* - Message sequence chart of streaming sequence for A2DP transport - - Audio daemon User - on snd_pcm_open - <--BT_GET_CAPABILITIES_REQ - - BT_GET_CAPABILITIES_RSP--> - - on snd_pcm_hw_params - <--BT_SETCONFIGURATION_REQ - - BT_SET_CONFIGURATION_RSP--> - - on snd_pcm_prepare - <--BT_START_STREAM_REQ - - - BT_START_STREAM_RSP--> - - BT_NEW_STREAM_IND --> - - < streams data > - .......... - - on snd_pcm_drop/snd_pcm_drain - - <--BT_STOP_STREAM_REQ - - - BT_STOP_STREAM_RSP--> - - on IPC close or appl crash - - - */ - -#ifndef BT_AUDIOCLIENT_H -#define BT_AUDIOCLIENT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include -#include - -#define BT_SUGGESTED_BUFFER_SIZE 512 -#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio" - -/* Generic message header definition, except for RESPONSE messages */ -typedef struct { - uint8_t type; - uint8_t name; - uint16_t length; -} __attribute__ ((packed)) bt_audio_msg_header_t; - -typedef struct { - bt_audio_msg_header_t h; - uint8_t posix_errno; -} __attribute__ ((packed)) bt_audio_error_t; - -/* Message types */ -#define BT_REQUEST 0 -#define BT_RESPONSE 1 -#define BT_INDICATION 2 -#define BT_ERROR 3 - -/* Messages names */ -#define BT_GET_CAPABILITIES 0 -#define BT_OPEN 1 -#define BT_SET_CONFIGURATION 2 -#define BT_NEW_STREAM 3 -#define BT_START_STREAM 4 -#define BT_STOP_STREAM 5 -#define BT_CLOSE 6 -#define BT_CONTROL 7 -#define BT_DELAY_REPORT 8 - -#define BT_CAPABILITIES_TRANSPORT_A2DP 0 -#define BT_CAPABILITIES_TRANSPORT_SCO 1 -#define BT_CAPABILITIES_TRANSPORT_ANY 2 - -#define BT_CAPABILITIES_ACCESS_MODE_READ 1 -#define BT_CAPABILITIES_ACCESS_MODE_WRITE 2 -#define BT_CAPABILITIES_ACCESS_MODE_READWRITE 3 - -#define BT_FLAG_AUTOCONNECT 1 - -struct bt_get_capabilities_req { - bt_audio_msg_header_t h; - char source[18]; /* Address of the local Device */ - char destination[18];/* Address of the remote Device */ - char object[128]; /* DBus object path */ - uint8_t transport; /* Requested transport */ - uint8_t flags; /* Requested flags */ - uint8_t seid; /* Requested capability configuration */ -} __attribute__ ((packed)); - -/** - * SBC Codec parameters as per A2DP profile 1.0 § 4.3 - */ - -/* A2DP seid are 6 bytes long so HSP/HFP are assigned to 7-8 bits */ -#define BT_A2DP_SEID_RANGE (1 << 6) - 1 - -#define BT_A2DP_SBC_SOURCE 0x00 -#define BT_A2DP_SBC_SINK 0x01 -#define BT_A2DP_MPEG12_SOURCE 0x02 -#define BT_A2DP_MPEG12_SINK 0x03 -#define BT_A2DP_MPEG24_SOURCE 0x04 -#define BT_A2DP_MPEG24_SINK 0x05 -#define BT_A2DP_ATRAC_SOURCE 0x06 -#define BT_A2DP_ATRAC_SINK 0x07 -#define BT_A2DP_UNKNOWN_SOURCE 0x08 -#define BT_A2DP_UNKNOWN_SINK 0x09 - -#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3) -#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2) -#define BT_SBC_SAMPLING_FREQ_44100 (1 << 1) -#define BT_SBC_SAMPLING_FREQ_48000 1 - -#define BT_A2DP_CHANNEL_MODE_MONO (1 << 3) -#define BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL (1 << 2) -#define BT_A2DP_CHANNEL_MODE_STEREO (1 << 1) -#define BT_A2DP_CHANNEL_MODE_JOINT_STEREO 1 - -#define BT_A2DP_BLOCK_LENGTH_4 (1 << 3) -#define BT_A2DP_BLOCK_LENGTH_8 (1 << 2) -#define BT_A2DP_BLOCK_LENGTH_12 (1 << 1) -#define BT_A2DP_BLOCK_LENGTH_16 1 - -#define BT_A2DP_SUBBANDS_4 (1 << 1) -#define BT_A2DP_SUBBANDS_8 1 - -#define BT_A2DP_ALLOCATION_SNR (1 << 1) -#define BT_A2DP_ALLOCATION_LOUDNESS 1 - -#define BT_MPEG_SAMPLING_FREQ_16000 (1 << 5) -#define BT_MPEG_SAMPLING_FREQ_22050 (1 << 4) -#define BT_MPEG_SAMPLING_FREQ_24000 (1 << 3) -#define BT_MPEG_SAMPLING_FREQ_32000 (1 << 2) -#define BT_MPEG_SAMPLING_FREQ_44100 (1 << 1) -#define BT_MPEG_SAMPLING_FREQ_48000 1 - -#define BT_MPEG_LAYER_1 (1 << 2) -#define BT_MPEG_LAYER_2 (1 << 1) -#define BT_MPEG_LAYER_3 1 - -#define BT_HFP_CODEC_PCM 0x00 - -#define BT_PCM_FLAG_NREC 0x01 -#define BT_PCM_FLAG_PCM_ROUTING 0x02 - -#define BT_WRITE_LOCK (1 << 1) -#define BT_READ_LOCK 1 - -typedef struct { - uint8_t seid; - uint8_t transport; - uint8_t type; - uint8_t length; - uint8_t configured; - uint8_t lock; - uint8_t data[0]; -} __attribute__ ((packed)) codec_capabilities_t; - -typedef struct { - codec_capabilities_t capability; - uint8_t channel_mode; - uint8_t frequency; - uint8_t allocation_method; - uint8_t subbands; - uint8_t block_length; - uint8_t min_bitpool; - uint8_t max_bitpool; -} __attribute__ ((packed)) sbc_capabilities_t; - -typedef struct { - codec_capabilities_t capability; - uint8_t channel_mode; - uint8_t crc; - uint8_t layer; - uint8_t frequency; - uint8_t mpf; - uint16_t bitrate; -} __attribute__ ((packed)) mpeg_capabilities_t; - -typedef struct { - codec_capabilities_t capability; - uint8_t flags; - uint16_t sampling_rate; -} __attribute__ ((packed)) pcm_capabilities_t; - -struct bt_get_capabilities_rsp { - bt_audio_msg_header_t h; - char source[18]; /* Address of the local Device */ - char destination[18];/* Address of the remote Device */ - char object[128]; /* DBus object path */ - uint8_t data[0]; /* First codec_capabilities_t */ -} __attribute__ ((packed)); - -struct bt_open_req { - bt_audio_msg_header_t h; - char source[18]; /* Address of the local Device */ - char destination[18];/* Address of the remote Device */ - char object[128]; /* DBus object path */ - uint8_t seid; /* Requested capability configuration to lock */ - uint8_t lock; /* Requested lock */ -} __attribute__ ((packed)); - -struct bt_open_rsp { - bt_audio_msg_header_t h; - char source[18]; /* Address of the local Device */ - char destination[18];/* Address of the remote Device */ - char object[128]; /* DBus object path */ -} __attribute__ ((packed)); - -struct bt_set_configuration_req { - bt_audio_msg_header_t h; - codec_capabilities_t codec; /* Requested codec */ -} __attribute__ ((packed)); - -struct bt_set_configuration_rsp { - bt_audio_msg_header_t h; - uint16_t link_mtu; /* Max length that transport supports */ -} __attribute__ ((packed)); - -#define BT_STREAM_ACCESS_READ 0 -#define BT_STREAM_ACCESS_WRITE 1 -#define BT_STREAM_ACCESS_READWRITE 2 -struct bt_start_stream_req { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -struct bt_start_stream_rsp { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -/* This message is followed by one byte of data containing the stream data fd - as ancillary data */ -struct bt_new_stream_ind { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -struct bt_stop_stream_req { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -struct bt_stop_stream_rsp { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -struct bt_close_req { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -struct bt_close_rsp { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -struct bt_suspend_stream_ind { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -struct bt_resume_stream_ind { - bt_audio_msg_header_t h; -} __attribute__ ((packed)); - -#define BT_CONTROL_KEY_POWER 0x40 -#define BT_CONTROL_KEY_VOL_UP 0x41 -#define BT_CONTROL_KEY_VOL_DOWN 0x42 -#define BT_CONTROL_KEY_MUTE 0x43 -#define BT_CONTROL_KEY_PLAY 0x44 -#define BT_CONTROL_KEY_STOP 0x45 -#define BT_CONTROL_KEY_PAUSE 0x46 -#define BT_CONTROL_KEY_RECORD 0x47 -#define BT_CONTROL_KEY_REWIND 0x48 -#define BT_CONTROL_KEY_FAST_FORWARD 0x49 -#define BT_CONTROL_KEY_EJECT 0x4A -#define BT_CONTROL_KEY_FORWARD 0x4B -#define BT_CONTROL_KEY_BACKWARD 0x4C - -struct bt_control_req { - bt_audio_msg_header_t h; - uint8_t mode; /* Control Mode */ - uint8_t key; /* Control Key */ -} __attribute__ ((packed)); - -struct bt_control_rsp { - bt_audio_msg_header_t h; - uint8_t mode; /* Control Mode */ - uint8_t key; /* Control Key */ -} __attribute__ ((packed)); - -struct bt_control_ind { - bt_audio_msg_header_t h; - uint8_t mode; /* Control Mode */ - uint8_t key; /* Control Key */ -} __attribute__ ((packed)); - -struct bt_delay_report_req { - bt_audio_msg_header_t h; - uint16_t delay; -} __attribute__ ((packed)); - -struct bt_delay_report_ind { - bt_audio_msg_header_t h; - uint16_t delay; -} __attribute__ ((packed)); - -/* Function declaration */ - -/* Opens a connection to the audio service: return a socket descriptor */ -int bt_audio_service_open(void); - -/* Closes a connection to the audio service */ -int bt_audio_service_close(int sk); - -/* Receives stream data file descriptor : must be called after a -BT_STREAMFD_IND message is returned */ -int bt_audio_service_get_data_fd(int sk); - -/* Human readable message type string */ -const char *bt_audio_strtype(uint8_t type); - -/* Human readable message name string */ -const char *bt_audio_strname(uint8_t name); - -#ifdef __cplusplus -} -#endif - -#endif /* BT_AUDIOCLIENT_H */ diff -Nru bluez-4.101/audio/main.c bluez-5.23/audio/main.c --- bluez-4.101/audio/main.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,194 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "glib-helper.h" -#include "btio.h" -#include "plugin.h" -#include "log.h" -#include "device.h" -#include "headset.h" -#include "manager.h" -#include "gateway.h" - -static GIOChannel *sco_server = NULL; - -static GKeyFile *load_config_file(const char *file) -{ - GError *err = NULL; - GKeyFile *keyfile; - - keyfile = g_key_file_new(); - - g_key_file_set_list_separator(keyfile, ','); - - if (!g_key_file_load_from_file(keyfile, file, 0, &err)) { - error("Parsing %s failed: %s", file, err->message); - g_error_free(err); - g_key_file_free(keyfile); - return NULL; - } - - return keyfile; -} - -static void sco_server_cb(GIOChannel *chan, GError *err, gpointer data) -{ - int sk; - struct audio_device *device; - char addr[18]; - bdaddr_t src, dst; - - if (err) { - error("sco_server_cb: %s", err->message); - return; - } - - bt_io_get(chan, BT_IO_SCO, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_DEST_BDADDR, &dst, - BT_IO_OPT_DEST, addr, - BT_IO_OPT_INVALID); - if (err) { - error("bt_io_get: %s", err->message); - goto drop; - } - - device = manager_find_device(NULL, &src, &dst, AUDIO_HEADSET_INTERFACE, - FALSE); - if (!device) - device = manager_find_device(NULL, &src, &dst, - AUDIO_GATEWAY_INTERFACE, - FALSE); - - if (!device) - goto drop; - - if (device->headset) { - if (headset_get_state(device) < HEADSET_STATE_CONNECTED) { - DBG("Refusing SCO from non-connected headset"); - goto drop; - } - - if (!headset_get_hfp_active(device)) { - error("Refusing non-HFP SCO connect attempt from %s", - addr); - goto drop; - } - - if (headset_connect_sco(device, chan) < 0) - goto drop; - - headset_set_state(device, HEADSET_STATE_PLAYING); - } else if (device->gateway) { - if (!gateway_is_connected(device)) { - DBG("Refusing SCO from non-connected AG"); - goto drop; - } - - if (gateway_connect_sco(device, chan) < 0) - goto drop; - } else - goto drop; - - sk = g_io_channel_unix_get_fd(chan); - fcntl(sk, F_SETFL, 0); - - DBG("Accepted SCO connection from %s", addr); - - return; - -drop: - g_io_channel_shutdown(chan, TRUE, NULL); -} - -static DBusConnection *connection; - -static int audio_init(void) -{ - GKeyFile *config; - gboolean enable_sco; - - connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - if (connection == NULL) - return -EIO; - - config = load_config_file(CONFIGDIR "/audio.conf"); - - if (audio_manager_init(connection, config, &enable_sco) < 0) - goto failed; - - if (!enable_sco) - return 0; - - sco_server = bt_io_listen(BT_IO_SCO, sco_server_cb, NULL, NULL, - NULL, NULL, - BT_IO_OPT_INVALID); - if (!sco_server) { - error("Unable to start SCO server socket"); - goto failed; - } - - return 0; - -failed: - audio_manager_exit(); - - if (connection) { - dbus_connection_unref(connection); - connection = NULL; - } - - return -EIO; -} - -static void audio_exit(void) -{ - if (sco_server) { - g_io_channel_shutdown(sco_server, TRUE, NULL); - g_io_channel_unref(sco_server); - sco_server = NULL; - } - - audio_manager_exit(); - - dbus_connection_unref(connection); -} - -BLUETOOTH_PLUGIN_DEFINE(audio, VERSION, - BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, audio_init, audio_exit) diff -Nru bluez-4.101/audio/manager.c bluez-5.23/audio/manager.c --- bluez-4.101/audio/manager.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/manager.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1456 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include "glib-helper.h" -#include "btio.h" -#include "../src/adapter.h" -#include "../src/manager.h" -#include "../src/device.h" - -#include "log.h" -#include "ipc.h" -#include "device.h" -#include "error.h" -#include "avdtp.h" -#include "media.h" -#include "a2dp.h" -#include "headset.h" -#include "gateway.h" -#include "sink.h" -#include "source.h" -#include "avrcp.h" -#include "control.h" -#include "manager.h" -#include "sdpd.h" -#include "telephony.h" -#include "unix.h" - -typedef enum { - HEADSET = 1 << 0, - GATEWAY = 1 << 1, - SINK = 1 << 2, - SOURCE = 1 << 3, - CONTROL = 1 << 4, - TARGET = 1 << 5, - INVALID = 1 << 6 -} audio_service_type; - -typedef enum { - GENERIC_AUDIO = 0, - ADVANCED_AUDIO, - AV_REMOTE, - GET_RECORDS -} audio_sdp_state_t; - -struct audio_adapter { - struct btd_adapter *btd_adapter; - gboolean powered; - uint32_t hsp_ag_record_id; - uint32_t hfp_ag_record_id; - uint32_t hfp_hs_record_id; - GIOChannel *hsp_ag_server; - GIOChannel *hfp_ag_server; - GIOChannel *hfp_hs_server; - gint ref; -}; - -static gboolean auto_connect = TRUE; -static int max_connected_headsets = 1; -static DBusConnection *connection = NULL; -static GKeyFile *config = NULL; -static GSList *adapters = NULL; -static GSList *devices = NULL; - -static struct enabled_interfaces enabled = { - .hfp = TRUE, - .headset = TRUE, - .gateway = FALSE, - .sink = TRUE, - .source = FALSE, - .control = TRUE, - .socket = FALSE, - .media = TRUE, -}; - -static struct audio_adapter *find_adapter(GSList *list, - struct btd_adapter *btd_adapter) -{ - for (; list; list = list->next) { - struct audio_adapter *adapter = list->data; - - if (adapter->btd_adapter == btd_adapter) - return adapter; - } - - return NULL; -} - -gboolean server_is_enabled(bdaddr_t *src, uint16_t svc) -{ - switch (svc) { - case HEADSET_SVCLASS_ID: - return enabled.headset; - case HEADSET_AGW_SVCLASS_ID: - return FALSE; - case HANDSFREE_SVCLASS_ID: - return enabled.headset && enabled.hfp; - case HANDSFREE_AGW_SVCLASS_ID: - return enabled.gateway; - case AUDIO_SINK_SVCLASS_ID: - return enabled.sink; - case AUDIO_SOURCE_SVCLASS_ID: - return enabled.source; - case AV_REMOTE_TARGET_SVCLASS_ID: - case AV_REMOTE_SVCLASS_ID: - return enabled.control; - default: - return FALSE; - } -} - -static void handle_uuid(const char *uuidstr, struct audio_device *device) -{ - uuid_t uuid; - uint16_t uuid16; - - if (bt_string2uuid(&uuid, uuidstr) < 0) { - error("%s not detected as an UUID-128", uuidstr); - return; - } - - if (!sdp_uuid128_to_uuid(&uuid) && uuid.type != SDP_UUID16) { - error("Could not convert %s to a UUID-16", uuidstr); - return; - } - - uuid16 = uuid.value.uuid16; - - if (!server_is_enabled(&device->src, uuid16)) { - DBG("server not enabled for %s (0x%04x)", uuidstr, uuid16); - return; - } - - switch (uuid16) { - case HEADSET_SVCLASS_ID: - DBG("Found Headset record"); - if (device->headset) - headset_update(device, uuid16, uuidstr); - else - device->headset = headset_init(device, uuid16, - uuidstr); - break; - case HEADSET_AGW_SVCLASS_ID: - DBG("Found Headset AG record"); - break; - case HANDSFREE_SVCLASS_ID: - DBG("Found Handsfree record"); - if (device->headset) - headset_update(device, uuid16, uuidstr); - else - device->headset = headset_init(device, uuid16, - uuidstr); - break; - case HANDSFREE_AGW_SVCLASS_ID: - DBG("Found Handsfree AG record"); - if (enabled.gateway && (device->gateway == NULL)) - device->gateway = gateway_init(device); - break; - case AUDIO_SINK_SVCLASS_ID: - DBG("Found Audio Sink"); - if (device->sink == NULL) - device->sink = sink_init(device); - break; - case AUDIO_SOURCE_SVCLASS_ID: - DBG("Found Audio Source"); - if (device->source == NULL) - device->source = source_init(device); - break; - case AV_REMOTE_SVCLASS_ID: - case AV_REMOTE_TARGET_SVCLASS_ID: - DBG("Found AV %s", uuid16 == AV_REMOTE_SVCLASS_ID ? - "Remote" : "Target"); - if (device->control) - control_update(device->control, uuid16); - else - device->control = control_init(device, uuid16); - - if (device->sink && sink_is_active(device)) - avrcp_connect(device); - break; - default: - DBG("Unrecognized UUID: 0x%04X", uuid16); - break; - } -} - -static sdp_record_t *hsp_ag_record(uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_record_t *record; - sdp_list_t *aproto, *proto[2]; - sdp_data_t *channel; - - record = sdp_record_alloc(); - if (!record) - return NULL; - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(record, root); - - sdp_uuid16_create(&svclass_uuid, HEADSET_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID); - profile.version = 0x0102; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(record, aproto); - - sdp_set_info_attr(record, "Headset Audio Gateway", 0, 0); - - sdp_data_free(channel); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); - - return record; -} - -static sdp_record_t *hfp_hs_record(uint8_t ch) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_record_t *record; - sdp_list_t *aproto, *proto[2]; - sdp_data_t *channel; - - record = sdp_record_alloc(); - if (!record) - return NULL; - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(record, root); - - sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); - profile.version = 0x0105; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(record, aproto); - - sdp_set_info_attr(record, "Hands-Free", 0, 0); - - sdp_data_free(channel); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); - - return record; -} - -static sdp_record_t *hfp_ag_record(uint8_t ch, uint32_t feat) -{ - sdp_list_t *svclass_id, *pfseq, *apseq, *root; - uuid_t root_uuid, svclass_uuid, ga_svclass_uuid; - uuid_t l2cap_uuid, rfcomm_uuid; - sdp_profile_desc_t profile; - sdp_list_t *aproto, *proto[2]; - sdp_record_t *record; - sdp_data_t *channel, *features; - uint8_t netid = 0x01; - uint16_t sdpfeat; - sdp_data_t *network; - - record = sdp_record_alloc(); - if (!record) - return NULL; - - network = sdp_data_alloc(SDP_UINT8, &netid); - if (!network) { - sdp_record_free(record); - return NULL; - } - - sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); - root = sdp_list_append(0, &root_uuid); - sdp_set_browse_groups(record, root); - - sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID); - svclass_id = sdp_list_append(0, &svclass_uuid); - sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID); - svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid); - sdp_set_service_classes(record, svclass_id); - - sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID); - profile.version = 0x0105; - pfseq = sdp_list_append(0, &profile); - sdp_set_profile_descs(record, pfseq); - - sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); - proto[0] = sdp_list_append(0, &l2cap_uuid); - apseq = sdp_list_append(0, proto[0]); - - sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); - proto[1] = sdp_list_append(0, &rfcomm_uuid); - channel = sdp_data_alloc(SDP_UINT8, &ch); - proto[1] = sdp_list_append(proto[1], channel); - apseq = sdp_list_append(apseq, proto[1]); - - sdpfeat = (uint16_t) feat & 0xF; - features = sdp_data_alloc(SDP_UINT16, &sdpfeat); - sdp_attr_add(record, SDP_ATTR_SUPPORTED_FEATURES, features); - - aproto = sdp_list_append(0, apseq); - sdp_set_access_protos(record, aproto); - - sdp_set_info_attr(record, "Hands-Free Audio Gateway", 0, 0); - - sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network); - - sdp_data_free(channel); - sdp_list_free(proto[0], 0); - sdp_list_free(proto[1], 0); - sdp_list_free(apseq, 0); - sdp_list_free(pfseq, 0); - sdp_list_free(aproto, 0); - sdp_list_free(root, 0); - sdp_list_free(svclass_id, 0); - - return record; -} - -static void headset_auth_cb(DBusError *derr, void *user_data) -{ - struct audio_device *device = user_data; - GError *err = NULL; - GIOChannel *io; - - if (device->hs_preauth_id) { - g_source_remove(device->hs_preauth_id); - device->hs_preauth_id = 0; - } - - if (derr && dbus_error_is_set(derr)) { - error("Access denied: %s", derr->message); - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - return; - } - - io = headset_get_rfcomm(device); - - if (!bt_io_accept(io, headset_connect_cb, device, NULL, &err)) { - error("bt_io_accept: %s", err->message); - g_error_free(err); - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - return; - } -} - -static gboolean hs_preauth_cb(GIOChannel *chan, GIOCondition cond, - gpointer user_data) -{ - struct audio_device *device = user_data; - - DBG("Headset disconnected during authorization"); - - audio_device_cancel_authorization(device, headset_auth_cb, device); - - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - - device->hs_preauth_id = 0; - - return FALSE; -} - -static void ag_confirm(GIOChannel *chan, gpointer data) -{ - const char *server_uuid, *remote_uuid; - struct audio_device *device; - gboolean hfp_active; - bdaddr_t src, dst; - int perr; - GError *err = NULL; - uint8_t ch; - - bt_io_get(chan, BT_IO_RFCOMM, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_DEST_BDADDR, &dst, - BT_IO_OPT_CHANNEL, &ch, - BT_IO_OPT_INVALID); - if (err) { - error("%s", err->message); - g_error_free(err); - goto drop; - } - - if (ch == DEFAULT_HS_AG_CHANNEL) { - hfp_active = FALSE; - server_uuid = HSP_AG_UUID; - remote_uuid = HSP_HS_UUID; - } else { - hfp_active = TRUE; - server_uuid = HFP_AG_UUID; - remote_uuid = HFP_HS_UUID; - } - - device = manager_get_device(&src, &dst, TRUE); - if (!device) - goto drop; - - if (!manager_allow_headset_connection(device)) { - DBG("Refusing headset: too many existing connections"); - goto drop; - } - - if (!device->headset) { - btd_device_add_uuid(device->btd_dev, remote_uuid); - if (!device->headset) - goto drop; - } - - if (headset_get_state(device) > HEADSET_STATE_DISCONNECTED) { - DBG("Refusing new connection since one already exists"); - goto drop; - } - - headset_set_hfp_active(device, hfp_active); - headset_set_rfcomm_initiator(device, TRUE); - - if (headset_connect_rfcomm(device, chan) < 0) { - error("headset_connect_rfcomm failed"); - goto drop; - } - - headset_set_state(device, HEADSET_STATE_CONNECTING); - - perr = audio_device_request_authorization(device, server_uuid, - headset_auth_cb, device); - if (perr < 0) { - DBG("Authorization denied: %s", strerror(-perr)); - headset_set_state(device, HEADSET_STATE_DISCONNECTED); - return; - } - - device->hs_preauth_id = g_io_add_watch(chan, - G_IO_NVAL | G_IO_HUP | G_IO_ERR, - hs_preauth_cb, device); - - device->auto_connect = auto_connect; - - return; - -drop: - g_io_channel_shutdown(chan, TRUE, NULL); -} - -static void gateway_auth_cb(DBusError *derr, void *user_data) -{ - struct audio_device *device = user_data; - - if (derr && dbus_error_is_set(derr)) { - error("Access denied: %s", derr->message); - gateway_set_state(device, GATEWAY_STATE_DISCONNECTED); - } else { - char ag_address[18]; - - ba2str(&device->dst, ag_address); - DBG("Accepted AG connection from %s for %s", - ag_address, device->path); - - gateway_start_service(device); - } -} - -static void hf_io_cb(GIOChannel *chan, gpointer data) -{ - bdaddr_t src, dst; - GError *err = NULL; - uint8_t ch; - const char *server_uuid, *remote_uuid; - struct audio_device *device; - int perr; - - bt_io_get(chan, BT_IO_RFCOMM, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_DEST_BDADDR, &dst, - BT_IO_OPT_CHANNEL, &ch, - BT_IO_OPT_INVALID); - - if (err) { - error("%s", err->message); - g_error_free(err); - return; - } - - server_uuid = HFP_HS_UUID; - remote_uuid = HFP_AG_UUID; - - device = manager_get_device(&src, &dst, TRUE); - if (!device) - goto drop; - - if (!device->gateway) { - btd_device_add_uuid(device->btd_dev, remote_uuid); - if (!device->gateway) - goto drop; - } - - if (gateway_is_active(device)) { - DBG("Refusing new connection since one already exists"); - goto drop; - } - - if (gateway_connect_rfcomm(device, chan) < 0) { - error("Allocating new GIOChannel failed!"); - goto drop; - } - - perr = audio_device_request_authorization(device, server_uuid, - gateway_auth_cb, device); - if (perr < 0) { - DBG("Authorization denied: %s", strerror(-perr)); - gateway_set_state(device, GATEWAY_STATE_DISCONNECTED); - } - - return; - -drop: - g_io_channel_shutdown(chan, TRUE, NULL); -} - -static int headset_server_init(struct audio_adapter *adapter) -{ - uint8_t chan = DEFAULT_HS_AG_CHANNEL; - sdp_record_t *record; - gboolean master = TRUE; - GError *err = NULL; - uint32_t features; - GIOChannel *io; - bdaddr_t src; - - if (config) { - gboolean tmp; - - tmp = g_key_file_get_boolean(config, "General", "Master", - &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else - master = tmp; - } - - adapter_get_address(adapter->btd_adapter, &src); - - io = bt_io_listen(BT_IO_RFCOMM, NULL, ag_confirm, adapter, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_CHANNEL, chan, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_MASTER, master, - BT_IO_OPT_INVALID); - if (!io) - goto failed; - - adapter->hsp_ag_server = io; - - record = hsp_ag_record(chan); - if (!record) { - error("Unable to allocate new service record"); - goto failed; - } - - if (add_record_to_server(&src, record) < 0) { - error("Unable to register HS AG service record"); - sdp_record_free(record); - goto failed; - } - adapter->hsp_ag_record_id = record->handle; - - features = headset_config_init(config); - - if (!enabled.hfp) - return 0; - - chan = DEFAULT_HF_AG_CHANNEL; - - io = bt_io_listen(BT_IO_RFCOMM, NULL, ag_confirm, adapter, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_CHANNEL, chan, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_MASTER, master, - BT_IO_OPT_INVALID); - if (!io) - goto failed; - - adapter->hfp_ag_server = io; - - record = hfp_ag_record(chan, features); - if (!record) { - error("Unable to allocate new service record"); - goto failed; - } - - if (add_record_to_server(&src, record) < 0) { - error("Unable to register HF AG service record"); - sdp_record_free(record); - goto failed; - } - adapter->hfp_ag_record_id = record->handle; - - return 0; - -failed: - if (err) { - error("%s", err->message); - g_error_free(err); - } - - if (adapter->hsp_ag_server) { - g_io_channel_shutdown(adapter->hsp_ag_server, TRUE, NULL); - g_io_channel_unref(adapter->hsp_ag_server); - adapter->hsp_ag_server = NULL; - } - - if (adapter->hfp_ag_server) { - g_io_channel_shutdown(adapter->hfp_ag_server, TRUE, NULL); - g_io_channel_unref(adapter->hfp_ag_server); - adapter->hfp_ag_server = NULL; - } - - return -1; -} - -static int gateway_server_init(struct audio_adapter *adapter) -{ - uint8_t chan = DEFAULT_HFP_HS_CHANNEL; - sdp_record_t *record; - gboolean master = TRUE; - GError *err = NULL; - GIOChannel *io; - bdaddr_t src; - - if (config) { - gboolean tmp; - - tmp = g_key_file_get_boolean(config, "General", "Master", - &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else - master = tmp; - } - - adapter_get_address(adapter->btd_adapter, &src); - - io = bt_io_listen(BT_IO_RFCOMM, NULL, hf_io_cb, adapter, NULL, &err, - BT_IO_OPT_SOURCE_BDADDR, &src, - BT_IO_OPT_CHANNEL, chan, - BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, - BT_IO_OPT_MASTER, master, - BT_IO_OPT_INVALID); - if (!io) { - error("%s", err->message); - g_error_free(err); - return -1; - } - - adapter->hfp_hs_server = io; - record = hfp_hs_record(chan); - if (!record) { - error("Unable to allocate new service record"); - goto failed; - } - - if (add_record_to_server(&src, record) < 0) { - error("Unable to register HFP HS service record"); - sdp_record_free(record); - goto failed; - } - - adapter->hfp_hs_record_id = record->handle; - - return 0; - -failed: - g_io_channel_shutdown(adapter->hfp_hs_server, TRUE, NULL); - g_io_channel_unref(adapter->hfp_hs_server); - adapter->hfp_hs_server = NULL; - return -1; -} - -static int audio_probe(struct btd_device *device, GSList *uuids) -{ - struct btd_adapter *adapter = device_get_adapter(device); - bdaddr_t src, dst; - struct audio_device *audio_dev; - - adapter_get_address(adapter, &src); - device_get_address(device, &dst, NULL); - - audio_dev = manager_get_device(&src, &dst, TRUE); - if (!audio_dev) { - DBG("unable to get a device object"); - return -1; - } - - g_slist_foreach(uuids, (GFunc) handle_uuid, audio_dev); - - return 0; -} - -static void audio_remove(struct btd_device *device) -{ - struct audio_device *dev; - const char *path; - - path = device_get_path(device); - - dev = manager_find_device(path, NULL, NULL, NULL, FALSE); - if (!dev) - return; - - devices = g_slist_remove(devices, dev); - - audio_device_unregister(dev); - -} - -static struct audio_adapter *audio_adapter_ref(struct audio_adapter *adp) -{ - adp->ref++; - - DBG("%p: ref=%d", adp, adp->ref); - - return adp; -} - -static void audio_adapter_unref(struct audio_adapter *adp) -{ - adp->ref--; - - DBG("%p: ref=%d", adp, adp->ref); - - if (adp->ref > 0) - return; - - adapters = g_slist_remove(adapters, adp); - btd_adapter_unref(adp->btd_adapter); - g_free(adp); -} - -static struct audio_adapter *audio_adapter_create(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - - adp = g_new0(struct audio_adapter, 1); - adp->btd_adapter = btd_adapter_ref(adapter); - - return audio_adapter_ref(adp); -} - -static struct audio_adapter *audio_adapter_get(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - - adp = find_adapter(adapters, adapter); - if (!adp) { - adp = audio_adapter_create(adapter); - adapters = g_slist_append(adapters, adp); - } else - audio_adapter_ref(adp); - - return adp; -} - -static void state_changed(struct btd_adapter *adapter, gboolean powered) -{ - struct audio_adapter *adp; - static gboolean telephony = FALSE; - GSList *l; - - DBG("%s powered %s", adapter_get_path(adapter), - powered ? "on" : "off"); - - /* ignore powered change, adapter is powering down */ - if (powered && adapter_powering_down(adapter)) - return; - - adp = find_adapter(adapters, adapter); - if (!adp) - return; - - adp->powered = powered; - - if (powered) { - /* telephony driver already initialized*/ - if (telephony == TRUE) - return; - telephony_init(); - telephony = TRUE; - return; - } - - /* telephony not initialized just ignore power down */ - if (telephony == FALSE) - return; - - for (l = adapters; l; l = l->next) { - adp = l->data; - - if (adp->powered == TRUE) - return; - } - - telephony_exit(); - telephony = FALSE; -} - -static int headset_server_probe(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - int err; - - DBG("path %s", path); - - adp = audio_adapter_get(adapter); - if (!adp) - return -EINVAL; - - err = headset_server_init(adp); - if (err < 0) { - audio_adapter_unref(adp); - return err; - } - - btd_adapter_register_powered_callback(adapter, state_changed); - - return 0; -} - -static void headset_server_remove(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - - DBG("path %s", path); - - btd_adapter_unregister_powered_callback(adapter, state_changed); - - adp = find_adapter(adapters, adapter); - if (!adp) - return; - - if (adp->hsp_ag_record_id) { - remove_record_from_server(adp->hsp_ag_record_id); - adp->hsp_ag_record_id = 0; - } - - if (adp->hsp_ag_server) { - g_io_channel_shutdown(adp->hsp_ag_server, TRUE, NULL); - g_io_channel_unref(adp->hsp_ag_server); - adp->hsp_ag_server = NULL; - } - - if (adp->hfp_ag_record_id) { - remove_record_from_server(adp->hfp_ag_record_id); - adp->hfp_ag_record_id = 0; - } - - if (adp->hfp_ag_server) { - g_io_channel_shutdown(adp->hfp_ag_server, TRUE, NULL); - g_io_channel_unref(adp->hfp_ag_server); - adp->hfp_ag_server = NULL; - } - - audio_adapter_unref(adp); -} - -static int gateway_server_probe(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - int err; - - adp = audio_adapter_get(adapter); - if (!adp) - return -EINVAL; - - err = gateway_server_init(adp); - if (err < 0) - audio_adapter_unref(adp); - - return err; -} - -static void gateway_server_remove(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - - DBG("path %s", path); - - adp = find_adapter(adapters, adapter); - if (!adp) - return; - - if (adp->hfp_hs_record_id) { - remove_record_from_server(adp->hfp_hs_record_id); - adp->hfp_hs_record_id = 0; - } - - if (adp->hfp_hs_server) { - g_io_channel_shutdown(adp->hfp_hs_server, TRUE, NULL); - g_io_channel_unref(adp->hfp_hs_server); - adp->hfp_hs_server = NULL; - } - - audio_adapter_unref(adp); -} - -static int a2dp_server_probe(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - bdaddr_t src; - int err; - - DBG("path %s", path); - - adp = audio_adapter_get(adapter); - if (!adp) - return -EINVAL; - - adapter_get_address(adapter, &src); - - err = a2dp_register(connection, &src, config); - if (err < 0) - audio_adapter_unref(adp); - - return err; -} - -static void a2dp_server_remove(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - bdaddr_t src; - - DBG("path %s", path); - - adp = find_adapter(adapters, adapter); - if (!adp) - return; - - adapter_get_address(adapter, &src); - a2dp_unregister(&src); - audio_adapter_unref(adp); -} - -static int avrcp_server_probe(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - bdaddr_t src; - int err; - - DBG("path %s", path); - - adp = audio_adapter_get(adapter); - if (!adp) - return -EINVAL; - - adapter_get_address(adapter, &src); - - err = avrcp_register(connection, &src, config); - if (err < 0) - audio_adapter_unref(adp); - - return err; -} - -static void avrcp_server_remove(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - bdaddr_t src; - - DBG("path %s", path); - - adp = find_adapter(adapters, adapter); - if (!adp) - return; - - adapter_get_address(adapter, &src); - avrcp_unregister(&src); - audio_adapter_unref(adp); -} - -static int media_server_probe(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - bdaddr_t src; - int err; - - DBG("path %s", path); - - adp = audio_adapter_get(adapter); - if (!adp) - return -EINVAL; - - adapter_get_address(adapter, &src); - - err = media_register(connection, path, &src); - if (err < 0) - audio_adapter_unref(adp); - - return err; -} - -static void media_server_remove(struct btd_adapter *adapter) -{ - struct audio_adapter *adp; - const gchar *path = adapter_get_path(adapter); - - DBG("path %s", path); - - adp = find_adapter(adapters, adapter); - if (!adp) - return; - - media_unregister(path); - audio_adapter_unref(adp); -} - -static struct btd_device_driver audio_driver = { - .name = "audio", - .uuids = BTD_UUIDS(HSP_HS_UUID, HFP_HS_UUID, HSP_AG_UUID, HFP_AG_UUID, - ADVANCED_AUDIO_UUID, A2DP_SOURCE_UUID, A2DP_SINK_UUID, - AVRCP_TARGET_UUID, AVRCP_REMOTE_UUID), - .probe = audio_probe, - .remove = audio_remove, -}; - -static struct btd_adapter_driver headset_server_driver = { - .name = "audio-headset", - .probe = headset_server_probe, - .remove = headset_server_remove, -}; - -static struct btd_adapter_driver gateway_server_driver = { - .name = "audio-gateway", - .probe = gateway_server_probe, - .remove = gateway_server_remove, -}; - -static struct btd_adapter_driver a2dp_server_driver = { - .name = "audio-a2dp", - .probe = a2dp_server_probe, - .remove = a2dp_server_remove, -}; - -static struct btd_adapter_driver avrcp_server_driver = { - .name = "audio-control", - .probe = avrcp_server_probe, - .remove = avrcp_server_remove, -}; - -static struct btd_adapter_driver media_server_driver = { - .name = "media", - .probe = media_server_probe, - .remove = media_server_remove, -}; - -int audio_manager_init(DBusConnection *conn, GKeyFile *conf, - gboolean *enable_sco) -{ - char **list; - int i; - gboolean b; - GError *err = NULL; - - connection = dbus_connection_ref(conn); - - if (!conf) - goto proceed; - - config = conf; - - list = g_key_file_get_string_list(config, "General", "Enable", - NULL, NULL); - for (i = 0; list && list[i] != NULL; i++) { - if (g_str_equal(list[i], "Headset")) - enabled.headset = TRUE; - else if (g_str_equal(list[i], "Gateway")) - enabled.gateway = TRUE; - else if (g_str_equal(list[i], "Sink")) - enabled.sink = TRUE; - else if (g_str_equal(list[i], "Source")) - enabled.source = TRUE; - else if (g_str_equal(list[i], "Control")) - enabled.control = TRUE; - else if (g_str_equal(list[i], "Socket")) - enabled.socket = TRUE; - else if (g_str_equal(list[i], "Media")) - enabled.media = TRUE; - - } - g_strfreev(list); - - list = g_key_file_get_string_list(config, "General", "Disable", - NULL, NULL); - for (i = 0; list && list[i] != NULL; i++) { - if (g_str_equal(list[i], "Headset")) - enabled.headset = FALSE; - else if (g_str_equal(list[i], "Gateway")) - enabled.gateway = FALSE; - else if (g_str_equal(list[i], "Sink")) - enabled.sink = FALSE; - else if (g_str_equal(list[i], "Source")) - enabled.source = FALSE; - else if (g_str_equal(list[i], "Control")) - enabled.control = FALSE; - else if (g_str_equal(list[i], "Socket")) - enabled.socket = FALSE; - else if (g_str_equal(list[i], "Media")) - enabled.media = FALSE; - } - g_strfreev(list); - - b = g_key_file_get_boolean(config, "General", "AutoConnect", &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else - auto_connect = b; - - b = g_key_file_get_boolean(config, "Headset", "HFP", - &err); - if (err) - g_clear_error(&err); - else - enabled.hfp = b; - - err = NULL; - i = g_key_file_get_integer(config, "Headset", "MaxConnected", - &err); - if (err) { - DBG("audio.conf: %s", err->message); - g_clear_error(&err); - } else - max_connected_headsets = i; - -proceed: - if (enabled.socket) - unix_init(); - - if (enabled.media) - btd_register_adapter_driver(&media_server_driver); - - if (enabled.headset) - btd_register_adapter_driver(&headset_server_driver); - - if (enabled.gateway) - btd_register_adapter_driver(&gateway_server_driver); - - if (enabled.source || enabled.sink) - btd_register_adapter_driver(&a2dp_server_driver); - - if (enabled.control) - btd_register_adapter_driver(&avrcp_server_driver); - - btd_register_device_driver(&audio_driver); - - *enable_sco = (enabled.gateway || enabled.headset); - - return 0; -} - -void audio_manager_exit(void) -{ - /* Bail out early if we haven't been initialized */ - if (connection == NULL) - return; - - dbus_connection_unref(connection); - connection = NULL; - - if (config) { - g_key_file_free(config); - config = NULL; - } - - if (enabled.socket) - unix_exit(); - - if (enabled.media) - btd_unregister_adapter_driver(&media_server_driver); - - if (enabled.headset) - btd_unregister_adapter_driver(&headset_server_driver); - - if (enabled.gateway) - btd_unregister_adapter_driver(&gateway_server_driver); - - if (enabled.source || enabled.sink) - btd_unregister_adapter_driver(&a2dp_server_driver); - - if (enabled.control) - btd_unregister_adapter_driver(&avrcp_server_driver); - - btd_unregister_device_driver(&audio_driver); -} - -GSList *manager_find_devices(const char *path, - const bdaddr_t *src, - const bdaddr_t *dst, - const char *interface, - gboolean connected) -{ - GSList *result = NULL; - GSList *l; - - for (l = devices; l != NULL; l = l->next) { - struct audio_device *dev = l->data; - - if ((path && (strcmp(path, "")) && strcmp(dev->path, path))) - continue; - - if ((src && bacmp(src, BDADDR_ANY)) && bacmp(&dev->src, src)) - continue; - - if ((dst && bacmp(dst, BDADDR_ANY)) && bacmp(&dev->dst, dst)) - continue; - - if (interface && !strcmp(AUDIO_HEADSET_INTERFACE, interface) - && !dev->headset) - continue; - - if (interface && !strcmp(AUDIO_GATEWAY_INTERFACE, interface) - && !dev->gateway) - continue; - - if (interface && !strcmp(AUDIO_SINK_INTERFACE, interface) - && !dev->sink) - continue; - - if (interface && !strcmp(AUDIO_SOURCE_INTERFACE, interface) - && !dev->source) - continue; - - if (interface && !strcmp(AUDIO_CONTROL_INTERFACE, interface) - && !dev->control) - continue; - - if (connected && !audio_device_is_active(dev, interface)) - continue; - - result = g_slist_append(result, dev); - } - - return result; -} - -struct audio_device *manager_find_device(const char *path, - const bdaddr_t *src, - const bdaddr_t *dst, - const char *interface, - gboolean connected) -{ - struct audio_device *result; - GSList *l; - - l = manager_find_devices(path, src, dst, interface, connected); - if (l == NULL) - return NULL; - - result = l->data; - g_slist_free(l); - return result; -} - -struct audio_device *manager_get_device(const bdaddr_t *src, - const bdaddr_t *dst, - gboolean create) -{ - struct audio_device *dev; - struct btd_adapter *adapter; - struct btd_device *device; - char addr[18]; - const char *path; - - dev = manager_find_device(NULL, src, dst, NULL, FALSE); - if (dev) - return dev; - - if (!create) - return NULL; - - ba2str(src, addr); - - adapter = manager_find_adapter(src); - if (!adapter) { - error("Unable to get a btd_adapter object for %s", - addr); - return NULL; - } - - ba2str(dst, addr); - - device = adapter_get_device(connection, adapter, addr); - if (!device) { - error("Unable to get btd_device object for %s", addr); - return NULL; - } - - path = device_get_path(device); - - dev = audio_device_register(connection, device, path, src, dst); - if (!dev) - return NULL; - - devices = g_slist_append(devices, dev); - - return dev; -} - -gboolean manager_allow_headset_connection(struct audio_device *device) -{ - GSList *l; - int connected = 0; - - for (l = devices; l != NULL; l = l->next) { - struct audio_device *dev = l->data; - struct headset *hs = dev->headset; - - if (dev == device) - continue; - - if (device && bacmp(&dev->src, &device->src) != 0) - continue; - - if (!hs) - continue; - - if (headset_get_state(dev) > HEADSET_STATE_DISCONNECTED) - connected++; - - if (connected >= max_connected_headsets) - return FALSE; - } - - return TRUE; -} - -void manager_set_fast_connectable(gboolean enable) -{ - GSList *l; - - if (enable && !manager_allow_headset_connection(NULL)) { - DBG("Refusing enabling fast connectable"); - return; - } - - for (l = adapters; l != NULL; l = l->next) { - struct audio_adapter *adapter = l->data; - - if (btd_adapter_set_fast_connectable(adapter->btd_adapter, - enable)) - error("Changing fast connectable for hci%d failed", - adapter_get_dev_id(adapter->btd_adapter)); - } -} diff -Nru bluez-4.101/audio/manager.h bluez-5.23/audio/manager.h --- bluez-4.101/audio/manager.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/manager.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -struct enabled_interfaces { - gboolean hfp; - gboolean headset; - gboolean gateway; - gboolean sink; - gboolean source; - gboolean control; - gboolean socket; - gboolean media; - gboolean media_player; -}; - -int audio_manager_init(DBusConnection *conn, GKeyFile *config, - gboolean *enable_sco); -void audio_manager_exit(void); - -gboolean server_is_enabled(bdaddr_t *src, uint16_t svc); - -struct audio_device *manager_find_device(const char *path, - const bdaddr_t *src, - const bdaddr_t *dst, - const char *interface, - gboolean connected); - -GSList *manager_find_devices(const char *path, - const bdaddr_t *src, - const bdaddr_t *dst, - const char *interface, - gboolean connected); - -struct audio_device *manager_get_device(const bdaddr_t *src, - const bdaddr_t *dst, - gboolean create); - -gboolean manager_allow_headset_connection(struct audio_device *device); - -/* TRUE to enable fast connectable and FALSE to disable fast connectable for all - * audio adapters. */ -void manager_set_fast_connectable(gboolean enable); diff -Nru bluez-4.101/audio/media.c bluez-5.23/audio/media.c --- bluez-4.101/audio/media.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/media.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1904 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2007 Nokia Corporation - * Copyright (C) 2004-2009 Marcel Holtmann - * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved. - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -#include -#include - -#include "../src/adapter.h" -#include "../src/dbus-common.h" - -#include "glib-helper.h" -#include "log.h" -#include "error.h" -#include "device.h" -#include "avdtp.h" -#include "media.h" -#include "transport.h" -#include "a2dp.h" -#include "avrcp.h" -#include "headset.h" -#include "gateway.h" -#include "manager.h" - -#define MEDIA_INTERFACE "org.bluez.Media" -#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint" -#define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer" - -#define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */ - -struct media_adapter { - bdaddr_t src; /* Adapter address */ - char *path; /* Adapter path */ - DBusConnection *conn; /* Adapter connection */ - GSList *endpoints; /* Endpoints list */ - GSList *players; /* Players list */ -}; - -struct endpoint_request { - struct media_endpoint *endpoint; - DBusMessage *msg; - DBusPendingCall *call; - media_endpoint_cb_t cb; - GDestroyNotify destroy; - void *user_data; -}; - -struct media_endpoint { - struct a2dp_sep *sep; - char *sender; /* Endpoint DBus bus id */ - char *path; /* Endpoint object path */ - char *uuid; /* Endpoint property UUID */ - uint8_t codec; /* Endpoint codec */ - uint8_t *capabilities; /* Endpoint property capabilities */ - size_t size; /* Endpoint capabilities size */ - guint hs_watch; - guint ag_watch; - guint watch; - GSList *requests; - struct media_adapter *adapter; - GSList *transports; -}; - -struct media_player { - struct media_adapter *adapter; - struct avrcp_player *player; - char *sender; /* Player DBus bus id */ - char *path; /* Player object path */ - GHashTable *settings; /* Player settings */ - GHashTable *track; /* Player current track */ - guint watch; - guint property_watch; - guint track_watch; - uint8_t status; - uint32_t position; - uint8_t volume; - GTimer *timer; -}; - -struct metadata_value { - int type; - union { - char *str; - uint32_t num; - } value; -}; - -static GSList *adapters = NULL; - -static void endpoint_request_free(struct endpoint_request *request) -{ - if (request->call) - dbus_pending_call_unref(request->call); - - if (request->destroy) - request->destroy(request->user_data); - - dbus_message_unref(request->msg); - g_free(request); -} - -static void media_endpoint_cancel(struct endpoint_request *request) -{ - struct media_endpoint *endpoint = request->endpoint; - - if (request->call) - dbus_pending_call_cancel(request->call); - - endpoint->requests = g_slist_remove(endpoint->requests, request); - - endpoint_request_free(request); -} - -static void media_endpoint_cancel_all(struct media_endpoint *endpoint) -{ - while (endpoint->requests != NULL) - media_endpoint_cancel(endpoint->requests->data); -} - -static void media_endpoint_destroy(struct media_endpoint *endpoint) -{ - struct media_adapter *adapter = endpoint->adapter; - - DBG("sender=%s path=%s", endpoint->sender, endpoint->path); - - if (endpoint->hs_watch) - headset_remove_state_cb(endpoint->hs_watch); - - if (endpoint->ag_watch) - gateway_remove_state_cb(endpoint->ag_watch); - - media_endpoint_cancel_all(endpoint); - - g_slist_free_full(endpoint->transports, - (GDestroyNotify) media_transport_destroy); - - g_dbus_remove_watch(adapter->conn, endpoint->watch); - g_free(endpoint->capabilities); - g_free(endpoint->sender); - g_free(endpoint->path); - g_free(endpoint->uuid); - g_free(endpoint); -} - -static void media_endpoint_remove(struct media_endpoint *endpoint) -{ - struct media_adapter *adapter = endpoint->adapter; - - if (endpoint->sep) { - a2dp_remove_sep(endpoint->sep); - return; - } - - info("Endpoint unregistered: sender=%s path=%s", endpoint->sender, - endpoint->path); - - adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint); - - media_endpoint_destroy(endpoint); -} - -static void media_endpoint_exit(DBusConnection *connection, void *user_data) -{ - struct media_endpoint *endpoint = user_data; - - endpoint->watch = 0; - media_endpoint_remove(endpoint); -} - -static void headset_setconf_cb(struct media_endpoint *endpoint, void *ret, - int size, void *user_data) -{ - struct audio_device *dev = user_data; - - if (ret != NULL) - return; - - headset_shutdown(dev); -} - -static void clear_configuration(struct media_endpoint *endpoint, - struct media_transport *transport) -{ - DBusConnection *conn; - DBusMessage *msg; - const char *path; - - conn = endpoint->adapter->conn; - - msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, - MEDIA_ENDPOINT_INTERFACE, - "ClearConfiguration"); - if (msg == NULL) { - error("Couldn't allocate D-Bus message"); - goto done; - } - - path = media_transport_get_path(transport); - dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); - g_dbus_send_message(conn, msg); -done: - endpoint->transports = g_slist_remove(endpoint->transports, transport); - media_transport_destroy(transport); -} - -static void clear_endpoint(struct media_endpoint *endpoint) -{ - media_endpoint_cancel_all(endpoint); - - while (endpoint->transports != NULL) - clear_configuration(endpoint, endpoint->transports->data); -} - -static void endpoint_reply(DBusPendingCall *call, void *user_data) -{ - struct endpoint_request *request = user_data; - struct media_endpoint *endpoint = request->endpoint; - DBusMessage *reply; - DBusError err; - gboolean value; - void *ret = NULL; - int size = -1; - - /* steal_reply will always return non-NULL since the callback - * is only called after a reply has been received */ - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("Endpoint replied with an error: %s", - err.name); - - /* Clear endpoint configuration in case of NO_REPLY error */ - if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) { - if (request->cb) - request->cb(endpoint, NULL, size, - request->user_data); - clear_endpoint(endpoint); - dbus_message_unref(reply); - dbus_error_free(&err); - return; - } - - dbus_error_free(&err); - goto done; - } - - if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE, - "SelectConfiguration")) { - DBusMessageIter args, array; - uint8_t *configuration; - - dbus_message_iter_init(reply, &args); - - dbus_message_iter_recurse(&args, &array); - - dbus_message_iter_get_fixed_array(&array, &configuration, &size); - - ret = configuration; - goto done; - } else if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) { - error("Wrong reply signature: %s", err.message); - dbus_error_free(&err); - goto done; - } - - size = 1; - value = TRUE; - ret = &value; - -done: - dbus_message_unref(reply); - - if (request->cb) - request->cb(endpoint, ret, size, request->user_data); - - endpoint->requests = g_slist_remove(endpoint->requests, request); - endpoint_request_free(request); -} - -static gboolean media_endpoint_async_call(DBusConnection *conn, - DBusMessage *msg, - struct media_endpoint *endpoint, - media_endpoint_cb_t cb, - void *user_data, - GDestroyNotify destroy) -{ - struct endpoint_request *request; - - request = g_new0(struct endpoint_request, 1); - - /* Timeout should be less than avdtp request timeout (4 seconds) */ - if (dbus_connection_send_with_reply(conn, msg, &request->call, - REQUEST_TIMEOUT) == FALSE) { - error("D-Bus send failed"); - g_free(request); - return FALSE; - } - - dbus_pending_call_set_notify(request->call, endpoint_reply, request, - NULL); - - request->endpoint = endpoint; - request->msg = msg; - request->cb = cb; - request->destroy = destroy; - request->user_data = user_data; - - endpoint->requests = g_slist_append(endpoint->requests, request); - - DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg), - dbus_message_get_destination(msg), - dbus_message_get_path(msg)); - - return TRUE; -} - -static gboolean select_configuration(struct media_endpoint *endpoint, - uint8_t *capabilities, - size_t length, - media_endpoint_cb_t cb, - void *user_data, - GDestroyNotify destroy) -{ - DBusConnection *conn; - DBusMessage *msg; - - conn = endpoint->adapter->conn; - - msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, - MEDIA_ENDPOINT_INTERFACE, - "SelectConfiguration"); - if (msg == NULL) { - error("Couldn't allocate D-Bus message"); - return FALSE; - } - - dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, - &capabilities, length, - DBUS_TYPE_INVALID); - - return media_endpoint_async_call(conn, msg, endpoint, cb, user_data, - destroy); -} - -static gint transport_device_cmp(gconstpointer data, gconstpointer user_data) -{ - struct media_transport *transport = (struct media_transport *) data; - const struct audio_device *device = user_data; - - if (device == media_transport_get_dev(transport)) - return 0; - - return -1; -} - -static struct media_transport *find_device_transport( - struct media_endpoint *endpoint, - struct audio_device *device) -{ - GSList *match; - - match = g_slist_find_custom(endpoint->transports, device, - transport_device_cmp); - if (match == NULL) - return NULL; - - return match->data; -} - -static gboolean set_configuration(struct media_endpoint *endpoint, - struct audio_device *device, - uint8_t *configuration, size_t size, - media_endpoint_cb_t cb, - void *user_data, - GDestroyNotify destroy) -{ - DBusConnection *conn; - DBusMessage *msg; - const char *path; - DBusMessageIter iter; - struct media_transport *transport; - - transport = find_device_transport(endpoint, device); - - if (transport != NULL) - return FALSE; - - conn = endpoint->adapter->conn; - - transport = media_transport_create(conn, endpoint, device, - configuration, size); - if (transport == NULL) - return FALSE; - - msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, - MEDIA_ENDPOINT_INTERFACE, - "SetConfiguration"); - if (msg == NULL) { - error("Couldn't allocate D-Bus message"); - media_transport_destroy(transport); - return FALSE; - } - - endpoint->transports = g_slist_append(endpoint->transports, transport); - - dbus_message_iter_init_append(msg, &iter); - - path = media_transport_get_path(transport); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path); - - transport_get_properties(transport, &iter); - - return media_endpoint_async_call(conn, msg, endpoint, cb, user_data, - destroy); -} - -static void release_endpoint(struct media_endpoint *endpoint) -{ - DBusMessage *msg; - - DBG("sender=%s path=%s", endpoint->sender, endpoint->path); - - /* already exit */ - if (endpoint->watch == 0) - goto done; - - msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, - MEDIA_ENDPOINT_INTERFACE, - "Release"); - if (msg == NULL) { - error("Couldn't allocate D-Bus message"); - return; - } - - g_dbus_send_message(endpoint->adapter->conn, msg); - -done: - media_endpoint_remove(endpoint); -} - -static void headset_state_changed(struct audio_device *dev, - headset_state_t old_state, - headset_state_t new_state, - void *user_data) -{ - struct media_endpoint *endpoint = user_data; - struct media_transport *transport; - - DBG(""); - - if (bacmp(&endpoint->adapter->src, &dev->src) != 0) - return; - - switch (new_state) { - case HEADSET_STATE_DISCONNECTED: - transport = find_device_transport(endpoint, dev); - - if (transport != NULL) { - DBG("Clear endpoint %p", endpoint); - clear_configuration(endpoint, transport); - } - break; - case HEADSET_STATE_CONNECTING: - set_configuration(endpoint, dev, NULL, 0, headset_setconf_cb, - dev, NULL); - break; - case HEADSET_STATE_CONNECTED: - break; - case HEADSET_STATE_PLAY_IN_PROGRESS: - break; - case HEADSET_STATE_PLAYING: - break; - } -} - -static const char *get_name(struct a2dp_sep *sep, void *user_data) -{ - struct media_endpoint *endpoint = user_data; - - return endpoint->sender; -} - -static size_t get_capabilities(struct a2dp_sep *sep, uint8_t **capabilities, - void *user_data) -{ - struct media_endpoint *endpoint = user_data; - - *capabilities = endpoint->capabilities; - return endpoint->size; -} - -struct a2dp_config_data { - struct a2dp_setup *setup; - a2dp_endpoint_config_t cb; -}; - -struct a2dp_select_data { - struct a2dp_setup *setup; - a2dp_endpoint_select_t cb; -}; - -static void select_cb(struct media_endpoint *endpoint, void *ret, int size, - void *user_data) -{ - struct a2dp_select_data *data = user_data; - - data->cb(data->setup, ret, size); -} - -static int select_config(struct a2dp_sep *sep, uint8_t *capabilities, - size_t length, struct a2dp_setup *setup, - a2dp_endpoint_select_t cb, void *user_data) -{ - struct media_endpoint *endpoint = user_data; - struct a2dp_select_data *data; - - data = g_new0(struct a2dp_select_data, 1); - data->setup = setup; - data->cb = cb; - - if (select_configuration(endpoint, capabilities, length, - select_cb, data, g_free) == TRUE) - return 0; - - g_free(data); - return -ENOMEM; -} - -static void config_cb(struct media_endpoint *endpoint, void *ret, int size, - void *user_data) -{ - struct a2dp_config_data *data = user_data; - - data->cb(data->setup, ret ? TRUE : FALSE); -} - -static int set_config(struct a2dp_sep *sep, struct audio_device *dev, - uint8_t *configuration, size_t length, - struct a2dp_setup *setup, - a2dp_endpoint_config_t cb, - void *user_data) -{ - struct media_endpoint *endpoint = user_data; - struct a2dp_config_data *data; - - data = g_new0(struct a2dp_config_data, 1); - data->setup = setup; - data->cb = cb; - - if (set_configuration(endpoint, dev, configuration, length, - config_cb, data, g_free) == TRUE) - return 0; - - g_free(data); - return -ENOMEM; -} - -static void clear_config(struct a2dp_sep *sep, void *user_data) -{ - struct media_endpoint *endpoint = user_data; - - clear_endpoint(endpoint); -} - -static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data) -{ - struct media_endpoint *endpoint = user_data; - - if (endpoint->transports == NULL) - return; - - media_transport_update_delay(endpoint->transports->data, delay); -} - -static struct a2dp_endpoint a2dp_endpoint = { - .get_name = get_name, - .get_capabilities = get_capabilities, - .select_configuration = select_config, - .set_configuration = set_config, - .clear_configuration = clear_config, - .set_delay = set_delay -}; - -static void a2dp_destroy_endpoint(void *user_data) -{ - struct media_endpoint *endpoint = user_data; - - clear_endpoint(endpoint); - - endpoint->sep = NULL; - release_endpoint(endpoint); -} - -static void gateway_setconf_cb(struct media_endpoint *endpoint, void *ret, - int size, void *user_data) -{ - struct audio_device *dev = user_data; - - if (ret != NULL) - return; - - gateway_set_state(dev, GATEWAY_STATE_DISCONNECTED); -} - -static void gateway_state_changed(struct audio_device *dev, - gateway_state_t old_state, - gateway_state_t new_state, - void *user_data) -{ - struct media_endpoint *endpoint = user_data; - struct media_transport *transport; - - DBG(""); - - if (bacmp(&endpoint->adapter->src, &dev->src) != 0) - return; - - switch (new_state) { - case GATEWAY_STATE_DISCONNECTED: - transport = find_device_transport(endpoint, dev); - if (transport != NULL) { - DBG("Clear endpoint %p", endpoint); - clear_configuration(endpoint, transport); - } - break; - case GATEWAY_STATE_CONNECTING: - set_configuration(endpoint, dev, NULL, 0, - gateway_setconf_cb, dev, NULL); - break; - case GATEWAY_STATE_CONNECTED: - break; - case GATEWAY_STATE_PLAYING: - break; - } -} - -static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint, - gboolean delay_reporting, - int *err) -{ - endpoint->sep = a2dp_add_sep(&endpoint->adapter->src, - AVDTP_SEP_TYPE_SOURCE, endpoint->codec, - delay_reporting, &a2dp_endpoint, - endpoint, a2dp_destroy_endpoint, err); - if (endpoint->sep == NULL) - return FALSE; - - return TRUE; -} - -static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint, - gboolean delay_reporting, - int *err) -{ - endpoint->sep = a2dp_add_sep(&endpoint->adapter->src, - AVDTP_SEP_TYPE_SINK, endpoint->codec, - delay_reporting, &a2dp_endpoint, - endpoint, a2dp_destroy_endpoint, err); - if (endpoint->sep == NULL) - return FALSE; - - return TRUE; -} - -static gboolean endpoint_init_ag(struct media_endpoint *endpoint, int *err) -{ - GSList *list; - GSList *l; - - endpoint->hs_watch = headset_add_state_cb(headset_state_changed, - endpoint); - list = manager_find_devices(NULL, &endpoint->adapter->src, BDADDR_ANY, - AUDIO_HEADSET_INTERFACE, TRUE); - - for (l = list; l != NULL; l = l->next) { - struct audio_device *dev = l->data; - - set_configuration(endpoint, dev, NULL, 0, - headset_setconf_cb, dev, NULL); - } - - g_slist_free(list); - - return TRUE; -} - -static gboolean endpoint_init_hs(struct media_endpoint *endpoint, int *err) -{ - GSList *list; - GSList *l; - - endpoint->ag_watch = gateway_add_state_cb(gateway_state_changed, - endpoint); - list = manager_find_devices(NULL, &endpoint->adapter->src, BDADDR_ANY, - AUDIO_GATEWAY_INTERFACE, TRUE); - - for (l = list; l != NULL; l = l->next) { - struct audio_device *dev = l->data; - - set_configuration(endpoint, dev, NULL, 0, - gateway_setconf_cb, dev, NULL); - } - - g_slist_free(list); - - return TRUE; -} - -static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter, - const char *sender, - const char *path, - const char *uuid, - gboolean delay_reporting, - uint8_t codec, - uint8_t *capabilities, - int size, - int *err) -{ - struct media_endpoint *endpoint; - gboolean succeeded; - - endpoint = g_new0(struct media_endpoint, 1); - endpoint->sender = g_strdup(sender); - endpoint->path = g_strdup(path); - endpoint->uuid = g_strdup(uuid); - endpoint->codec = codec; - - if (size > 0) { - endpoint->capabilities = g_new(uint8_t, size); - memcpy(endpoint->capabilities, capabilities, size); - endpoint->size = size; - } - - endpoint->adapter = adapter; - - if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) - succeeded = endpoint_init_a2dp_source(endpoint, - delay_reporting, err); - else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) - succeeded = endpoint_init_a2dp_sink(endpoint, - delay_reporting, err); - else if (strcasecmp(uuid, HFP_AG_UUID) == 0 || - strcasecmp(uuid, HSP_AG_UUID) == 0) - succeeded = endpoint_init_ag(endpoint, err); - else if (strcasecmp(uuid, HFP_HS_UUID) == 0 || - strcasecmp(uuid, HSP_HS_UUID) == 0) - succeeded = endpoint_init_hs(endpoint, err); - else { - succeeded = FALSE; - - if (err) - *err = -EINVAL; - } - - if (!succeeded) { - g_free(endpoint); - return NULL; - } - - endpoint->watch = g_dbus_add_disconnect_watch(adapter->conn, sender, - media_endpoint_exit, endpoint, - NULL); - - adapter->endpoints = g_slist_append(adapter->endpoints, endpoint); - info("Endpoint registered: sender=%s path=%s", sender, path); - - if (err) - *err = 0; - return endpoint; -} - -static struct media_endpoint *media_adapter_find_endpoint( - struct media_adapter *adapter, - const char *sender, - const char *path, - const char *uuid) -{ - GSList *l; - - for (l = adapter->endpoints; l; l = l->next) { - struct media_endpoint *endpoint = l->data; - - if (sender && g_strcmp0(endpoint->sender, sender) != 0) - continue; - - if (path && g_strcmp0(endpoint->path, path) != 0) - continue; - - if (uuid && g_strcmp0(endpoint->uuid, uuid) != 0) - continue; - - return endpoint; - } - - return NULL; -} - -static int parse_properties(DBusMessageIter *props, const char **uuid, - gboolean *delay_reporting, uint8_t *codec, - uint8_t **capabilities, int *size) -{ - gboolean has_uuid = FALSE; - gboolean has_codec = FALSE; - - while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) { - const char *key; - DBusMessageIter value, entry; - int var; - - dbus_message_iter_recurse(props, &entry); - dbus_message_iter_get_basic(&entry, &key); - - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); - - var = dbus_message_iter_get_arg_type(&value); - if (strcasecmp(key, "UUID") == 0) { - if (var != DBUS_TYPE_STRING) - return -EINVAL; - dbus_message_iter_get_basic(&value, uuid); - has_uuid = TRUE; - } else if (strcasecmp(key, "Codec") == 0) { - if (var != DBUS_TYPE_BYTE) - return -EINVAL; - dbus_message_iter_get_basic(&value, codec); - has_codec = TRUE; - } else if (strcasecmp(key, "DelayReporting") == 0) { - if (var != DBUS_TYPE_BOOLEAN) - return -EINVAL; - dbus_message_iter_get_basic(&value, delay_reporting); - } else if (strcasecmp(key, "Capabilities") == 0) { - DBusMessageIter array; - - if (var != DBUS_TYPE_ARRAY) - return -EINVAL; - - dbus_message_iter_recurse(&value, &array); - dbus_message_iter_get_fixed_array(&array, capabilities, - size); - } - - dbus_message_iter_next(props); - } - - return (has_uuid && has_codec) ? 0 : -EINVAL; -} - -static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct media_adapter *adapter = data; - DBusMessageIter args, props; - const char *sender, *path, *uuid; - gboolean delay_reporting = FALSE; - uint8_t codec; - uint8_t *capabilities; - int size = 0; - int err; - - sender = dbus_message_get_sender(msg); - - dbus_message_iter_init(msg, &args); - - dbus_message_iter_get_basic(&args, &path); - dbus_message_iter_next(&args); - - if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL) - return btd_error_already_exists(msg); - - dbus_message_iter_recurse(&args, &props); - if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) - return btd_error_invalid_args(msg); - - if (parse_properties(&props, &uuid, &delay_reporting, &codec, - &capabilities, &size) < 0) - return btd_error_invalid_args(msg); - - if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting, - codec, capabilities, size, &err) == NULL) { - if (err == -EPROTONOSUPPORT) - return btd_error_not_supported(msg); - else - return btd_error_invalid_args(msg); - } - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct media_adapter *adapter = data; - struct media_endpoint *endpoint; - const char *sender, *path; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return NULL; - - sender = dbus_message_get_sender(msg); - - endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL); - if (endpoint == NULL) - return btd_error_does_not_exist(msg); - - media_endpoint_remove(endpoint); - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static struct media_player *media_adapter_find_player( - struct media_adapter *adapter, - const char *sender, - const char *path) -{ - GSList *l; - - for (l = adapter->players; l; l = l->next) { - struct media_player *mp = l->data; - - if (sender && g_strcmp0(mp->sender, sender) != 0) - continue; - - if (path && g_strcmp0(mp->path, path) != 0) - continue; - - return mp; - } - - return NULL; -} - -static void release_player(struct media_player *mp) -{ - DBusMessage *msg; - - DBG("sender=%s path=%s", mp->sender, mp->path); - - msg = dbus_message_new_method_call(mp->sender, mp->path, - MEDIA_PLAYER_INTERFACE, - "Release"); - if (msg == NULL) { - error("Couldn't allocate D-Bus message"); - return; - } - - g_dbus_send_message(mp->adapter->conn, msg); -} - -static void media_player_free(gpointer data) -{ - struct media_player *mp = data; - struct media_adapter *adapter = mp->adapter; - - if (mp->player) { - adapter->players = g_slist_remove(adapter->players, mp); - release_player(mp); - } - - g_dbus_remove_watch(adapter->conn, mp->watch); - g_dbus_remove_watch(adapter->conn, mp->property_watch); - g_dbus_remove_watch(adapter->conn, mp->track_watch); - - if (mp->track) - g_hash_table_unref(mp->track); - - if (mp->settings) - g_hash_table_unref(mp->settings); - - g_timer_destroy(mp->timer); - g_free(mp->sender); - g_free(mp->path); - g_free(mp); -} - -static void media_player_destroy(struct media_player *mp) -{ - struct media_adapter *adapter = mp->adapter; - - DBG("sender=%s path=%s", mp->sender, mp->path); - - if (mp->player) { - struct avrcp_player *player = mp->player; - mp->player = NULL; - adapter->players = g_slist_remove(adapter->players, mp); - avrcp_unregister_player(player); - return; - } - - media_player_free(mp); -} - -static void media_player_remove(struct media_player *mp) -{ - info("Player unregistered: sender=%s path=%s", mp->sender, mp->path); - - media_player_destroy(mp); -} - -static const char *attrval_to_str(uint8_t attr, uint8_t value) -{ - switch (attr) { - case AVRCP_ATTRIBUTE_EQUALIZER: - switch (value) { - case AVRCP_EQUALIZER_ON: - return "on"; - case AVRCP_EQUALIZER_OFF: - return "off"; - } - - break; - case AVRCP_ATTRIBUTE_REPEAT_MODE: - switch (value) { - case AVRCP_REPEAT_MODE_OFF: - return "off"; - case AVRCP_REPEAT_MODE_SINGLE: - return "singletrack"; - case AVRCP_REPEAT_MODE_ALL: - return "alltracks"; - case AVRCP_REPEAT_MODE_GROUP: - return "group"; - } - - break; - /* Shuffle and scan have the same values */ - case AVRCP_ATTRIBUTE_SHUFFLE: - case AVRCP_ATTRIBUTE_SCAN: - switch (value) { - case AVRCP_SCAN_OFF: - return "off"; - case AVRCP_SCAN_ALL: - return "alltracks"; - case AVRCP_SCAN_GROUP: - return "group"; - } - - break; - } - - return NULL; -} - -static int attrval_to_val(uint8_t attr, const char *value) -{ - int ret; - - switch (attr) { - case AVRCP_ATTRIBUTE_EQUALIZER: - if (!strcmp(value, "off")) - ret = AVRCP_EQUALIZER_OFF; - else if (!strcmp(value, "on")) - ret = AVRCP_EQUALIZER_ON; - else - ret = -EINVAL; - - return ret; - case AVRCP_ATTRIBUTE_REPEAT_MODE: - if (!strcmp(value, "off")) - ret = AVRCP_REPEAT_MODE_OFF; - else if (!strcmp(value, "singletrack")) - ret = AVRCP_REPEAT_MODE_SINGLE; - else if (!strcmp(value, "alltracks")) - ret = AVRCP_REPEAT_MODE_ALL; - else if (!strcmp(value, "group")) - ret = AVRCP_REPEAT_MODE_GROUP; - else - ret = -EINVAL; - - return ret; - case AVRCP_ATTRIBUTE_SHUFFLE: - if (!strcmp(value, "off")) - ret = AVRCP_SHUFFLE_OFF; - else if (!strcmp(value, "alltracks")) - ret = AVRCP_SHUFFLE_ALL; - else if (!strcmp(value, "group")) - ret = AVRCP_SHUFFLE_GROUP; - else - ret = -EINVAL; - - return ret; - case AVRCP_ATTRIBUTE_SCAN: - if (!strcmp(value, "off")) - ret = AVRCP_SCAN_OFF; - else if (!strcmp(value, "alltracks")) - ret = AVRCP_SCAN_ALL; - else if (!strcmp(value, "group")) - ret = AVRCP_SCAN_GROUP; - else - ret = -EINVAL; - - return ret; - } - - return -EINVAL; -} - -static const char *attr_to_str(uint8_t attr) -{ - switch (attr) { - case AVRCP_ATTRIBUTE_EQUALIZER: - return "Equalizer"; - case AVRCP_ATTRIBUTE_REPEAT_MODE: - return "Repeat"; - case AVRCP_ATTRIBUTE_SHUFFLE: - return "Shuffle"; - case AVRCP_ATTRIBUTE_SCAN: - return "Scan"; - } - - return NULL; -} - -static int attr_to_val(const char *str) -{ - if (!strcasecmp(str, "Equalizer")) - return AVRCP_ATTRIBUTE_EQUALIZER; - else if (!strcasecmp(str, "Repeat")) - return AVRCP_ATTRIBUTE_REPEAT_MODE; - else if (!strcasecmp(str, "Shuffle")) - return AVRCP_ATTRIBUTE_SHUFFLE; - else if (!strcasecmp(str, "Scan")) - return AVRCP_ATTRIBUTE_SCAN; - - return -EINVAL; -} - -static int play_status_to_val(const char *status) -{ - if (!strcasecmp(status, "stopped")) - return AVRCP_PLAY_STATUS_STOPPED; - else if (!strcasecmp(status, "playing")) - return AVRCP_PLAY_STATUS_PLAYING; - else if (!strcasecmp(status, "paused")) - return AVRCP_PLAY_STATUS_PAUSED; - else if (!strcasecmp(status, "forward-seek")) - return AVRCP_PLAY_STATUS_FWD_SEEK; - else if (!strcasecmp(status, "reverse-seek")) - return AVRCP_PLAY_STATUS_REV_SEEK; - else if (!strcasecmp(status, "error")) - return AVRCP_PLAY_STATUS_ERROR; - - return -EINVAL; -} - -static int metadata_to_val(const char *str) -{ - if (!strcasecmp(str, "Title")) - return AVRCP_MEDIA_ATTRIBUTE_TITLE; - else if (!strcasecmp(str, "Artist")) - return AVRCP_MEDIA_ATTRIBUTE_ARTIST; - else if (!strcasecmp(str, "Album")) - return AVRCP_MEDIA_ATTRIBUTE_ALBUM; - else if (!strcasecmp(str, "Genre")) - return AVRCP_MEDIA_ATTRIBUTE_GENRE; - else if (!strcasecmp(str, "NumberOfTracks")) - return AVRCP_MEDIA_ATTRIBUTE_N_TRACKS; - else if (!strcasecmp(str, "Number")) - return AVRCP_MEDIA_ATTRIBUTE_TRACK; - else if (!strcasecmp(str, "Duration")) - return AVRCP_MEDIA_ATTRIBUTE_DURATION; - - return -EINVAL; -} - -static const char *metadata_to_str(uint32_t id) -{ - switch (id) { - case AVRCP_MEDIA_ATTRIBUTE_TITLE: - return "Title"; - case AVRCP_MEDIA_ATTRIBUTE_ARTIST: - return "Artist"; - case AVRCP_MEDIA_ATTRIBUTE_ALBUM: - return "Album"; - case AVRCP_MEDIA_ATTRIBUTE_GENRE: - return "Genre"; - case AVRCP_MEDIA_ATTRIBUTE_TRACK: - return "Track"; - case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS: - return "NumberOfTracks"; - case AVRCP_MEDIA_ATTRIBUTE_DURATION: - return "Duration"; - } - - return NULL; -} - -static int get_setting(uint8_t attr, void *user_data) -{ - struct media_player *mp = user_data; - guint attr_uint = attr; - void *value; - - DBG("%s", attr_to_str(attr)); - - value = g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint)); - if (!value) - return -EINVAL; - - return GPOINTER_TO_UINT(value); -} - -static int set_setting(uint8_t attr, uint8_t val, void *user_data) -{ - struct media_player *mp = user_data; - struct media_adapter *adapter = mp->adapter; - const char *property, *value; - guint attr_uint = attr; - DBusMessage *msg; - DBusMessageIter iter, var; - - property = attr_to_str(attr); - value = attrval_to_str(attr, val); - - DBG("%s = %s", property, value); - - if (property == NULL || value == NULL) - return -EINVAL; - - if (!g_hash_table_lookup(mp->settings, GUINT_TO_POINTER(attr_uint))) - return -EINVAL; - - msg = dbus_message_new_method_call(mp->sender, mp->path, - MEDIA_PLAYER_INTERFACE, - "SetProperty"); - if (msg == NULL) { - error("Couldn't allocate D-Bus message"); - return -ENOMEM; - } - - dbus_message_iter_init_append(msg, &iter); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &property); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, - DBUS_TYPE_STRING_AS_STRING, - &var); - dbus_message_iter_append_basic(&var, DBUS_TYPE_STRING, &value); - dbus_message_iter_close_container(&iter, &var); - - g_dbus_send_message(adapter->conn, msg); - - return 0; -} - -static GList *list_metadata(void *user_data) -{ - struct media_player *mp = user_data; - - DBG(""); - - if (mp->track == NULL) - return NULL; - - return g_hash_table_get_keys(mp->track); -} - -static uint64_t get_uid(void *user_data) -{ - struct media_player *mp = user_data; - - DBG("%p", mp->track); - - if (mp->track == NULL) - return UINT64_MAX; - - return 0; -} - -static void *get_metadata(uint32_t id, void *user_data) -{ - struct media_player *mp = user_data; - struct metadata_value *value; - - DBG("%s", metadata_to_str(id)); - - if (mp->track == NULL) - return NULL; - - value = g_hash_table_lookup(mp->track, GUINT_TO_POINTER(id)); - if (!value) - return NULL; - - switch (value->type) { - case DBUS_TYPE_STRING: - return value->value.str; - case DBUS_TYPE_UINT32: - return GUINT_TO_POINTER(value->value.num); - } - - return NULL; -} - -static uint8_t get_status(void *user_data) -{ - struct media_player *mp = user_data; - - return mp->status; -} - -static uint32_t get_position(void *user_data) -{ - struct media_player *mp = user_data; - double timedelta; - uint32_t sec, msec; - - if (mp->status != AVRCP_PLAY_STATUS_PLAYING) - return mp->position; - - timedelta = g_timer_elapsed(mp->timer, NULL); - - sec = (uint32_t) timedelta; - msec = (uint32_t) ((timedelta - sec) * 1000); - - return mp->position + sec * 1000 + msec; -} - -static void set_volume(uint8_t volume, struct audio_device *dev, void *user_data) -{ - struct media_player *mp = user_data; - GSList *l; - - if (mp->volume == volume) - return; - - mp->volume = volume; - - for (l = mp->adapter->endpoints; l; l = l->next) { - struct media_endpoint *endpoint = l->data; - struct media_transport *transport; - - /* Volume is A2DP only */ - if (endpoint->sep == NULL) - continue; - - transport = find_device_transport(endpoint, dev); - if (transport == NULL) - continue; - - media_transport_update_volume(transport, volume); - } -} - -static struct avrcp_player_cb player_cb = { - .get_setting = get_setting, - .set_setting = set_setting, - .list_metadata = list_metadata, - .get_uid = get_uid, - .get_metadata = get_metadata, - .get_position = get_position, - .get_status = get_status, - .set_volume = set_volume -}; - -static void media_player_exit(DBusConnection *connection, void *user_data) -{ - struct media_player *mp = user_data; - - mp->watch = 0; - media_player_remove(mp); -} - -static gboolean set_status(struct media_player *mp, DBusMessageIter *iter) -{ - const char *value; - int val; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) - return FALSE; - - dbus_message_iter_get_basic(iter, &value); - DBG("Status=%s", value); - - val = play_status_to_val(value); - if (val < 0) { - error("Invalid status"); - return FALSE; - } - - if (mp->status == val) - return TRUE; - - mp->position = get_position(mp); - g_timer_start(mp->timer); - - mp->status = val; - - avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, &val); - - return TRUE; -} - -static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) -{ - uint32_t value; - struct metadata_value *duration; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) - return FALSE; - - dbus_message_iter_get_basic(iter, &value); - DBG("Position=%u", value); - - mp->position = value; - g_timer_start(mp->timer); - - if (!mp->position) { - avrcp_player_event(mp->player, - AVRCP_EVENT_TRACK_REACHED_START, NULL); - return TRUE; - } - - duration = g_hash_table_lookup(mp->track, GUINT_TO_POINTER( - AVRCP_MEDIA_ATTRIBUTE_DURATION)); - - /* - * If position is the maximum value allowed or greater than track's - * duration, we send a track-reached-end event. - */ - if (mp->position == UINT32_MAX || - (duration && mp->position >= duration->value.num)) - avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END, - NULL); - - return TRUE; -} - -static gboolean set_property(struct media_player *mp, const char *key, - DBusMessageIter *entry) -{ - DBusMessageIter var; - const char *value; - int attr, val; - - if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT) - return FALSE; - - dbus_message_iter_recurse(entry, &var); - - if (strcasecmp(key, "Status") == 0) - return set_status(mp, &var); - - if (strcasecmp(key, "Position") == 0) - return set_position(mp, &var); - - attr = attr_to_val(key); - if (attr < 0) - return FALSE; - - if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) - return FALSE; - - dbus_message_iter_get_basic(&var, &value); - - val = attrval_to_val(attr, value); - if (val < 0) - return FALSE; - - DBG("%s=%s", key, value); - - g_hash_table_replace(mp->settings, GUINT_TO_POINTER(attr), - GUINT_TO_POINTER(val)); - - return TRUE; -} - -static gboolean property_changed(DBusConnection *connection, DBusMessage *msg, - void *user_data) -{ - struct media_player *mp = user_data; - DBusMessageIter iter; - const char *property; - - DBG("sender=%s path=%s", mp->sender, mp->path); - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { - error("Unexpected signature in %s.%s signal", - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - return TRUE; - } - - dbus_message_iter_get_basic(&iter, &property); - - dbus_message_iter_next(&iter); - - set_property(mp, property, &iter); - - return TRUE; -} - -static void metadata_value_free(gpointer data) -{ - struct metadata_value *value = data; - - switch (value->type) { - case DBUS_TYPE_STRING: - g_free(value->value.str); - break; - } - - g_free(value); -} - -static gboolean parse_player_metadata(struct media_player *mp, - DBusMessageIter *iter) -{ - DBusMessageIter dict; - DBusMessageIter var; - GHashTable *track; - int ctype; - gboolean title = FALSE; - uint64_t uid; - - ctype = dbus_message_iter_get_arg_type(iter); - if (ctype != DBUS_TYPE_ARRAY) - return FALSE; - - dbus_message_iter_recurse(iter, &dict); - - track = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, - metadata_value_free); - - while ((ctype = dbus_message_iter_get_arg_type(&dict)) != - DBUS_TYPE_INVALID) { - DBusMessageIter entry; - const char *key; - struct metadata_value *value; - int id; - - if (ctype != DBUS_TYPE_DICT_ENTRY) - goto parse_error; - - dbus_message_iter_recurse(&dict, &entry); - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) - goto parse_error; - - dbus_message_iter_get_basic(&entry, &key); - dbus_message_iter_next(&entry); - - id = metadata_to_val(key); - if (id < 0) - goto parse_error; - - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) - goto parse_error; - - dbus_message_iter_recurse(&entry, &var); - - value = g_new0(struct metadata_value, 1); - value->type = dbus_message_iter_get_arg_type(&var); - - switch (id) { - case AVRCP_MEDIA_ATTRIBUTE_TITLE: - title = TRUE; - case AVRCP_MEDIA_ATTRIBUTE_ARTIST: - case AVRCP_MEDIA_ATTRIBUTE_ALBUM: - case AVRCP_MEDIA_ATTRIBUTE_GENRE: - if (value->type != DBUS_TYPE_STRING) { - g_free(value); - goto parse_error; - } - - dbus_message_iter_get_basic(&var, &value->value.str); - break; - case AVRCP_MEDIA_ATTRIBUTE_TRACK: - case AVRCP_MEDIA_ATTRIBUTE_N_TRACKS: - case AVRCP_MEDIA_ATTRIBUTE_DURATION: - if (value->type != DBUS_TYPE_UINT32) { - g_free(value); - goto parse_error; - } - - dbus_message_iter_get_basic(&var, &value->value.num); - break; - default: - goto parse_error; - } - - switch (value->type) { - case DBUS_TYPE_STRING: - value->value.str = g_strdup(value->value.str); - DBG("%s=%s", key, value->value.str); - break; - default: - DBG("%s=%u", key, value->value.num); - } - - g_hash_table_replace(track, GUINT_TO_POINTER(id), value); - dbus_message_iter_next(&dict); - } - - if (g_hash_table_size(track) == 0) { - g_hash_table_unref(track); - track = NULL; - } else if (title == FALSE) { - struct metadata_value *value = g_new(struct metadata_value, 1); - uint32_t id = AVRCP_MEDIA_ATTRIBUTE_TITLE; - - value->type = DBUS_TYPE_STRING; - value->value.str = g_strdup(""); - g_hash_table_insert(track, GUINT_TO_POINTER(id), value); - } - - if (mp->track != NULL) - g_hash_table_unref(mp->track); - - mp->track = track; - mp->position = 0; - g_timer_start(mp->timer); - uid = get_uid(mp); - - avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid); - avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, - NULL); - - return TRUE; - -parse_error: - if (track) - g_hash_table_unref(track); - - return FALSE; -} - -static gboolean track_changed(DBusConnection *connection, DBusMessage *msg, - void *user_data) -{ - struct media_player *mp = user_data; - DBusMessageIter iter; - - DBG("sender=%s path=%s", mp->sender, mp->path); - - dbus_message_iter_init(msg, &iter); - - if (parse_player_metadata(mp, &iter) == FALSE) { - error("Unexpected signature in %s.%s signal", - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - } - - return TRUE; -} - -static struct media_player *media_player_create(struct media_adapter *adapter, - const char *sender, - const char *path, - int *err) -{ - struct media_player *mp; - - mp = g_new0(struct media_player, 1); - mp->adapter = adapter; - mp->sender = g_strdup(sender); - mp->path = g_strdup(path); - mp->timer = g_timer_new(); - - mp->watch = g_dbus_add_disconnect_watch(adapter->conn, sender, - media_player_exit, mp, - NULL); - mp->property_watch = g_dbus_add_signal_watch(adapter->conn, sender, - path, MEDIA_PLAYER_INTERFACE, - "PropertyChanged", - property_changed, - mp, NULL); - mp->track_watch = g_dbus_add_signal_watch(adapter->conn, sender, - path, MEDIA_PLAYER_INTERFACE, - "TrackChanged", - track_changed, - mp, NULL); - mp->player = avrcp_register_player(&adapter->src, &player_cb, mp, - media_player_free); - if (!mp->player) { - if (err) - *err = -EPROTONOSUPPORT; - media_player_destroy(mp); - return NULL; - } - - mp->settings = g_hash_table_new(g_direct_hash, g_direct_equal); - - adapter->players = g_slist_append(adapter->players, mp); - - info("Player registered: sender=%s path=%s", sender, path); - - if (err) - *err = 0; - - return mp; -} - -static gboolean parse_player_properties(struct media_player *mp, - DBusMessageIter *iter) -{ - DBusMessageIter dict; - int ctype; - - ctype = dbus_message_iter_get_arg_type(iter); - if (ctype != DBUS_TYPE_ARRAY) - return FALSE; - - dbus_message_iter_recurse(iter, &dict); - - while ((ctype = dbus_message_iter_get_arg_type(&dict)) != - DBUS_TYPE_INVALID) { - DBusMessageIter entry; - const char *key; - - if (ctype != DBUS_TYPE_DICT_ENTRY) - return FALSE; - - dbus_message_iter_recurse(&dict, &entry); - if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) - return FALSE; - - dbus_message_iter_get_basic(&entry, &key); - dbus_message_iter_next(&entry); - - if (set_property(mp, key, &entry) == FALSE) - return FALSE; - - dbus_message_iter_next(&dict); - } - - return TRUE; -} - -static DBusMessage *register_player(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct media_adapter *adapter = data; - struct media_player *mp; - DBusMessageIter args; - const char *sender, *path; - int err; - - sender = dbus_message_get_sender(msg); - - dbus_message_iter_init(msg, &args); - - dbus_message_iter_get_basic(&args, &path); - dbus_message_iter_next(&args); - - if (media_adapter_find_player(adapter, sender, path) != NULL) - return btd_error_already_exists(msg); - - mp = media_player_create(adapter, sender, path, &err); - if (mp == NULL) { - if (err == -EPROTONOSUPPORT) - return btd_error_not_supported(msg); - else - return btd_error_invalid_args(msg); - } - - if (parse_player_properties(mp, &args) == FALSE) { - media_player_destroy(mp); - return btd_error_invalid_args(msg); - } - - dbus_message_iter_next(&args); - - if (parse_player_metadata(mp, &args) == FALSE) { - media_player_destroy(mp); - return btd_error_invalid_args(msg); - } - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static DBusMessage *unregister_player(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct media_adapter *adapter = data; - struct media_player *player; - const char *sender, *path; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) - return NULL; - - sender = dbus_message_get_sender(msg); - - player = media_adapter_find_player(adapter, sender, path); - if (player == NULL) - return btd_error_does_not_exist(msg); - - media_player_remove(player); - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static const GDBusMethodTable media_methods[] = { - { GDBUS_METHOD("RegisterEndpoint", - GDBUS_ARGS({ "endpoint", "o" }, { "properties", "a{sv}" }), - NULL, register_endpoint) }, - { GDBUS_METHOD("UnregisterEndpoint", - GDBUS_ARGS({ "endpoint", "o" }), NULL, unregister_endpoint) }, - { GDBUS_METHOD("RegisterPlayer", - GDBUS_ARGS({ "player", "o" }, { "properties", "a{sv}" }, - { "metadata", "a{sv}" }), - NULL, register_player) }, - { GDBUS_METHOD("UnregisterPlayer", - GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) }, - { }, -}; - -static void path_free(void *data) -{ - struct media_adapter *adapter = data; - - while (adapter->endpoints) - release_endpoint(adapter->endpoints->data); - - dbus_connection_unref(adapter->conn); - - adapters = g_slist_remove(adapters, adapter); - - g_free(adapter->path); - g_free(adapter); -} - -int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src) -{ - struct media_adapter *adapter; - - adapter = g_new0(struct media_adapter, 1); - adapter->conn = dbus_connection_ref(conn); - bacpy(&adapter->src, src); - adapter->path = g_strdup(path); - - if (!g_dbus_register_interface(conn, path, MEDIA_INTERFACE, - media_methods, NULL, NULL, - adapter, path_free)) { - error("D-Bus failed to register %s path", path); - path_free(adapter); - return -1; - } - - adapters = g_slist_append(adapters, adapter); - - return 0; -} - -void media_unregister(const char *path) -{ - GSList *l; - - for (l = adapters; l; l = l->next) { - struct media_adapter *adapter = l->data; - - if (g_strcmp0(path, adapter->path) == 0) { - g_dbus_unregister_interface(adapter->conn, path, - MEDIA_INTERFACE); - return; - } - } -} - -struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint) -{ - return endpoint->sep; -} - -const char *media_endpoint_get_uuid(struct media_endpoint *endpoint) -{ - return endpoint->uuid; -} - -uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint) -{ - return endpoint->codec; -} diff -Nru bluez-4.101/audio/media.h bluez-5.23/audio/media.h --- bluez-4.101/audio/media.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/media.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2007 Nokia Corporation - * Copyright (C) 2004-2009 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -struct media_endpoint; - -typedef void (*media_endpoint_cb_t) (struct media_endpoint *endpoint, - void *ret, int size, void *user_data); - -int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src); -void media_unregister(const char *path); - -struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint); -const char *media_endpoint_get_uuid(struct media_endpoint *endpoint); -uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint); -struct media_transport *media_endpoint_get_transport( - struct media_endpoint *endpoint); diff -Nru bluez-4.101/audio/pcm_bluetooth.c bluez-5.23/audio/pcm_bluetooth.c --- bluez-4.101/audio/pcm_bluetooth.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/pcm_bluetooth.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1785 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "ipc.h" -#include "sbc.h" -#include "rtp.h" - -/* #define ENABLE_DEBUG */ - -#define UINT_SECS_MAX (UINT_MAX / 1000000 - 1) - -#define MIN_PERIOD_TIME 1 - -#define BUFFER_SIZE 2048 - -#ifdef ENABLE_DEBUG -#define DBG(fmt, arg...) printf("DEBUG: %s: " fmt "\n" , __FUNCTION__ , ## arg) -#else -#define DBG(fmt, arg...) -#endif - -#ifndef SOL_SCO -#define SOL_SCO 17 -#endif - -#ifndef SCO_TXBUFS -#define SCO_TXBUFS 0x03 -#endif - -#ifndef SCO_RXBUFS -#define SCO_RXBUFS 0x04 -#endif - -#ifndef MIN -# define MIN(x, y) ((x) < (y) ? (x) : (y)) -#endif - -#ifndef MAX -# define MAX(x, y) ((x) > (y) ? (x) : (y)) -#endif - -#define MAX_BITPOOL 64 -#define MIN_BITPOOL 2 - -/* adapted from glibc sys/time.h timersub() macro */ -#define priv_timespecsub(a, b, result) \ - do { \ - (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ - if ((result)->tv_nsec < 0) { \ - --(result)->tv_sec; \ - (result)->tv_nsec += 1000000000; \ - } \ - } while (0) - -struct bluetooth_a2dp { - sbc_capabilities_t sbc_capabilities; - sbc_t sbc; /* Codec data */ - int sbc_initialized; /* Keep track if the encoder is initialized */ - unsigned int codesize; /* SBC codesize */ - int samples; /* Number of encoded samples */ - uint8_t buffer[BUFFER_SIZE]; /* Codec transfer buffer */ - unsigned int count; /* Codec transfer buffer counter */ - - int nsamples; /* Cumulative number of codec samples */ - uint16_t seq_num; /* Cumulative packet sequence */ - int frame_count; /* Current frames in buffer*/ -}; - -struct bluetooth_alsa_config { - char device[18]; /* Address of the remote Device */ - int has_device; - uint8_t transport; /* Requested transport */ - int has_transport; - uint16_t rate; - int has_rate; - uint8_t channel_mode; /* A2DP only */ - int has_channel_mode; - uint8_t allocation_method; /* A2DP only */ - int has_allocation_method; - uint8_t subbands; /* A2DP only */ - int has_subbands; - uint8_t block_length; /* A2DP only */ - int has_block_length; - uint8_t bitpool; /* A2DP only */ - int has_bitpool; - int autoconnect; -}; - -struct bluetooth_data { - snd_pcm_ioplug_t io; - struct bluetooth_alsa_config alsa_config; /* ALSA resource file parameters */ - volatile snd_pcm_sframes_t hw_ptr; - int transport; /* chosen transport SCO or AD2P */ - unsigned int link_mtu; /* MTU for selected transport channel */ - volatile struct pollfd stream; /* Audio stream filedescriptor */ - struct pollfd server; /* Audio daemon filedescriptor */ - uint8_t buffer[BUFFER_SIZE]; /* Encoded transfer buffer */ - unsigned int count; /* Transfer buffer counter */ - struct bluetooth_a2dp a2dp; /* A2DP data */ - - pthread_t hw_thread; /* Makes virtual hw pointer move */ - int pipefd[2]; /* Inter thread communication */ - int stopped; - sig_atomic_t reset; /* Request XRUN handling */ -}; - -static int audioservice_send(int sk, const bt_audio_msg_header_t *msg); -static int audioservice_expect(int sk, bt_audio_msg_header_t *outmsg, - int expected_type); - -static int bluetooth_start(snd_pcm_ioplug_t *io) -{ - DBG("bluetooth_start %p", io); - - return 0; -} - -static int bluetooth_stop(snd_pcm_ioplug_t *io) -{ - DBG("bluetooth_stop %p", io); - - return 0; -} - -static void *playback_hw_thread(void *param) -{ - struct bluetooth_data *data = param; - unsigned int prev_periods; - double period_time; - struct timespec start; - struct pollfd fds[2]; - int poll_timeout; - - data->server.events = POLLIN; - /* note: only errors for data->stream.events */ - - fds[0] = data->server; - fds[1] = data->stream; - - prev_periods = 0; - period_time = 1000000.0 * data->io.period_size / data->io.rate; - if (period_time > (int) (MIN_PERIOD_TIME * 1000)) - poll_timeout = (int) (period_time / 1000.0f); - else - poll_timeout = MIN_PERIOD_TIME; - - clock_gettime(CLOCK_MONOTONIC, &start); - - while (1) { - unsigned int dtime, periods; - struct timespec cur, delta; - int ret; - - if (data->stopped) - goto iter_sleep; - - if (data->reset) { - DBG("Handle XRUN in hw-thread."); - data->reset = 0; - clock_gettime(CLOCK_MONOTONIC, &start); - prev_periods = 0; - } - - clock_gettime(CLOCK_MONOTONIC, &cur); - - priv_timespecsub(&cur, &start, &delta); - - dtime = delta.tv_sec * 1000000 + delta.tv_nsec / 1000; - periods = 1.0 * dtime / period_time; - - if (periods > prev_periods) { - char c = 'w'; - int frags = periods - prev_periods, n; - - data->hw_ptr += frags * data->io.period_size; - data->hw_ptr %= data->io.buffer_size; - - for (n = 0; n < frags; n++) { - /* Notify user that hardware pointer - * has moved * */ - if (write(data->pipefd[1], &c, 1) < 0) - pthread_testcancel(); - } - - /* Reset point of reference to avoid too big values - * that wont fit an unsigned int */ - if ((unsigned int) delta.tv_sec < UINT_SECS_MAX) - prev_periods = periods; - else { - prev_periods = 0; - clock_gettime(CLOCK_MONOTONIC, &start); - } - } - -iter_sleep: - /* sleep up to one period interval */ - ret = poll(fds, 2, poll_timeout); - - if (ret < 0) { - if (errno != EINTR) { - SNDERR("poll error: %s (%d)", strerror(errno), - errno); - break; - } - } else if (ret > 0) { - ret = (fds[0].revents) ? 0 : 1; - SNDERR("poll fd %d revents %d", ret, fds[ret].revents); - if (fds[ret].revents & (POLLERR | POLLHUP | POLLNVAL)) - break; - } - - /* Offer opportunity to be canceled by main thread */ - pthread_testcancel(); - } - - data->hw_thread = 0; - pthread_exit(NULL); -} - -static int bluetooth_playback_start(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - int err; - - DBG("%p", io); - - data->stopped = 0; - - if (data->hw_thread) - return 0; - - err = pthread_create(&data->hw_thread, 0, playback_hw_thread, data); - - return -err; -} - -static int bluetooth_playback_stop(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - - DBG("%p", io); - - data->stopped = 1; - - return 0; -} - -static snd_pcm_sframes_t bluetooth_pointer(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - - return data->hw_ptr; -} - -static void bluetooth_exit(struct bluetooth_data *data) -{ - struct bluetooth_a2dp *a2dp = &data->a2dp; - - if (data->server.fd >= 0) - bt_audio_service_close(data->server.fd); - - if (data->stream.fd >= 0) - close(data->stream.fd); - - if (data->hw_thread) { - pthread_cancel(data->hw_thread); - pthread_join(data->hw_thread, 0); - } - - if (a2dp->sbc_initialized) - sbc_finish(&a2dp->sbc); - - if (data->pipefd[0] > 0) - close(data->pipefd[0]); - - if (data->pipefd[1] > 0) - close(data->pipefd[1]); - - free(data); -} - -static int bluetooth_close(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - - DBG("%p", io); - - bluetooth_exit(data); - - return 0; -} - -static int bluetooth_prepare(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - char c = 'w'; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_start_stream_req *req = (void *) buf; - struct bt_start_stream_rsp *rsp = (void *) buf; - struct bt_new_stream_ind *ind = (void *) buf; - uint32_t period_count = io->buffer_size / io->period_size; - int opt_name, err; - struct timeval t = { 0, period_count }; - - DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", - io->period_size, io->buffer_size); - - data->reset = 0; - - /* As we're gonna receive messages on the server socket, we have to stop the - hw thread that is polling on it, if any */ - if (data->hw_thread) { - pthread_cancel(data->hw_thread); - pthread_join(data->hw_thread, 0); - data->hw_thread = 0; - } - - if (io->stream == SND_PCM_STREAM_PLAYBACK) - /* If not null for playback, xmms doesn't display time - * correctly */ - data->hw_ptr = 0; - else - /* ALSA library is really picky on the fact hw_ptr is not null. - * If it is, capture won't start */ - data->hw_ptr = io->period_size; - - /* send start */ - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - req->h.type = BT_REQUEST; - req->h.name = BT_START_STREAM; - req->h.length = sizeof(*req); - - err = audioservice_send(data->server.fd, &req->h); - if (err < 0) - return err; - - rsp->h.length = sizeof(*rsp); - err = audioservice_expect(data->server.fd, &rsp->h, - BT_START_STREAM); - if (err < 0) - return err; - - ind->h.length = sizeof(*ind); - err = audioservice_expect(data->server.fd, &ind->h, - BT_NEW_STREAM); - if (err < 0) - return err; - - if (data->stream.fd >= 0) - close(data->stream.fd); - - data->stream.fd = bt_audio_service_get_data_fd(data->server.fd); - if (data->stream.fd < 0) { - return -errno; - } - - if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) { - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SO_SNDTIMEO : SO_RCVTIMEO; - - if (setsockopt(data->stream.fd, SOL_SOCKET, opt_name, &t, - sizeof(t)) < 0) - return -errno; - } else { - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SCO_TXBUFS : SCO_RXBUFS; - - if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, - sizeof(period_count)) == 0) - return 0; - - opt_name = (io->stream == SND_PCM_STREAM_PLAYBACK) ? - SO_SNDBUF : SO_RCVBUF; - - if (setsockopt(data->stream.fd, SOL_SCO, opt_name, &period_count, - sizeof(period_count)) == 0) - return 0; - - /* FIXME : handle error codes */ - } - - /* wake up any client polling at us */ - if (write(data->pipefd[1], &c, 1) < 0) { - err = -errno; - return err; - } - - return 0; -} - -static int bluetooth_hsp_hw_params(snd_pcm_ioplug_t *io, - snd_pcm_hw_params_t *params) -{ - struct bluetooth_data *data = io->private_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_open_req *open_req = (void *) buf; - struct bt_open_rsp *open_rsp = (void *) buf; - struct bt_set_configuration_req *req = (void *) buf; - struct bt_set_configuration_rsp *rsp = (void *) buf; - int err; - - DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", - io->period_size, io->buffer_size); - - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - open_req->h.type = BT_REQUEST; - open_req->h.name = BT_OPEN; - open_req->h.length = sizeof(*open_req); - - strncpy(open_req->destination, data->alsa_config.device, 18); - open_req->seid = BT_A2DP_SEID_RANGE + 1; - open_req->lock = (io->stream == SND_PCM_STREAM_PLAYBACK ? - BT_WRITE_LOCK : BT_READ_LOCK); - - err = audioservice_send(data->server.fd, &open_req->h); - if (err < 0) - return err; - - open_rsp->h.length = sizeof(*open_rsp); - err = audioservice_expect(data->server.fd, &open_rsp->h, - BT_OPEN); - if (err < 0) - return err; - - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - req->h.type = BT_REQUEST; - req->h.name = BT_SET_CONFIGURATION; - req->h.length = sizeof(*req); - - req->codec.transport = BT_CAPABILITIES_TRANSPORT_SCO; - req->codec.seid = BT_A2DP_SEID_RANGE + 1; - req->codec.length = sizeof(pcm_capabilities_t); - - req->h.length += req->codec.length - sizeof(req->codec); - err = audioservice_send(data->server.fd, &req->h); - if (err < 0) - return err; - - rsp->h.length = sizeof(*rsp); - err = audioservice_expect(data->server.fd, &rsp->h, - BT_SET_CONFIGURATION); - if (err < 0) - return err; - - data->transport = BT_CAPABILITIES_TRANSPORT_SCO; - data->link_mtu = rsp->link_mtu; - - return 0; -} - -static uint8_t default_bitpool(uint8_t freq, uint8_t mode) -{ - switch (freq) { - case BT_SBC_SAMPLING_FREQ_16000: - case BT_SBC_SAMPLING_FREQ_32000: - return 53; - case BT_SBC_SAMPLING_FREQ_44100: - switch (mode) { - case BT_A2DP_CHANNEL_MODE_MONO: - case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: - return 31; - case BT_A2DP_CHANNEL_MODE_STEREO: - case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: - return 53; - default: - DBG("Invalid channel mode %u", mode); - return 53; - } - case BT_SBC_SAMPLING_FREQ_48000: - switch (mode) { - case BT_A2DP_CHANNEL_MODE_MONO: - case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL: - return 29; - case BT_A2DP_CHANNEL_MODE_STEREO: - case BT_A2DP_CHANNEL_MODE_JOINT_STEREO: - return 51; - default: - DBG("Invalid channel mode %u", mode); - return 51; - } - default: - DBG("Invalid sampling freq %u", freq); - return 53; - } -} - -static int bluetooth_a2dp_init(struct bluetooth_data *data, - snd_pcm_hw_params_t *params) -{ - struct bluetooth_alsa_config *cfg = &data->alsa_config; - sbc_capabilities_t *cap = &data->a2dp.sbc_capabilities; - unsigned int max_bitpool, min_bitpool, rate, channels; - int dir; - - snd_pcm_hw_params_get_rate(params, &rate, &dir); - snd_pcm_hw_params_get_channels(params, &channels); - - switch (rate) { - case 48000: - cap->frequency = BT_SBC_SAMPLING_FREQ_48000; - break; - case 44100: - cap->frequency = BT_SBC_SAMPLING_FREQ_44100; - break; - case 32000: - cap->frequency = BT_SBC_SAMPLING_FREQ_32000; - break; - case 16000: - cap->frequency = BT_SBC_SAMPLING_FREQ_16000; - break; - default: - DBG("Rate %d not supported", rate); - return -1; - } - - if (cfg->has_channel_mode) - cap->channel_mode = cfg->channel_mode; - else if (channels == 2) { - if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) - cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) - cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; - else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) - cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - } else { - if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; - } - - if (!cap->channel_mode) { - DBG("No supported channel modes"); - return -1; - } - - if (cfg->has_block_length) - cap->block_length = cfg->block_length; - else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) - cap->block_length = BT_A2DP_BLOCK_LENGTH_16; - else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12) - cap->block_length = BT_A2DP_BLOCK_LENGTH_12; - else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8) - cap->block_length = BT_A2DP_BLOCK_LENGTH_8; - else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4) - cap->block_length = BT_A2DP_BLOCK_LENGTH_4; - else { - DBG("No supported block lengths"); - return -1; - } - - if (cfg->has_subbands) - cap->subbands = cfg->subbands; - if (cap->subbands & BT_A2DP_SUBBANDS_8) - cap->subbands = BT_A2DP_SUBBANDS_8; - else if (cap->subbands & BT_A2DP_SUBBANDS_4) - cap->subbands = BT_A2DP_SUBBANDS_4; - else { - DBG("No supported subbands"); - return -1; - } - - if (cfg->has_allocation_method) - cap->allocation_method = cfg->allocation_method; - if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) - cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; - else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR) - cap->allocation_method = BT_A2DP_ALLOCATION_SNR; - - if (cfg->has_bitpool) - min_bitpool = max_bitpool = cfg->bitpool; - else { - min_bitpool = MAX(MIN_BITPOOL, cap->min_bitpool); - max_bitpool = MIN(default_bitpool(cap->frequency, - cap->channel_mode), - cap->max_bitpool); - } - - cap->min_bitpool = min_bitpool; - cap->max_bitpool = max_bitpool; - - return 0; -} - -static void bluetooth_a2dp_setup(struct bluetooth_a2dp *a2dp) -{ - sbc_capabilities_t active_capabilities = a2dp->sbc_capabilities; - - if (a2dp->sbc_initialized) - sbc_reinit(&a2dp->sbc, 0); - else - sbc_init(&a2dp->sbc, 0); - a2dp->sbc_initialized = 1; - - if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_16000) - a2dp->sbc.frequency = SBC_FREQ_16000; - - if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_32000) - a2dp->sbc.frequency = SBC_FREQ_32000; - - if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_44100) - a2dp->sbc.frequency = SBC_FREQ_44100; - - if (active_capabilities.frequency & BT_SBC_SAMPLING_FREQ_48000) - a2dp->sbc.frequency = SBC_FREQ_48000; - - if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_MONO) - a2dp->sbc.mode = SBC_MODE_MONO; - - if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) - a2dp->sbc.mode = SBC_MODE_DUAL_CHANNEL; - - if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) - a2dp->sbc.mode = SBC_MODE_STEREO; - - if (active_capabilities.channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) - a2dp->sbc.mode = SBC_MODE_JOINT_STEREO; - - a2dp->sbc.allocation = active_capabilities.allocation_method - == BT_A2DP_ALLOCATION_SNR ? SBC_AM_SNR - : SBC_AM_LOUDNESS; - - switch (active_capabilities.subbands) { - case BT_A2DP_SUBBANDS_4: - a2dp->sbc.subbands = SBC_SB_4; - break; - case BT_A2DP_SUBBANDS_8: - a2dp->sbc.subbands = SBC_SB_8; - break; - } - - switch (active_capabilities.block_length) { - case BT_A2DP_BLOCK_LENGTH_4: - a2dp->sbc.blocks = SBC_BLK_4; - break; - case BT_A2DP_BLOCK_LENGTH_8: - a2dp->sbc.blocks = SBC_BLK_8; - break; - case BT_A2DP_BLOCK_LENGTH_12: - a2dp->sbc.blocks = SBC_BLK_12; - break; - case BT_A2DP_BLOCK_LENGTH_16: - a2dp->sbc.blocks = SBC_BLK_16; - break; - } - - a2dp->sbc.bitpool = active_capabilities.max_bitpool; - a2dp->codesize = sbc_get_codesize(&a2dp->sbc); - a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); -} - -static int bluetooth_a2dp_hw_params(snd_pcm_ioplug_t *io, - snd_pcm_hw_params_t *params) -{ - struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp = &data->a2dp; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_open_req *open_req = (void *) buf; - struct bt_open_rsp *open_rsp = (void *) buf; - struct bt_set_configuration_req *req = (void *) buf; - struct bt_set_configuration_rsp *rsp = (void *) buf; - int err; - - DBG("Preparing with io->period_size=%lu io->buffer_size=%lu", - io->period_size, io->buffer_size); - - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - open_req->h.type = BT_REQUEST; - open_req->h.name = BT_OPEN; - open_req->h.length = sizeof(*open_req); - - strncpy(open_req->destination, data->alsa_config.device, 18); - open_req->seid = a2dp->sbc_capabilities.capability.seid; - open_req->lock = (io->stream == SND_PCM_STREAM_PLAYBACK ? - BT_WRITE_LOCK : BT_READ_LOCK); - - err = audioservice_send(data->server.fd, &open_req->h); - if (err < 0) - return err; - - open_rsp->h.length = sizeof(*open_rsp); - err = audioservice_expect(data->server.fd, &open_rsp->h, - BT_OPEN); - if (err < 0) - return err; - - err = bluetooth_a2dp_init(data, params); - if (err < 0) - return err; - - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - req->h.type = BT_REQUEST; - req->h.name = BT_SET_CONFIGURATION; - req->h.length = sizeof(*req); - - memcpy(&req->codec, &a2dp->sbc_capabilities, - sizeof(a2dp->sbc_capabilities)); - - req->codec.transport = BT_CAPABILITIES_TRANSPORT_A2DP; - req->codec.length = sizeof(a2dp->sbc_capabilities); - req->h.length += req->codec.length - sizeof(req->codec); - - err = audioservice_send(data->server.fd, &req->h); - if (err < 0) - return err; - - rsp->h.length = sizeof(*rsp); - err = audioservice_expect(data->server.fd, &rsp->h, - BT_SET_CONFIGURATION); - if (err < 0) - return err; - - data->transport = BT_CAPABILITIES_TRANSPORT_A2DP; - data->link_mtu = rsp->link_mtu; - - /* Setup SBC encoder now we agree on parameters */ - bluetooth_a2dp_setup(a2dp); - - DBG("\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", - a2dp->sbc.allocation, a2dp->sbc.subbands, a2dp->sbc.blocks, - a2dp->sbc.bitpool); - - return 0; -} - -static int bluetooth_poll_descriptors(snd_pcm_ioplug_t *io, - struct pollfd *pfd, unsigned int space) -{ - struct bluetooth_data *data = io->private_data; - - assert(io); - - if (space < 1) - return 0; - - pfd[0].fd = data->stream.fd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - - return 1; -} - -static int bluetooth_poll_revents(snd_pcm_ioplug_t *io ATTRIBUTE_UNUSED, - struct pollfd *pfds, unsigned int nfds, - unsigned short *revents) -{ - assert(pfds && nfds == 1 && revents); - - *revents = pfds[0].revents; - - return 0; -} - -static int bluetooth_playback_poll_descriptors_count(snd_pcm_ioplug_t *io) -{ - return 2; -} - -static int bluetooth_playback_poll_descriptors(snd_pcm_ioplug_t *io, - struct pollfd *pfd, unsigned int space) -{ - struct bluetooth_data *data = io->private_data; - - DBG(""); - - assert(data->pipefd[0] >= 0); - - if (space < 2) - return 0; - - pfd[0].fd = data->pipefd[0]; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - pfd[1].fd = data->stream.fd; - pfd[1].events = POLLERR | POLLHUP | POLLNVAL; - pfd[1].revents = 0; - - return 2; -} - -static int bluetooth_playback_poll_revents(snd_pcm_ioplug_t *io, - struct pollfd *pfds, unsigned int nfds, - unsigned short *revents) -{ - static char buf[1]; - - DBG(""); - - assert(pfds); - assert(nfds == 2); - assert(revents); - assert(pfds[0].fd >= 0); - assert(pfds[1].fd >= 0); - - if (io->state != SND_PCM_STATE_PREPARED) - if (read(pfds[0].fd, buf, 1) < 0) - SYSERR("read error: %s (%d)", strerror(errno), errno); - - if (pfds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) - io->state = SND_PCM_STATE_DISCONNECTED; - - *revents = (pfds[0].revents & POLLIN) ? POLLOUT : 0; - - return 0; -} - - -static snd_pcm_sframes_t bluetooth_hsp_read(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) -{ - struct bluetooth_data *data = io->private_data; - snd_pcm_uframes_t frames_to_write, ret; - unsigned char *buff; - unsigned int frame_size = 0; - int nrecv; - - DBG("areas->step=%u areas->first=%u offset=%lu size=%lu io->nonblock=%u", - areas->step, areas->first, offset, size, io->nonblock); - - frame_size = areas->step / 8; - - if (data->count > 0) - goto proceed; - - nrecv = recv(data->stream.fd, data->buffer, data->link_mtu, - io->nonblock ? MSG_DONTWAIT : 0); - - if (nrecv < 0) { - ret = (errno == EPIPE) ? -EIO : -errno; - goto done; - } - - if ((unsigned int) nrecv != data->link_mtu) { - ret = -EIO; - SNDERR(strerror(-ret)); - goto done; - } - - /* Increment hardware transmition pointer */ - data->hw_ptr = (data->hw_ptr + data->link_mtu / frame_size) % - io->buffer_size; - -proceed: - buff = (unsigned char *) areas->addr + - (areas->first + areas->step * offset) / 8; - - if ((data->count + size * frame_size) <= data->link_mtu) - frames_to_write = size; - else - frames_to_write = (data->link_mtu - data->count) / frame_size; - - memcpy(buff, data->buffer + data->count, frame_size * frames_to_write); - data->count += (frame_size * frames_to_write); - data->count %= data->link_mtu; - - /* Return written frames count */ - ret = frames_to_write; - -done: - DBG("returning %lu", ret); - return ret; -} - -static snd_pcm_sframes_t bluetooth_hsp_write(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, - snd_pcm_uframes_t size) -{ - struct bluetooth_data *data = io->private_data; - snd_pcm_sframes_t ret = 0; - snd_pcm_uframes_t frames_to_read; - uint8_t *buff; - int rsend, frame_size; - - DBG("areas->step=%u areas->first=%u offset=%lu, size=%lu io->nonblock=%u", - areas->step, areas->first, offset, size, io->nonblock); - - if (io->hw_ptr > io->appl_ptr) { - ret = bluetooth_playback_stop(io); - if (ret == 0) - ret = -EPIPE; - goto done; - } - - frame_size = areas->step / 8; - if ((data->count + size * frame_size) <= data->link_mtu) - frames_to_read = size; - else - frames_to_read = (data->link_mtu - data->count) / frame_size; - - DBG("count=%d frames_to_read=%lu", data->count, frames_to_read); - - /* Ready for more data */ - buff = (uint8_t *) areas->addr + - (areas->first + areas->step * offset) / 8; - memcpy(data->buffer + data->count, buff, frame_size * frames_to_read); - - /* Remember we have some frames in the pipe now */ - data->count += frames_to_read * frame_size; - if (data->count != data->link_mtu) { - ret = frames_to_read; - goto done; - } - - rsend = send(data->stream.fd, data->buffer, data->link_mtu, - io->nonblock ? MSG_DONTWAIT : 0); - if (rsend > 0) { - /* Reset count pointer */ - data->count = 0; - - ret = frames_to_read; - } else if (rsend < 0) - ret = (errno == EPIPE) ? -EIO : -errno; - else - ret = -EIO; - -done: - DBG("returning %ld", ret); - return ret; -} - -static snd_pcm_sframes_t bluetooth_a2dp_read(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, snd_pcm_uframes_t size) -{ - snd_pcm_uframes_t ret = 0; - return ret; -} - -static int avdtp_write(struct bluetooth_data *data) -{ - int err; - struct rtp_header *header; - struct rtp_payload *payload; - struct bluetooth_a2dp *a2dp = &data->a2dp; - - header = (void *) a2dp->buffer; - payload = (void *) (a2dp->buffer + sizeof(*header)); - - memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload)); - - payload->frame_count = a2dp->frame_count; - header->v = 2; - header->pt = 1; - header->sequence_number = htons(a2dp->seq_num); - header->timestamp = htonl(a2dp->nsamples); - header->ssrc = htonl(1); - - err = send(data->stream.fd, a2dp->buffer, a2dp->count, MSG_DONTWAIT); - if (err < 0) { - err = -errno; - DBG("send failed: %s (%d)", strerror(-err), -err); - } - - /* Reset buffer of data to send */ - a2dp->count = sizeof(struct rtp_header) + sizeof(struct rtp_payload); - a2dp->frame_count = 0; - a2dp->samples = 0; - a2dp->seq_num++; - - return err; -} - -static snd_pcm_sframes_t bluetooth_a2dp_write(snd_pcm_ioplug_t *io, - const snd_pcm_channel_area_t *areas, - snd_pcm_uframes_t offset, snd_pcm_uframes_t size) -{ - struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp = &data->a2dp; - snd_pcm_sframes_t ret = 0; - unsigned int bytes_left; - int frame_size, encoded; - ssize_t written; - uint8_t *buff; - - DBG("areas->step=%u areas->first=%u offset=%lu size=%lu", - areas->step, areas->first, offset, size); - DBG("hw_ptr=%lu appl_ptr=%lu diff=%lu", io->hw_ptr, io->appl_ptr, - io->appl_ptr - io->hw_ptr); - - /* Calutate starting pointers */ - frame_size = areas->step / 8; - bytes_left = size * frame_size; - buff = (uint8_t *) areas->addr + - (areas->first + areas->step * (offset)) / 8; - - /* Check for underrun */ - if (io->hw_ptr > io->appl_ptr) { - ret = bluetooth_playback_stop(io); - if (ret == 0) - ret = -EPIPE; - data->reset = 1; - return ret; - } - - /* Check if we should autostart */ - if (io->state == SND_PCM_STATE_PREPARED) { - snd_pcm_sw_params_t *swparams; - snd_pcm_uframes_t threshold; - - snd_pcm_sw_params_malloc(&swparams); - if (!snd_pcm_sw_params_current(io->pcm, swparams) && - !snd_pcm_sw_params_get_start_threshold(swparams, - &threshold)) { - if (io->appl_ptr >= threshold) { - ret = snd_pcm_start(io->pcm); - if (ret != 0) - return ret; - } - } - - snd_pcm_sw_params_free(swparams); - } - - /* Check if we have any left over data from the last write */ - if (data->count > 0) { - unsigned int additional_bytes_needed = - a2dp->codesize - data->count; - if (additional_bytes_needed > bytes_left) - goto out; - - memcpy(data->buffer + data->count, buff, - additional_bytes_needed); - - /* Enough data to encode (sbc wants 1k blocks) */ - encoded = sbc_encode(&a2dp->sbc, data->buffer, a2dp->codesize, - a2dp->buffer + a2dp->count, - sizeof(a2dp->buffer) - a2dp->count, - &written); - if (encoded <= 0) { - DBG("Encoding error %d", encoded); - goto done; - } - - /* Increment a2dp buffers */ - a2dp->count += written; - a2dp->frame_count++; - a2dp->samples += encoded / frame_size; - a2dp->nsamples += encoded / frame_size; - - /* No space left for another frame then send */ - if (a2dp->count + written >= data->link_mtu) { - avdtp_write(data); - DBG("sending packet %d, count %d, link_mtu %u", - a2dp->seq_num, a2dp->count, - data->link_mtu); - } - - /* Increment up buff pointer to take into account - * the data processed */ - buff += additional_bytes_needed; - bytes_left -= additional_bytes_needed; - - /* Since data has been process mark it as zero */ - data->count = 0; - } - - - /* Process this buffer in full chunks */ - while (bytes_left >= a2dp->codesize) { - /* Enough data to encode (sbc wants 1k blocks) */ - encoded = sbc_encode(&a2dp->sbc, buff, a2dp->codesize, - a2dp->buffer + a2dp->count, - sizeof(a2dp->buffer) - a2dp->count, - &written); - if (encoded <= 0) { - DBG("Encoding error %d", encoded); - goto done; - } - - /* Increment up buff pointer to take into account - * the data processed */ - buff += a2dp->codesize; - bytes_left -= a2dp->codesize; - - /* Increment a2dp buffers */ - a2dp->count += written; - a2dp->frame_count++; - a2dp->samples += encoded / frame_size; - a2dp->nsamples += encoded / frame_size; - - /* No space left for another frame then send */ - if (a2dp->count + written >= data->link_mtu) { - avdtp_write(data); - DBG("sending packet %d, count %d, link_mtu %u", - a2dp->seq_num, a2dp->count, - data->link_mtu); - } - } - -out: - /* Copy the extra to our temp buffer for the next write */ - if (bytes_left > 0) { - memcpy(data->buffer + data->count, buff, bytes_left); - data->count += bytes_left; - bytes_left = 0; - } - -done: - DBG("returning %ld", size - bytes_left / frame_size); - - return size - bytes_left / frame_size; -} - -static int bluetooth_playback_delay(snd_pcm_ioplug_t *io, - snd_pcm_sframes_t *delayp) -{ - DBG(""); - - /* This updates io->hw_ptr value using pointer() function */ - snd_pcm_hwsync(io->pcm); - - *delayp = io->appl_ptr - io->hw_ptr; - if ((io->state == SND_PCM_STATE_RUNNING) && (*delayp < 0)) { - io->callback->stop(io); - io->state = SND_PCM_STATE_XRUN; - *delayp = 0; - } - - /* This should never fail, ALSA API is really not - prepared to handle a non zero return value */ - return 0; -} - -static snd_pcm_ioplug_callback_t bluetooth_hsp_playback = { - .start = bluetooth_playback_start, - .stop = bluetooth_playback_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_hsp_write, - .poll_descriptors_count = bluetooth_playback_poll_descriptors_count, - .poll_descriptors = bluetooth_playback_poll_descriptors, - .poll_revents = bluetooth_playback_poll_revents, - .delay = bluetooth_playback_delay, -}; - -static snd_pcm_ioplug_callback_t bluetooth_hsp_capture = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_hsp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_hsp_read, - .poll_descriptors = bluetooth_poll_descriptors, - .poll_revents = bluetooth_poll_revents, -}; - -static snd_pcm_ioplug_callback_t bluetooth_a2dp_playback = { - .start = bluetooth_playback_start, - .stop = bluetooth_playback_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_a2dp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_a2dp_write, - .poll_descriptors_count = bluetooth_playback_poll_descriptors_count, - .poll_descriptors = bluetooth_playback_poll_descriptors, - .poll_revents = bluetooth_playback_poll_revents, - .delay = bluetooth_playback_delay, -}; - -static snd_pcm_ioplug_callback_t bluetooth_a2dp_capture = { - .start = bluetooth_start, - .stop = bluetooth_stop, - .pointer = bluetooth_pointer, - .close = bluetooth_close, - .hw_params = bluetooth_a2dp_hw_params, - .prepare = bluetooth_prepare, - .transfer = bluetooth_a2dp_read, - .poll_descriptors = bluetooth_poll_descriptors, - .poll_revents = bluetooth_poll_revents, -}; - -#define ARRAY_NELEMS(a) (sizeof((a)) / sizeof((a)[0])) - -static int bluetooth_hsp_hw_constraint(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - snd_pcm_access_t access_list[] = { - SND_PCM_ACCESS_RW_INTERLEAVED, - /* Mmap access is really useless fo this driver, but we - * support it because some pieces of software out there - * insist on using it */ - SND_PCM_ACCESS_MMAP_INTERLEAVED - }; - unsigned int format_list[] = { - SND_PCM_FORMAT_S16 - }; - int err; - - /* access type */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, - ARRAY_NELEMS(access_list), access_list); - if (err < 0) - return err; - - /* supported formats */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, - ARRAY_NELEMS(format_list), format_list); - if (err < 0) - return err; - - /* supported channels */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - 1, 1); - if (err < 0) - return err; - - /* supported rate */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE, - 8000, 8000); - if (err < 0) - return err; - - /* supported block size */ - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - data->link_mtu, data->link_mtu); - if (err < 0) - return err; - - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS, - 2, 200); - if (err < 0) - return err; - - return 0; -} - -static int bluetooth_a2dp_hw_constraint(snd_pcm_ioplug_t *io) -{ - struct bluetooth_data *data = io->private_data; - struct bluetooth_a2dp *a2dp = &data->a2dp; - struct bluetooth_alsa_config *cfg = &data->alsa_config; - snd_pcm_access_t access_list[] = { - SND_PCM_ACCESS_RW_INTERLEAVED, - /* Mmap access is really useless fo this driver, but we - * support it because some pieces of software out there - * insist on using it */ - SND_PCM_ACCESS_MMAP_INTERLEAVED - }; - unsigned int format_list[] = { - SND_PCM_FORMAT_S16 - }; - unsigned int rate_list[4]; - unsigned int rate_count; - int err, min_channels, max_channels; - unsigned int period_list[] = { - 2048, - 4096, /* e.g. 23.2msec/period (stereo 16bit at 44.1kHz) */ - 8192 - }; - - /* access type */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS, - ARRAY_NELEMS(access_list), access_list); - if (err < 0) - return err; - - /* supported formats */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT, - ARRAY_NELEMS(format_list), format_list); - if (err < 0) - return err; - - /* supported channels */ - if (cfg->has_channel_mode) - a2dp->sbc_capabilities.channel_mode = cfg->channel_mode; - - if (a2dp->sbc_capabilities.channel_mode & - BT_A2DP_CHANNEL_MODE_MONO) - min_channels = 1; - else - min_channels = 2; - - if (a2dp->sbc_capabilities.channel_mode & - (~BT_A2DP_CHANNEL_MODE_MONO)) - max_channels = 2; - else - max_channels = 1; - - err = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS, - min_channels, max_channels); - if (err < 0) - return err; - - /* supported buffer sizes - * (can be used as 3*8192, 6*4096, 12*2048, ...) */ - err = snd_pcm_ioplug_set_param_minmax(io, - SND_PCM_IOPLUG_HW_BUFFER_BYTES, - 8192*3, 8192*3); - if (err < 0) - return err; - - /* supported block sizes: */ - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, - ARRAY_NELEMS(period_list), period_list); - if (err < 0) - return err; - - /* supported rates */ - rate_count = 0; - if (cfg->has_rate) { - rate_list[rate_count] = cfg->rate; - rate_count++; - } else { - if (a2dp->sbc_capabilities.frequency & - BT_SBC_SAMPLING_FREQ_16000) { - rate_list[rate_count] = 16000; - rate_count++; - } - - if (a2dp->sbc_capabilities.frequency & - BT_SBC_SAMPLING_FREQ_32000) { - rate_list[rate_count] = 32000; - rate_count++; - } - - if (a2dp->sbc_capabilities.frequency & - BT_SBC_SAMPLING_FREQ_44100) { - rate_list[rate_count] = 44100; - rate_count++; - } - - if (a2dp->sbc_capabilities.frequency & - BT_SBC_SAMPLING_FREQ_48000) { - rate_list[rate_count] = 48000; - rate_count++; - } - } - - err = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_RATE, - rate_count, rate_list); - if (err < 0) - return err; - - return 0; -} - -static int bluetooth_parse_config(snd_config_t *conf, - struct bluetooth_alsa_config *bt_config) -{ - snd_config_iterator_t i, next; - - memset(bt_config, 0, sizeof(struct bluetooth_alsa_config)); - - /* Set defaults */ - bt_config->autoconnect = 1; - - snd_config_for_each(i, next, conf) { - snd_config_t *n = snd_config_iterator_entry(i); - const char *id, *value; - - if (snd_config_get_id(n, &id) < 0) - continue; - - if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) - continue; - - if (strcmp(id, "autoconnect") == 0) { - int b; - - b = snd_config_get_bool(n); - if (b < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - bt_config->autoconnect = b; - continue; - } - - if (strcmp(id, "device") == 0 || strcmp(id, "bdaddr") == 0) { - if (snd_config_get_string(n, &value) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - bt_config->has_device = 1; - strncpy(bt_config->device, value, 18); - continue; - } - - if (strcmp(id, "profile") == 0) { - if (snd_config_get_string(n, &value) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - if (strcmp(value, "auto") == 0) { - bt_config->transport = BT_CAPABILITIES_TRANSPORT_ANY; - bt_config->has_transport = 1; - } else if (strcmp(value, "voice") == 0 || - strcmp(value, "hfp") == 0) { - bt_config->transport = BT_CAPABILITIES_TRANSPORT_SCO; - bt_config->has_transport = 1; - } else if (strcmp(value, "hifi") == 0 || - strcmp(value, "a2dp") == 0) { - bt_config->transport = BT_CAPABILITIES_TRANSPORT_A2DP; - bt_config->has_transport = 1; - } - continue; - } - - if (strcmp(id, "rate") == 0) { - if (snd_config_get_string(n, &value) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - bt_config->rate = atoi(value); - bt_config->has_rate = 1; - continue; - } - - if (strcmp(id, "mode") == 0) { - if (snd_config_get_string(n, &value) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - if (strcmp(value, "mono") == 0) { - bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; - bt_config->has_channel_mode = 1; - } else if (strcmp(value, "dual") == 0) { - bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - bt_config->has_channel_mode = 1; - } else if (strcmp(value, "stereo") == 0) { - bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; - bt_config->has_channel_mode = 1; - } else if (strcmp(value, "joint") == 0) { - bt_config->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - bt_config->has_channel_mode = 1; - } - continue; - } - - if (strcmp(id, "allocation") == 0) { - if (snd_config_get_string(n, &value) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - if (strcmp(value, "loudness") == 0) { - bt_config->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; - bt_config->has_allocation_method = 1; - } else if (strcmp(value, "snr") == 0) { - bt_config->allocation_method = BT_A2DP_ALLOCATION_SNR; - bt_config->has_allocation_method = 1; - } - continue; - } - - if (strcmp(id, "subbands") == 0) { - if (snd_config_get_string(n, &value) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - bt_config->subbands = atoi(value); - bt_config->has_subbands = 1; - continue; - } - - if (strcmp(id, "blocks") == 0) { - if (snd_config_get_string(n, &value) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - bt_config->block_length = atoi(value); - bt_config->has_block_length = 1; - continue; - } - - if (strcmp(id, "bitpool") == 0) { - if (snd_config_get_string(n, &value) < 0) { - SNDERR("Invalid type for %s", id); - return -EINVAL; - } - - bt_config->bitpool = atoi(value); - bt_config->has_bitpool = 1; - continue; - } - - SNDERR("Unknown field %s", id); - return -EINVAL; - } - - return 0; -} - -static int audioservice_send(int sk, const bt_audio_msg_header_t *msg) -{ - int err; - uint16_t length; - - length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE; - - DBG("sending %s:%s", bt_audio_strtype(msg->type), - bt_audio_strname(msg->name)); - if (send(sk, msg, length, 0) > 0) - err = 0; - else { - err = -errno; - SNDERR("Error sending data to audio service: %s(%d)", - strerror(-err), -err); - } - - return err; -} - -static int audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) -{ - int err; - ssize_t ret; - const char *type, *name; - uint16_t length; - - length = inmsg->length ? inmsg->length : BT_SUGGESTED_BUFFER_SIZE; - - DBG("trying to receive msg from audio service..."); - - ret = recv(sk, inmsg, length, 0); - if (ret < 0) { - err = -errno; - SNDERR("Error receiving IPC data from bluetoothd: %s (%d)", - strerror(-err), -err); - } else if ((size_t) ret < sizeof(bt_audio_msg_header_t)) { - SNDERR("Too short (%d bytes) IPC packet from bluetoothd", ret); - err = -EINVAL; - } else { - type = bt_audio_strtype(inmsg->type); - name = bt_audio_strname(inmsg->name); - if (type && name) { - DBG("Received %s - %s", type, name); - err = 0; - } else { - err = -EINVAL; - SNDERR("Bogus message type %d - name %d" - " received from audio service", - inmsg->type, inmsg->name); - } - - } - - return err; -} - -static int audioservice_expect(int sk, bt_audio_msg_header_t *rsp, - int expected_name) -{ - bt_audio_error_t *error; - int err = audioservice_recv(sk, rsp); - - if (err != 0) - return err; - - if (rsp->name != expected_name) { - err = -EINVAL; - SNDERR("Bogus message %s received while %s was expected", - bt_audio_strname(rsp->name), - bt_audio_strname(expected_name)); - } - - if (rsp->type == BT_ERROR) { - error = (void *) rsp; - SNDERR("%s failed : %s(%d)", - bt_audio_strname(rsp->name), - strerror(error->posix_errno), - error->posix_errno); - return -error->posix_errno; - } - - return err; -} - -static int bluetooth_parse_capabilities(struct bluetooth_data *data, - struct bt_get_capabilities_rsp *rsp) -{ - int bytes_left = rsp->h.length - sizeof(*rsp); - codec_capabilities_t *codec = (void *) rsp->data; - - data->transport = codec->transport; - - if (codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP) - return 0; - - while (bytes_left > 0) { - if ((codec->type == BT_A2DP_SBC_SINK) && - !(codec->lock & BT_WRITE_LOCK)) - break; - - bytes_left -= codec->length; - codec = (void *) codec + codec->length; - } - - if (bytes_left <= 0 || - codec->length != sizeof(data->a2dp.sbc_capabilities)) - return -EINVAL; - - memcpy(&data->a2dp.sbc_capabilities, codec, codec->length); - - return 0; -} - -static int bluetooth_init(struct bluetooth_data *data, - snd_pcm_stream_t stream, snd_config_t *conf) -{ - int sk, err; - struct bluetooth_alsa_config *alsa_conf = &data->alsa_config; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_get_capabilities_req *req = (void *) buf; - struct bt_get_capabilities_rsp *rsp = (void *) buf; - - memset(data, 0, sizeof(struct bluetooth_data)); - - err = bluetooth_parse_config(conf, alsa_conf); - if (err < 0) - return err; - - data->server.fd = -1; - data->stream.fd = -1; - - sk = bt_audio_service_open(); - if (sk < 0) { - err = -errno; - goto failed; - } - - data->server.fd = sk; - data->server.events = POLLIN; - - data->pipefd[0] = -1; - data->pipefd[1] = -1; - - if (pipe(data->pipefd) < 0) { - err = -errno; - goto failed; - } - if (fcntl(data->pipefd[0], F_SETFL, O_NONBLOCK) < 0) { - err = -errno; - goto failed; - } - if (fcntl(data->pipefd[1], F_SETFL, O_NONBLOCK) < 0) { - err = -errno; - goto failed; - } - - memset(req, 0, BT_SUGGESTED_BUFFER_SIZE); - req->h.type = BT_REQUEST; - req->h.name = BT_GET_CAPABILITIES; - req->h.length = sizeof(*req); - - if (alsa_conf->autoconnect) - req->flags |= BT_FLAG_AUTOCONNECT; - strncpy(req->destination, alsa_conf->device, 18); - if (alsa_conf->has_transport) - req->transport = alsa_conf->transport; - else - req->transport = BT_CAPABILITIES_TRANSPORT_ANY; - - err = audioservice_send(data->server.fd, &req->h); - if (err < 0) - goto failed; - - rsp->h.length = 0; - err = audioservice_expect(data->server.fd, &rsp->h, - BT_GET_CAPABILITIES); - if (err < 0) - goto failed; - - bluetooth_parse_capabilities(data, rsp); - - return 0; - -failed: - if (sk >= 0) - bt_audio_service_close(sk); - return err; -} - -SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth); - -SND_PCM_PLUGIN_DEFINE_FUNC(bluetooth) -{ - struct bluetooth_data *data; - int err; - - DBG("Bluetooth PCM plugin (%s)", - stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); - - data = malloc(sizeof(struct bluetooth_data)); - if (!data) { - err = -ENOMEM; - goto error; - } - - err = bluetooth_init(data, stream, conf); - if (err < 0) - goto error; - - data->io.version = SND_PCM_IOPLUG_VERSION; - data->io.name = "Bluetooth Audio Device"; - data->io.mmap_rw = 0; /* No direct mmap communication */ - data->io.private_data = data; - - if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) - data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? - &bluetooth_a2dp_playback : - &bluetooth_a2dp_capture; - else - data->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? - &bluetooth_hsp_playback : - &bluetooth_hsp_capture; - - err = snd_pcm_ioplug_create(&data->io, name, stream, mode); - if (err < 0) - goto error; - - if (data->transport == BT_CAPABILITIES_TRANSPORT_A2DP) - err = bluetooth_a2dp_hw_constraint(&data->io); - else - err = bluetooth_hsp_hw_constraint(&data->io); - - if (err < 0) { - snd_pcm_ioplug_delete(&data->io); - goto error; - } - - *pcmp = data->io.pcm; - - return 0; - -error: - if (data) - bluetooth_exit(data); - - return err; -} - -SND_PCM_PLUGIN_SYMBOL(bluetooth); diff -Nru bluez-4.101/audio/rtp.h bluez-5.23/audio/rtp.h --- bluez-4.101/audio/rtp.h 2010-05-23 12:47:33.000000000 +0000 +++ bluez-5.23/audio/rtp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * 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 - * - */ - -#if __BYTE_ORDER == __LITTLE_ENDIAN - -struct rtp_header { - unsigned cc:4; - unsigned x:1; - unsigned p:1; - unsigned v:2; - - unsigned pt:7; - unsigned m:1; - - uint16_t sequence_number; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc[0]; -} __attribute__ ((packed)); - -struct rtp_payload { - unsigned frame_count:4; - unsigned rfa0:1; - unsigned is_last_fragment:1; - unsigned is_first_fragment:1; - unsigned is_fragmented:1; -} __attribute__ ((packed)); - -#elif __BYTE_ORDER == __BIG_ENDIAN - -struct rtp_header { - unsigned v:2; - unsigned p:1; - unsigned x:1; - unsigned cc:4; - - unsigned m:1; - unsigned pt:7; - - uint16_t sequence_number; - uint32_t timestamp; - uint32_t ssrc; - uint32_t csrc[0]; -} __attribute__ ((packed)); - -struct rtp_payload { - unsigned is_fragmented:1; - unsigned is_first_fragment:1; - unsigned is_last_fragment:1; - unsigned rfa0:1; - unsigned frame_count:4; -} __attribute__ ((packed)); - -#else -#error "Unknown byte order" -#endif diff -Nru bluez-4.101/audio/sink.c bluez-5.23/audio/sink.c --- bluez-4.101/audio/sink.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/sink.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,745 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include -#include - -#include -#include -#include - -#include "log.h" - -#include "device.h" -#include "avdtp.h" -#include "media.h" -#include "a2dp.h" -#include "error.h" -#include "sink.h" -#include "dbus-common.h" -#include "../src/adapter.h" -#include "../src/device.h" - -#define STREAM_SETUP_RETRY_TIMER 2 - -struct pending_request { - DBusConnection *conn; - DBusMessage *msg; - unsigned int id; -}; - -struct sink { - struct audio_device *dev; - struct avdtp *session; - struct avdtp_stream *stream; - unsigned int cb_id; - guint retry_id; - avdtp_session_state_t session_state; - avdtp_state_t stream_state; - sink_state_t state; - struct pending_request *connect; - struct pending_request *disconnect; - DBusConnection *conn; -}; - -struct sink_state_callback { - sink_state_cb cb; - void *user_data; - unsigned int id; -}; - -static GSList *sink_callbacks = NULL; - -static unsigned int avdtp_callback_id = 0; - -static char *str_state[] = { - "SINK_STATE_DISCONNECTED", - "SINK_STATE_CONNECTING", - "SINK_STATE_CONNECTED", - "SINK_STATE_PLAYING", -}; - -static const char *state2str(sink_state_t state) -{ - switch (state) { - case SINK_STATE_DISCONNECTED: - return "disconnected"; - case SINK_STATE_CONNECTING: - return "connecting"; - case SINK_STATE_CONNECTED: - return "connected"; - case SINK_STATE_PLAYING: - return "playing"; - default: - error("Invalid sink state %d", state); - return NULL; - } -} - -static void sink_set_state(struct audio_device *dev, sink_state_t new_state) -{ - struct sink *sink = dev->sink; - const char *state_str; - sink_state_t old_state = sink->state; - GSList *l; - - sink->state = new_state; - - state_str = state2str(new_state); - if (state_str) - emit_property_changed(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, "State", - DBUS_TYPE_STRING, &state_str); - - DBG("State changed %s: %s -> %s", dev->path, str_state[old_state], - str_state[new_state]); - - for (l = sink_callbacks; l != NULL; l = l->next) { - struct sink_state_callback *cb = l->data; - cb->cb(dev, old_state, new_state, cb->user_data); - } -} - -static void avdtp_state_callback(struct audio_device *dev, - struct avdtp *session, - avdtp_session_state_t old_state, - avdtp_session_state_t new_state, - void *user_data) -{ - struct sink *sink = dev->sink; - - if (sink == NULL) - return; - - switch (new_state) { - case AVDTP_SESSION_STATE_DISCONNECTED: - if (sink->state != SINK_STATE_CONNECTING) { - gboolean value = FALSE; - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, "Disconnected", - DBUS_TYPE_INVALID); - emit_property_changed(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, "Connected", - DBUS_TYPE_BOOLEAN, &value); - } - sink_set_state(dev, SINK_STATE_DISCONNECTED); - break; - case AVDTP_SESSION_STATE_CONNECTING: - sink_set_state(dev, SINK_STATE_CONNECTING); - break; - case AVDTP_SESSION_STATE_CONNECTED: - break; - } - - sink->session_state = new_state; -} - -static void pending_request_free(struct audio_device *dev, - struct pending_request *pending) -{ - if (pending->conn) - dbus_connection_unref(pending->conn); - if (pending->msg) - dbus_message_unref(pending->msg); - if (pending->id) - a2dp_cancel(dev, pending->id); - - g_free(pending); -} - -static void stream_state_changed(struct avdtp_stream *stream, - avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, - void *user_data) -{ - struct audio_device *dev = user_data; - struct sink *sink = dev->sink; - gboolean value; - - if (err) - return; - - switch (new_state) { - case AVDTP_STATE_IDLE: - if (sink->disconnect) { - DBusMessage *reply; - struct pending_request *p; - - p = sink->disconnect; - sink->disconnect = NULL; - - reply = dbus_message_new_method_return(p->msg); - g_dbus_send_message(p->conn, reply); - pending_request_free(dev, p); - } - - if (sink->session) { - avdtp_unref(sink->session); - sink->session = NULL; - } - sink->stream = NULL; - sink->cb_id = 0; - break; - case AVDTP_STATE_OPEN: - if (old_state == AVDTP_STATE_CONFIGURED && - sink->state == SINK_STATE_CONNECTING) { - value = TRUE; - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, - "Connected", - DBUS_TYPE_INVALID); - emit_property_changed(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, - "Connected", - DBUS_TYPE_BOOLEAN, &value); - } else if (old_state == AVDTP_STATE_STREAMING) { - value = FALSE; - g_dbus_emit_signal(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, - "Stopped", - DBUS_TYPE_INVALID); - emit_property_changed(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, - "Playing", - DBUS_TYPE_BOOLEAN, &value); - } - sink_set_state(dev, SINK_STATE_CONNECTED); - break; - case AVDTP_STATE_STREAMING: - value = TRUE; - g_dbus_emit_signal(dev->conn, dev->path, AUDIO_SINK_INTERFACE, - "Playing", DBUS_TYPE_INVALID); - emit_property_changed(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, "Playing", - DBUS_TYPE_BOOLEAN, &value); - sink_set_state(dev, SINK_STATE_PLAYING); - break; - case AVDTP_STATE_CONFIGURED: - case AVDTP_STATE_CLOSING: - case AVDTP_STATE_ABORTING: - default: - break; - } - - sink->stream_state = new_state; -} - -static void error_failed(DBusConnection *conn, DBusMessage *msg, - const char *desc) -{ - DBusMessage *reply = btd_error_failed(msg, desc); - g_dbus_send_message(conn, reply); -} - -static gboolean stream_setup_retry(gpointer user_data) -{ - struct sink *sink = user_data; - struct pending_request *pending = sink->connect; - - sink->retry_id = 0; - - if (sink->stream_state >= AVDTP_STATE_OPEN) { - DBG("Stream successfully created, after XCASE connect:connect"); - if (pending->msg) { - DBusMessage *reply; - reply = dbus_message_new_method_return(pending->msg); - g_dbus_send_message(pending->conn, reply); - } - } else { - DBG("Stream setup failed, after XCASE connect:connect"); - if (pending->msg) - error_failed(pending->conn, pending->msg, "Stream setup failed"); - } - - sink->connect = NULL; - pending_request_free(sink->dev, pending); - - return FALSE; -} - -static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data) -{ - struct sink *sink = user_data; - struct pending_request *pending; - - pending = sink->connect; - - pending->id = 0; - - if (stream) { - DBG("Stream successfully created"); - - if (pending->msg) { - DBusMessage *reply; - reply = dbus_message_new_method_return(pending->msg); - g_dbus_send_message(pending->conn, reply); - } - - sink->connect = NULL; - pending_request_free(sink->dev, pending); - - return; - } - - avdtp_unref(sink->session); - sink->session = NULL; - if (avdtp_error_category(err) == AVDTP_ERRNO - && avdtp_error_posix_errno(err) != EHOSTDOWN) { - DBG("connect:connect XCASE detected"); - sink->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER, - stream_setup_retry, - sink); - } else { - if (pending->msg) - error_failed(pending->conn, pending->msg, "Stream setup failed"); - sink->connect = NULL; - pending_request_free(sink->dev, pending); - DBG("Stream setup failed : %s", avdtp_strerror(err)); - } -} - -static void select_complete(struct avdtp *session, struct a2dp_sep *sep, - GSList *caps, void *user_data) -{ - struct sink *sink = user_data; - struct pending_request *pending; - int id; - - pending = sink->connect; - pending->id = 0; - - id = a2dp_config(session, sep, stream_setup_complete, caps, sink); - if (id == 0) - goto failed; - - pending->id = id; - return; - -failed: - if (pending->msg) - error_failed(pending->conn, pending->msg, "Stream setup failed"); - pending_request_free(sink->dev, pending); - sink->connect = NULL; - avdtp_unref(sink->session); - sink->session = NULL; -} - -static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, - void *user_data) -{ - struct sink *sink = user_data; - struct pending_request *pending; - int id; - - if (!sink->connect) { - avdtp_unref(sink->session); - sink->session = NULL; - return; - } - - pending = sink->connect; - - if (err) { - avdtp_unref(sink->session); - sink->session = NULL; - if (avdtp_error_category(err) == AVDTP_ERRNO - && avdtp_error_posix_errno(err) != EHOSTDOWN) { - DBG("connect:connect XCASE detected"); - sink->retry_id = - g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER, - stream_setup_retry, - sink); - } else - goto failed; - return; - } - - DBG("Discovery complete"); - - id = a2dp_select_capabilities(sink->session, AVDTP_SEP_TYPE_SINK, NULL, - select_complete, sink); - if (id == 0) - goto failed; - - pending->id = id; - return; - -failed: - if (pending->msg) - error_failed(pending->conn, pending->msg, "Stream setup failed"); - pending_request_free(sink->dev, pending); - sink->connect = NULL; - avdtp_unref(sink->session); - sink->session = NULL; -} - -gboolean sink_setup_stream(struct sink *sink, struct avdtp *session) -{ - if (sink->connect || sink->disconnect) - return FALSE; - - if (session && !sink->session) - sink->session = avdtp_ref(session); - - if (!sink->session) - return FALSE; - - avdtp_set_auto_disconnect(sink->session, FALSE); - - if (avdtp_discover(sink->session, discovery_complete, sink) < 0) - return FALSE; - - sink->connect = g_new0(struct pending_request, 1); - - return TRUE; -} - -static DBusMessage *sink_connect(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *dev = data; - struct sink *sink = dev->sink; - struct pending_request *pending; - - if (!sink->session) - sink->session = avdtp_get(&dev->src, &dev->dst); - - if (!sink->session) - return btd_error_failed(msg, "Unable to get a session"); - - if (sink->connect || sink->disconnect) - return btd_error_busy(msg); - - if (sink->stream_state >= AVDTP_STATE_OPEN) - return btd_error_already_connected(msg); - - if (!sink_setup_stream(sink, NULL)) - return btd_error_failed(msg, "Failed to create a stream"); - - dev->auto_connect = FALSE; - - pending = sink->connect; - - pending->conn = dbus_connection_ref(conn); - pending->msg = dbus_message_ref(msg); - - DBG("stream creation in progress"); - - return NULL; -} - -static DBusMessage *sink_disconnect(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - struct sink *sink = device->sink; - struct pending_request *pending; - int err; - - if (!sink->session) - return btd_error_not_connected(msg); - - if (sink->connect || sink->disconnect) - return btd_error_busy(msg); - - if (sink->stream_state < AVDTP_STATE_OPEN) { - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - avdtp_unref(sink->session); - sink->session = NULL; - return reply; - } - - err = avdtp_close(sink->session, sink->stream, FALSE); - if (err < 0) - return btd_error_failed(msg, strerror(-err)); - - pending = g_new0(struct pending_request, 1); - pending->conn = dbus_connection_ref(conn); - pending->msg = dbus_message_ref(msg); - sink->disconnect = pending; - - return NULL; -} - -static DBusMessage *sink_is_connected(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - struct audio_device *device = data; - struct sink *sink = device->sink; - DBusMessage *reply; - dbus_bool_t connected; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - connected = (sink->stream_state >= AVDTP_STATE_CONFIGURED); - - dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &connected, - DBUS_TYPE_INVALID); - - return reply; -} - -static DBusMessage *sink_get_properties(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - struct sink *sink = device->sink; - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter dict; - const char *state; - gboolean value; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - /* Playing */ - value = (sink->stream_state == AVDTP_STATE_STREAMING); - dict_append_entry(&dict, "Playing", DBUS_TYPE_BOOLEAN, &value); - - /* Connected */ - value = (sink->stream_state >= AVDTP_STATE_CONFIGURED); - dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &value); - - /* State */ - state = state2str(sink->state); - if (state) - dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state); - - dbus_message_iter_close_container(&iter, &dict); - - return reply; -} - -static const GDBusMethodTable sink_methods[] = { - { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, sink_connect) }, - { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, sink_disconnect) }, - { GDBUS_DEPRECATED_METHOD("IsConnected", - NULL, GDBUS_ARGS({ "connected", "b" }), - sink_is_connected) }, - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - sink_get_properties) }, - { } -}; - -static const GDBusSignalTable sink_signals[] = { - { GDBUS_DEPRECATED_SIGNAL("Connected", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("Disconnected", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("Playing", NULL) }, - { GDBUS_DEPRECATED_SIGNAL("Stopped", NULL) }, - { GDBUS_SIGNAL("PropertyChanged", - GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { } -}; - -static void sink_free(struct audio_device *dev) -{ - struct sink *sink = dev->sink; - - if (sink->cb_id) - avdtp_stream_remove_cb(sink->session, sink->stream, - sink->cb_id); - - if (sink->session) - avdtp_unref(sink->session); - - if (sink->connect) - pending_request_free(dev, sink->connect); - - if (sink->disconnect) - pending_request_free(dev, sink->disconnect); - - if (sink->retry_id) - g_source_remove(sink->retry_id); - - g_free(sink); - dev->sink = NULL; -} - -static void path_unregister(void *data) -{ - struct audio_device *dev = data; - - DBG("Unregistered interface %s on path %s", - AUDIO_SINK_INTERFACE, dev->path); - - sink_free(dev); -} - -void sink_unregister(struct audio_device *dev) -{ - g_dbus_unregister_interface(dev->conn, dev->path, - AUDIO_SINK_INTERFACE); -} - -struct sink *sink_init(struct audio_device *dev) -{ - struct sink *sink; - - if (!g_dbus_register_interface(dev->conn, dev->path, - AUDIO_SINK_INTERFACE, - sink_methods, sink_signals, NULL, - dev, path_unregister)) - return NULL; - - DBG("Registered interface %s on path %s", - AUDIO_SINK_INTERFACE, dev->path); - - if (avdtp_callback_id == 0) - avdtp_callback_id = avdtp_add_state_cb(avdtp_state_callback, - NULL); - - sink = g_new0(struct sink, 1); - - sink->dev = dev; - - return sink; -} - -gboolean sink_is_active(struct audio_device *dev) -{ - struct sink *sink = dev->sink; - - if (sink->session) - return TRUE; - - return FALSE; -} - -sink_state_t sink_get_state(struct audio_device *dev) -{ - struct sink *sink = dev->sink; - - return sink->state; -} - -gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session, - struct avdtp_stream *stream) -{ - struct sink *sink = dev->sink; - - if (sink->stream) - return FALSE; - - if (!sink->session) - sink->session = avdtp_ref(session); - - sink->stream = stream; - - sink->cb_id = avdtp_stream_add_cb(session, stream, - stream_state_changed, dev); - - return TRUE; -} - -gboolean sink_shutdown(struct sink *sink) -{ - if (!sink->session) - return FALSE; - - avdtp_set_device_disconnect(sink->session, TRUE); - - /* cancel pending connect */ - if (sink->connect) { - struct pending_request *pending = sink->connect; - - if (pending->msg) - error_failed(pending->conn, pending->msg, - "Stream setup failed"); - pending_request_free(sink->dev, pending); - sink->connect = NULL; - - avdtp_unref(sink->session); - sink->session = NULL; - - return TRUE; - } - - /* disconnect already ongoing */ - if (sink->disconnect) - return TRUE; - - if (!sink->stream) - return FALSE; - - if (avdtp_close(sink->session, sink->stream, FALSE) < 0) - return FALSE; - - return TRUE; -} - -unsigned int sink_add_state_cb(sink_state_cb cb, void *user_data) -{ - struct sink_state_callback *state_cb; - static unsigned int id = 0; - - state_cb = g_new(struct sink_state_callback, 1); - state_cb->cb = cb; - state_cb->user_data = user_data; - state_cb->id = ++id; - - sink_callbacks = g_slist_append(sink_callbacks, state_cb); - - return state_cb->id; -} - -gboolean sink_remove_state_cb(unsigned int id) -{ - GSList *l; - - for (l = sink_callbacks; l != NULL; l = l->next) { - struct sink_state_callback *cb = l->data; - if (cb && cb->id == id) { - sink_callbacks = g_slist_remove(sink_callbacks, cb); - g_free(cb); - return TRUE; - } - } - - return FALSE; -} diff -Nru bluez-4.101/audio/sink.h bluez-5.23/audio/sink.h --- bluez-4.101/audio/sink.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/sink.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define AUDIO_SINK_INTERFACE "org.bluez.AudioSink" - -typedef enum { - SINK_STATE_DISCONNECTED, - SINK_STATE_CONNECTING, - SINK_STATE_CONNECTED, - SINK_STATE_PLAYING, -} sink_state_t; - -typedef void (*sink_state_cb) (struct audio_device *dev, - sink_state_t old_state, - sink_state_t new_state, - void *user_data); - -unsigned int sink_add_state_cb(sink_state_cb cb, void *user_data); -gboolean sink_remove_state_cb(unsigned int id); - -struct sink *sink_init(struct audio_device *dev); -void sink_unregister(struct audio_device *dev); -gboolean sink_is_active(struct audio_device *dev); -sink_state_t sink_get_state(struct audio_device *dev); -gboolean sink_new_stream(struct audio_device *dev, struct avdtp *session, - struct avdtp_stream *stream); -gboolean sink_setup_stream(struct sink *sink, struct avdtp *session); -gboolean sink_shutdown(struct sink *sink); diff -Nru bluez-4.101/audio/source.c bluez-5.23/audio/source.c --- bluez-4.101/audio/source.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/source.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,634 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * Copyright (C) 2009 Joao Paulo Rechi Vita - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include -#include - -#include -#include -#include - -#include "log.h" - -#include "device.h" -#include "avdtp.h" -#include "media.h" -#include "a2dp.h" -#include "error.h" -#include "source.h" -#include "dbus-common.h" -#include "../src/adapter.h" -#include "../src/device.h" - -#define STREAM_SETUP_RETRY_TIMER 2 - -struct pending_request { - DBusConnection *conn; - DBusMessage *msg; - unsigned int id; -}; - -struct source { - struct audio_device *dev; - struct avdtp *session; - struct avdtp_stream *stream; - unsigned int cb_id; - guint retry_id; - avdtp_session_state_t session_state; - avdtp_state_t stream_state; - source_state_t state; - struct pending_request *connect; - struct pending_request *disconnect; - DBusConnection *conn; -}; - -struct source_state_callback { - source_state_cb cb; - void *user_data; - unsigned int id; -}; - -static GSList *source_callbacks = NULL; - -static unsigned int avdtp_callback_id = 0; - -static const char *state2str(source_state_t state) -{ - switch (state) { - case SOURCE_STATE_DISCONNECTED: - return "disconnected"; - case SOURCE_STATE_CONNECTING: - return "connecting"; - case SOURCE_STATE_CONNECTED: - return "connected"; - case SOURCE_STATE_PLAYING: - return "playing"; - default: - error("Invalid source state %d", state); - return NULL; - } -} - -static void source_set_state(struct audio_device *dev, source_state_t new_state) -{ - struct source *source = dev->source; - const char *state_str; - source_state_t old_state = source->state; - GSList *l; - - source->state = new_state; - - state_str = state2str(new_state); - if (state_str) - emit_property_changed(dev->conn, dev->path, - AUDIO_SOURCE_INTERFACE, "State", - DBUS_TYPE_STRING, &state_str); - - for (l = source_callbacks; l != NULL; l = l->next) { - struct source_state_callback *cb = l->data; - cb->cb(dev, old_state, new_state, cb->user_data); - } -} - -static void avdtp_state_callback(struct audio_device *dev, - struct avdtp *session, - avdtp_session_state_t old_state, - avdtp_session_state_t new_state, - void *user_data) -{ - struct source *source = dev->source; - - if (source == NULL) - return; - - switch (new_state) { - case AVDTP_SESSION_STATE_DISCONNECTED: - source_set_state(dev, SOURCE_STATE_DISCONNECTED); - break; - case AVDTP_SESSION_STATE_CONNECTING: - source_set_state(dev, SOURCE_STATE_CONNECTING); - break; - case AVDTP_SESSION_STATE_CONNECTED: - break; - } - - source->session_state = new_state; -} - -static void pending_request_free(struct audio_device *dev, - struct pending_request *pending) -{ - if (pending->conn) - dbus_connection_unref(pending->conn); - if (pending->msg) - dbus_message_unref(pending->msg); - if (pending->id) - a2dp_cancel(dev, pending->id); - - g_free(pending); -} - -static void stream_state_changed(struct avdtp_stream *stream, - avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, - void *user_data) -{ - struct audio_device *dev = user_data; - struct source *source = dev->source; - - if (err) - return; - - switch (new_state) { - case AVDTP_STATE_IDLE: - if (source->disconnect) { - DBusMessage *reply; - struct pending_request *p; - - p = source->disconnect; - source->disconnect = NULL; - - reply = dbus_message_new_method_return(p->msg); - g_dbus_send_message(p->conn, reply); - pending_request_free(dev, p); - } - - if (source->session) { - avdtp_unref(source->session); - source->session = NULL; - } - source->stream = NULL; - source->cb_id = 0; - break; - case AVDTP_STATE_OPEN: - source_set_state(dev, SOURCE_STATE_CONNECTED); - break; - case AVDTP_STATE_STREAMING: - source_set_state(dev, SOURCE_STATE_PLAYING); - break; - case AVDTP_STATE_CONFIGURED: - case AVDTP_STATE_CLOSING: - case AVDTP_STATE_ABORTING: - default: - break; - } - - source->stream_state = new_state; -} - -static void error_failed(DBusConnection *conn, DBusMessage *msg, - const char *desc) -{ - DBusMessage *reply = btd_error_failed(msg, desc); - g_dbus_send_message(conn, reply); -} - -static gboolean stream_setup_retry(gpointer user_data) -{ - struct source *source = user_data; - struct pending_request *pending = source->connect; - - source->retry_id = 0; - - if (source->stream_state >= AVDTP_STATE_OPEN) { - DBG("Stream successfully created, after XCASE connect:connect"); - if (pending->msg) { - DBusMessage *reply; - reply = dbus_message_new_method_return(pending->msg); - g_dbus_send_message(pending->conn, reply); - } - } else { - DBG("Stream setup failed, after XCASE connect:connect"); - if (pending->msg) - error_failed(pending->conn, pending->msg, "Stream setup failed"); - } - - source->connect = NULL; - pending_request_free(source->dev, pending); - - return FALSE; -} - -static void stream_setup_complete(struct avdtp *session, struct a2dp_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, void *user_data) -{ - struct source *source = user_data; - struct pending_request *pending; - - pending = source->connect; - - pending->id = 0; - - if (stream) { - DBG("Stream successfully created"); - - if (pending->msg) { - DBusMessage *reply; - reply = dbus_message_new_method_return(pending->msg); - g_dbus_send_message(pending->conn, reply); - } - - source->connect = NULL; - pending_request_free(source->dev, pending); - - return; - } - - avdtp_unref(source->session); - source->session = NULL; - if (avdtp_error_category(err) == AVDTP_ERRNO - && avdtp_error_posix_errno(err) != EHOSTDOWN) { - DBG("connect:connect XCASE detected"); - source->retry_id = g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER, - stream_setup_retry, - source); - } else { - if (pending->msg) - error_failed(pending->conn, pending->msg, "Stream setup failed"); - source->connect = NULL; - pending_request_free(source->dev, pending); - DBG("Stream setup failed : %s", avdtp_strerror(err)); - } -} - -static void select_complete(struct avdtp *session, struct a2dp_sep *sep, - GSList *caps, void *user_data) -{ - struct source *source = user_data; - struct pending_request *pending; - int id; - - pending = source->connect; - - pending->id = 0; - - if (caps == NULL) - goto failed; - - id = a2dp_config(session, sep, stream_setup_complete, caps, source); - if (id == 0) - goto failed; - - pending->id = id; - return; - -failed: - if (pending->msg) - error_failed(pending->conn, pending->msg, "Stream setup failed"); - pending_request_free(source->dev, pending); - source->connect = NULL; - avdtp_unref(source->session); - source->session = NULL; -} - -static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp_error *err, - void *user_data) -{ - struct source *source = user_data; - struct pending_request *pending; - int id; - - pending = source->connect; - - if (err) { - avdtp_unref(source->session); - source->session = NULL; - if (avdtp_error_category(err) == AVDTP_ERRNO - && avdtp_error_posix_errno(err) != EHOSTDOWN) { - DBG("connect:connect XCASE detected"); - source->retry_id = - g_timeout_add_seconds(STREAM_SETUP_RETRY_TIMER, - stream_setup_retry, - source); - } else - goto failed; - return; - } - - DBG("Discovery complete"); - - id = a2dp_select_capabilities(source->session, AVDTP_SEP_TYPE_SOURCE, NULL, - select_complete, source); - if (id == 0) - goto failed; - - pending->id = id; - return; - -failed: - if (pending->msg) - error_failed(pending->conn, pending->msg, "Stream setup failed"); - pending_request_free(source->dev, pending); - source->connect = NULL; - avdtp_unref(source->session); - source->session = NULL; -} - -gboolean source_setup_stream(struct source *source, struct avdtp *session) -{ - if (source->connect || source->disconnect) - return FALSE; - - if (session && !source->session) - source->session = avdtp_ref(session); - - if (!source->session) - return FALSE; - - avdtp_set_auto_disconnect(source->session, FALSE); - - if (avdtp_discover(source->session, discovery_complete, source) < 0) - return FALSE; - - source->connect = g_new0(struct pending_request, 1); - - return TRUE; -} - -static DBusMessage *source_connect(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *dev = data; - struct source *source = dev->source; - struct pending_request *pending; - - if (!source->session) - source->session = avdtp_get(&dev->src, &dev->dst); - - if (!source->session) - return btd_error_failed(msg, "Unable to get a session"); - - if (source->connect || source->disconnect) - return btd_error_busy(msg); - - if (source->stream_state >= AVDTP_STATE_OPEN) - return btd_error_already_connected(msg); - - if (!source_setup_stream(source, NULL)) - return btd_error_failed(msg, "Failed to create a stream"); - - dev->auto_connect = FALSE; - - pending = source->connect; - - pending->conn = dbus_connection_ref(conn); - pending->msg = dbus_message_ref(msg); - - DBG("stream creation in progress"); - - return NULL; -} - -static DBusMessage *source_disconnect(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - struct source *source = device->source; - struct pending_request *pending; - int err; - - if (!source->session) - return btd_error_not_connected(msg); - - if (source->connect || source->disconnect) - return btd_error_busy(msg); - - if (source->stream_state < AVDTP_STATE_OPEN) { - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - avdtp_unref(source->session); - source->session = NULL; - return reply; - } - - err = avdtp_close(source->session, source->stream, FALSE); - if (err < 0) - return btd_error_failed(msg, strerror(-err)); - - pending = g_new0(struct pending_request, 1); - pending->conn = dbus_connection_ref(conn); - pending->msg = dbus_message_ref(msg); - source->disconnect = pending; - - return NULL; -} - -static DBusMessage *source_get_properties(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct audio_device *device = data; - struct source *source = device->source; - DBusMessage *reply; - DBusMessageIter iter; - DBusMessageIter dict; - const char *state; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - /* State */ - state = state2str(source->state); - if (state) - dict_append_entry(&dict, "State", DBUS_TYPE_STRING, &state); - - dbus_message_iter_close_container(&iter, &dict); - - return reply; -} - -static const GDBusMethodTable source_methods[] = { - { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, source_connect) }, - { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, source_disconnect) }, - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - source_get_properties) }, - { } -}; - -static const GDBusSignalTable source_signals[] = { - { GDBUS_SIGNAL("PropertyChanged", - GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { } -}; - -static void source_free(struct audio_device *dev) -{ - struct source *source = dev->source; - - if (source->cb_id) - avdtp_stream_remove_cb(source->session, source->stream, - source->cb_id); - - if (source->session) - avdtp_unref(source->session); - - if (source->connect) - pending_request_free(dev, source->connect); - - if (source->disconnect) - pending_request_free(dev, source->disconnect); - - if (source->retry_id) - g_source_remove(source->retry_id); - - g_free(source); - dev->source = NULL; -} - -static void path_unregister(void *data) -{ - struct audio_device *dev = data; - - DBG("Unregistered interface %s on path %s", - AUDIO_SOURCE_INTERFACE, dev->path); - - source_free(dev); -} - -void source_unregister(struct audio_device *dev) -{ - g_dbus_unregister_interface(dev->conn, dev->path, - AUDIO_SOURCE_INTERFACE); -} - -struct source *source_init(struct audio_device *dev) -{ - struct source *source; - - if (!g_dbus_register_interface(dev->conn, dev->path, - AUDIO_SOURCE_INTERFACE, - source_methods, source_signals, NULL, - dev, path_unregister)) - return NULL; - - DBG("Registered interface %s on path %s", - AUDIO_SOURCE_INTERFACE, dev->path); - - if (avdtp_callback_id == 0) - avdtp_callback_id = avdtp_add_state_cb(avdtp_state_callback, - NULL); - - source = g_new0(struct source, 1); - - source->dev = dev; - - return source; -} - -gboolean source_is_active(struct audio_device *dev) -{ - struct source *source = dev->source; - - if (source->session) - return TRUE; - - return FALSE; -} - -source_state_t source_get_state(struct audio_device *dev) -{ - struct source *source = dev->source; - - return source->state; -} - -gboolean source_new_stream(struct audio_device *dev, struct avdtp *session, - struct avdtp_stream *stream) -{ - struct source *source = dev->source; - - if (source->stream) - return FALSE; - - if (!source->session) - source->session = avdtp_ref(session); - - source->stream = stream; - - source->cb_id = avdtp_stream_add_cb(session, stream, - stream_state_changed, dev); - - return TRUE; -} - -gboolean source_shutdown(struct source *source) -{ - if (!source->stream) - return FALSE; - - if (avdtp_close(source->session, source->stream, FALSE) < 0) - return FALSE; - - return TRUE; -} - -unsigned int source_add_state_cb(source_state_cb cb, void *user_data) -{ - struct source_state_callback *state_cb; - static unsigned int id = 0; - - state_cb = g_new(struct source_state_callback, 1); - state_cb->cb = cb; - state_cb->user_data = user_data; - state_cb->id = ++id; - - source_callbacks = g_slist_append(source_callbacks, state_cb); - - return state_cb->id; -} - -gboolean source_remove_state_cb(unsigned int id) -{ - GSList *l; - - for (l = source_callbacks; l != NULL; l = l->next) { - struct source_state_callback *cb = l->data; - if (cb && cb->id == id) { - source_callbacks = g_slist_remove(source_callbacks, cb); - g_free(cb); - return TRUE; - } - } - - return FALSE; -} diff -Nru bluez-4.101/audio/source.h bluez-5.23/audio/source.h --- bluez-4.101/audio/source.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/source.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * Copyright (C) 2009 Joao Paulo Rechi Vita - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define AUDIO_SOURCE_INTERFACE "org.bluez.AudioSource" - -typedef enum { - SOURCE_STATE_DISCONNECTED, - SOURCE_STATE_CONNECTING, - SOURCE_STATE_CONNECTED, - SOURCE_STATE_PLAYING, -} source_state_t; - -typedef void (*source_state_cb) (struct audio_device *dev, - source_state_t old_state, - source_state_t new_state, - void *user_data); - -unsigned int source_add_state_cb(source_state_cb cb, void *user_data); -gboolean source_remove_state_cb(unsigned int id); - -struct source *source_init(struct audio_device *dev); -void source_unregister(struct audio_device *dev); -gboolean source_is_active(struct audio_device *dev); -source_state_t source_get_state(struct audio_device *dev); -gboolean source_new_stream(struct audio_device *dev, struct avdtp *session, - struct avdtp_stream *stream); -gboolean source_setup_stream(struct source *source, struct avdtp *session); -gboolean source_shutdown(struct source *source); diff -Nru bluez-4.101/audio/telephony-dummy.c bluez-5.23/audio/telephony-dummy.c --- bluez-4.101/audio/telephony-dummy.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/telephony-dummy.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,447 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "telephony.h" -#include "error.h" - -#define TELEPHONY_DUMMY_IFACE "org.bluez.TelephonyTest" -#define TELEPHONY_DUMMY_PATH "/org/bluez/test" - -static DBusConnection *connection = NULL; - -static const char *chld_str = "0,1,1x,2,2x,3,4"; -static char *subscriber_number = NULL; -static char *active_call_number = NULL; -static int active_call_status = 0; -static int active_call_dir = 0; - -static gboolean events_enabled = FALSE; - -static struct indicator dummy_indicators[] = -{ - { "battchg", "0-5", 5, TRUE }, - { "signal", "0-5", 5, TRUE }, - { "service", "0,1", 1, TRUE }, - { "call", "0,1", 0, TRUE }, - { "callsetup", "0-3", 0, TRUE }, - { "callheld", "0-2", 0, FALSE }, - { "roam", "0,1", 0, TRUE }, - { NULL } -}; - -void telephony_device_connected(void *telephony_device) -{ - DBG("telephony-dummy: device %p connected", telephony_device); -} - -void telephony_device_disconnected(void *telephony_device) -{ - DBG("telephony-dummy: device %p disconnected", telephony_device); - events_enabled = FALSE; -} - -void telephony_event_reporting_req(void *telephony_device, int ind) -{ - events_enabled = ind == 1 ? TRUE : FALSE; - - telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_response_and_hold_req(void *telephony_device, int rh) -{ - telephony_response_and_hold_rsp(telephony_device, - CME_ERROR_NOT_SUPPORTED); -} - -void telephony_last_dialed_number_req(void *telephony_device) -{ - telephony_last_dialed_number_rsp(telephony_device, CME_ERROR_NONE); - - /* Notify outgoing call set-up successfully initiated */ - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_OUTGOING); - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_ALERTING); - - active_call_status = CALL_STATUS_ALERTING; - active_call_dir = CALL_DIR_OUTGOING; -} - -void telephony_terminate_call_req(void *telephony_device) -{ - g_free(active_call_number); - active_call_number = NULL; - - telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE); - - if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_INACTIVE); - else - telephony_update_indicator(dummy_indicators, "call", - EV_CALL_INACTIVE); -} - -void telephony_answer_call_req(void *telephony_device) -{ - telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE); - - telephony_update_indicator(dummy_indicators, "call", EV_CALL_ACTIVE); - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_INACTIVE); - - active_call_status = CALL_STATUS_ACTIVE; -} - -void telephony_dial_number_req(void *telephony_device, const char *number) -{ - g_free(active_call_number); - active_call_number = g_strdup(number); - - DBG("telephony-dummy: dial request to %s", active_call_number); - - telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE); - - /* Notify outgoing call set-up successfully initiated */ - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_OUTGOING); - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_ALERTING); - - active_call_status = CALL_STATUS_ALERTING; - active_call_dir = CALL_DIR_OUTGOING; -} - -void telephony_transmit_dtmf_req(void *telephony_device, char tone) -{ - DBG("telephony-dummy: transmit dtmf: %c", tone); - telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_subscriber_number_req(void *telephony_device) -{ - DBG("telephony-dummy: subscriber number request"); - if (subscriber_number) - telephony_subscriber_number_ind(subscriber_number, - NUMBER_TYPE_TELEPHONY, - SUBSCRIBER_SERVICE_VOICE); - telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_list_current_calls_req(void *telephony_device) -{ - DBG("telephony-dummy: list current calls request"); - if (active_call_number) - telephony_list_current_call_ind(1, active_call_dir, - active_call_status, - CALL_MODE_VOICE, - CALL_MULTIPARTY_NO, - active_call_number, - NUMBER_TYPE_TELEPHONY); - telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_operator_selection_req(void *telephony_device) -{ - telephony_operator_selection_ind(OPERATOR_MODE_AUTO, "DummyOperator"); - telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_call_hold_req(void *telephony_device, const char *cmd) -{ - DBG("telephony-dymmy: got call hold request %s", cmd); - telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_nr_and_ec_req(void *telephony_device, gboolean enable) -{ - DBG("telephony-dummy: got %s NR and EC request", - enable ? "enable" : "disable"); - - telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_voice_dial_req(void *telephony_device, gboolean enable) -{ - DBG("telephony-dummy: got %s voice dial request", - enable ? "enable" : "disable"); - - g_dbus_emit_signal(connection, TELEPHONY_DUMMY_PATH, - TELEPHONY_DUMMY_IFACE, "VoiceDial", - DBUS_TYPE_INVALID); - - telephony_voice_dial_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_key_press_req(void *telephony_device, const char *keys) -{ - DBG("telephony-dummy: got key press request for %s", keys); - telephony_key_press_rsp(telephony_device, CME_ERROR_NONE); -} - -/* D-Bus method handlers */ -static DBusMessage *outgoing_call(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - const char *number; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - DBG("telephony-dummy: outgoing call to %s", number); - - g_free(active_call_number); - active_call_number = g_strdup(number); - - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_OUTGOING); - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_ALERTING); - - active_call_status = CALL_STATUS_ALERTING; - active_call_dir = CALL_DIR_OUTGOING; - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *incoming_call(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - const char *number; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - DBG("telephony-dummy: incoming call to %s", number); - - g_free(active_call_number); - active_call_number = g_strdup(number); - - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_INCOMING); - - active_call_status = CALL_STATUS_INCOMING; - active_call_dir = CALL_DIR_INCOMING; - - telephony_incoming_call_ind(number, NUMBER_TYPE_TELEPHONY); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *cancel_call(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - DBG("telephony-dummy: cancel call"); - - g_free(active_call_number); - active_call_number = NULL; - - if (telephony_get_indicator(dummy_indicators, "callsetup") > 0) { - telephony_update_indicator(dummy_indicators, "callsetup", - EV_CALLSETUP_INACTIVE); - telephony_calling_stopped_ind(); - } - - if (telephony_get_indicator(dummy_indicators, "call") > 0) - telephony_update_indicator(dummy_indicators, "call", - EV_CALL_INACTIVE); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *signal_strength(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - dbus_uint32_t strength; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &strength, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - if (strength > 5) - return btd_error_invalid_args(msg); - - telephony_update_indicator(dummy_indicators, "signal", strength); - - DBG("telephony-dummy: signal strength set to %u", strength); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *battery_level(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - dbus_uint32_t level; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &level, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - if (level > 5) - return btd_error_invalid_args(msg); - - telephony_update_indicator(dummy_indicators, "battchg", level); - - DBG("telephony-dummy: battery level set to %u", level); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *roaming_status(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - dbus_bool_t roaming; - int val; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &roaming, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - val = roaming ? EV_ROAM_ACTIVE : EV_ROAM_INACTIVE; - - telephony_update_indicator(dummy_indicators, "roam", val); - - DBG("telephony-dummy: roaming status set to %d", val); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *registration_status(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - dbus_bool_t registration; - int val; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, ®istration, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - val = registration ? EV_SERVICE_PRESENT : EV_SERVICE_NONE; - - telephony_update_indicator(dummy_indicators, "service", val); - - DBG("telephony-dummy: registration status set to %d", val); - - return dbus_message_new_method_return(msg); -} - -static DBusMessage *set_subscriber_number(DBusConnection *conn, - DBusMessage *msg, - void *data) -{ - const char *number; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) - return btd_error_invalid_args(msg); - - g_free(subscriber_number); - subscriber_number = g_strdup(number); - - DBG("telephony-dummy: subscriber number set to %s", number); - - return dbus_message_new_method_return(msg); -} - -static const GDBusMethodTable dummy_methods[] = { - { GDBUS_METHOD("OutgoingCall", - GDBUS_ARGS({ "number", "s" }), NULL, - outgoing_call) }, - { GDBUS_METHOD("IncomingCall", - GDBUS_ARGS({ "number", "s" }), NULL, - incoming_call) }, - { GDBUS_METHOD("CancelCall", NULL, NULL, cancel_call) }, - { GDBUS_METHOD("SignalStrength", - GDBUS_ARGS({ "strength", "u" }), NULL, - signal_strength) }, - { GDBUS_METHOD("BatteryLevel", - GDBUS_ARGS({ "level", "u" }), NULL, - battery_level) }, - { GDBUS_METHOD("RoamingStatus", - GDBUS_ARGS({ "roaming", "b" }), NULL, - roaming_status) }, - { GDBUS_METHOD("RegistrationStatus", - GDBUS_ARGS({ "registration", "b" }), NULL, - registration_status) }, - { GDBUS_METHOD("SetSubscriberNumber", - GDBUS_ARGS({ "number", "s" }), NULL, - set_subscriber_number) }, - { } -}; - -static const GDBusSignalTable dummy_signals[] = { - { GDBUS_SIGNAL("VoiceDial", NULL) }, - { } -}; - -int telephony_init(void) -{ - uint32_t features = AG_FEATURE_REJECT_A_CALL | - AG_FEATURE_ENHANCED_CALL_STATUS | - AG_FEATURE_EXTENDED_ERROR_RESULT_CODES; - - DBG(""); - - connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - - if (g_dbus_register_interface(connection, TELEPHONY_DUMMY_PATH, - TELEPHONY_DUMMY_IFACE, - dummy_methods, dummy_signals, - NULL, NULL, NULL) == FALSE) { - error("telephony-dummy interface %s init failed on path %s", - TELEPHONY_DUMMY_IFACE, TELEPHONY_DUMMY_PATH); - return -1; - } - - telephony_ready_ind(features, dummy_indicators, BTRH_NOT_SUPPORTED, - chld_str); - - return 0; -} - -void telephony_exit(void) -{ - DBG(""); - - g_dbus_unregister_interface(connection, TELEPHONY_DUMMY_PATH, - TELEPHONY_DUMMY_IFACE); - dbus_connection_unref(connection); - connection = NULL; - - telephony_deinit(); -} diff -Nru bluez-4.101/audio/telephony.h bluez-5.23/audio/telephony.h --- bluez-4.101/audio/telephony.h 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/audio/telephony.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,244 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include -#include -#include - -/* Response and hold values */ -#define BTRH_NOT_SUPPORTED -2 -#define BTRH_NONE -1 -#define BTRH_HOLD 0 -#define BTRH_ACCEPT 1 -#define BTRH_REJECT 2 - -/* HFP feature bits */ -#define AG_FEATURE_THREE_WAY_CALLING 0x0001 -#define AG_FEATURE_EC_ANDOR_NR 0x0002 -#define AG_FEATURE_VOICE_RECOGNITION 0x0004 -#define AG_FEATURE_INBAND_RINGTONE 0x0008 -#define AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG 0x0010 -#define AG_FEATURE_REJECT_A_CALL 0x0020 -#define AG_FEATURE_ENHANCED_CALL_STATUS 0x0040 -#define AG_FEATURE_ENHANCED_CALL_CONTROL 0x0080 -#define AG_FEATURE_EXTENDED_ERROR_RESULT_CODES 0x0100 - -#define HF_FEATURE_EC_ANDOR_NR 0x0001 -#define HF_FEATURE_CALL_WAITING_AND_3WAY 0x0002 -#define HF_FEATURE_CLI_PRESENTATION 0x0004 -#define HF_FEATURE_VOICE_RECOGNITION 0x0008 -#define HF_FEATURE_REMOTE_VOLUME_CONTROL 0x0010 -#define HF_FEATURE_ENHANCED_CALL_STATUS 0x0020 -#define HF_FEATURE_ENHANCED_CALL_CONTROL 0x0040 - -/* Indicator event values */ -#define EV_SERVICE_NONE 0 -#define EV_SERVICE_PRESENT 1 - -#define EV_CALL_INACTIVE 0 -#define EV_CALL_ACTIVE 1 - -#define EV_CALLSETUP_INACTIVE 0 -#define EV_CALLSETUP_INCOMING 1 -#define EV_CALLSETUP_OUTGOING 2 -#define EV_CALLSETUP_ALERTING 3 - -#define EV_CALLHELD_NONE 0 -#define EV_CALLHELD_MULTIPLE 1 -#define EV_CALLHELD_ON_HOLD 2 - -#define EV_ROAM_INACTIVE 0 -#define EV_ROAM_ACTIVE 1 - -/* Call parameters */ -#define CALL_DIR_OUTGOING 0 -#define CALL_DIR_INCOMING 1 - -#define CALL_STATUS_ACTIVE 0 -#define CALL_STATUS_HELD 1 -#define CALL_STATUS_DIALING 2 -#define CALL_STATUS_ALERTING 3 -#define CALL_STATUS_INCOMING 4 -#define CALL_STATUS_WAITING 5 - -#define CALL_MODE_VOICE 0 -#define CALL_MODE_DATA 1 -#define CALL_MODE_FAX 2 - -#define CALL_MULTIPARTY_NO 0 -#define CALL_MULTIPARTY_YES 1 - -/* Subscriber number parameters */ -#define SUBSCRIBER_SERVICE_VOICE 4 -#define SUBSCRIBER_SERVICE_FAX 5 - -/* Operator selection mode values */ -#define OPERATOR_MODE_AUTO 0 -#define OPERATOR_MODE_MANUAL 1 -#define OPERATOR_MODE_DEREGISTER 2 -#define OPERATOR_MODE_MANUAL_AUTO 4 - -/* Some common number types */ -#define NUMBER_TYPE_UNKNOWN 128 -#define NUMBER_TYPE_TELEPHONY 129 -#define NUMBER_TYPE_INTERNATIONAL 145 -#define NUMBER_TYPE_NATIONAL 161 -#define NUMBER_TYPE_VOIP 255 - -/* Extended Audio Gateway Error Result Codes */ -typedef enum { - CME_ERROR_NONE = -1, - CME_ERROR_AG_FAILURE = 0, - CME_ERROR_NO_PHONE_CONNECTION = 1, - CME_ERROR_NOT_ALLOWED = 3, - CME_ERROR_NOT_SUPPORTED = 4, - CME_ERROR_PH_SIM_PIN_REQUIRED = 5, - CME_ERROR_SIM_NOT_INSERTED = 10, - CME_ERROR_SIM_PIN_REQUIRED = 11, - CME_ERROR_SIM_PUK_REQUIRED = 12, - CME_ERROR_SIM_FAILURE = 13, - CME_ERROR_SIM_BUSY = 14, - CME_ERROR_INCORRECT_PASSWORD = 16, - CME_ERROR_SIM_PIN2_REQUIRED = 17, - CME_ERROR_SIM_PUK2_REQUIRED = 18, - CME_ERROR_MEMORY_FULL = 20, - CME_ERROR_INVALID_INDEX = 21, - CME_ERROR_MEMORY_FAILURE = 23, - CME_ERROR_TEXT_STRING_TOO_LONG = 24, - CME_ERROR_INVALID_TEXT_STRING = 25, - CME_ERROR_DIAL_STRING_TOO_LONG = 26, - CME_ERROR_INVALID_DIAL_STRING = 27, - CME_ERROR_NO_NETWORK_SERVICE = 30, - CME_ERROR_NETWORK_TIMEOUT = 31, - CME_ERROR_NETWORK_NOT_ALLOWED = 32, -} cme_error_t; - -struct indicator { - const char *desc; - const char *range; - int val; - gboolean ignore_redundant; -}; - -/* Notify telephony-*.c of connected/disconnected devices. Implemented by - * telephony-*.c - */ -void telephony_device_connected(void *telephony_device); -void telephony_device_disconnected(void *telephony_device); - -/* HF requests (sent by the handsfree device). These are implemented by - * telephony-*.c - */ -void telephony_event_reporting_req(void *telephony_device, int ind); -void telephony_response_and_hold_req(void *telephony_device, int rh); -void telephony_last_dialed_number_req(void *telephony_device); -void telephony_terminate_call_req(void *telephony_device); -void telephony_answer_call_req(void *telephony_device); -void telephony_dial_number_req(void *telephony_device, const char *number); -void telephony_transmit_dtmf_req(void *telephony_device, char tone); -void telephony_subscriber_number_req(void *telephony_device); -void telephony_list_current_calls_req(void *telephony_device); -void telephony_operator_selection_req(void *telephony_device); -void telephony_call_hold_req(void *telephony_device, const char *cmd); -void telephony_nr_and_ec_req(void *telephony_device, gboolean enable); -void telephony_voice_dial_req(void *telephony_device, gboolean enable); -void telephony_key_press_req(void *telephony_device, const char *keys); - -/* AG responses to HF requests. These are implemented by headset.c */ -int telephony_event_reporting_rsp(void *telephony_device, cme_error_t err); -int telephony_response_and_hold_rsp(void *telephony_device, cme_error_t err); -int telephony_last_dialed_number_rsp(void *telephony_device, cme_error_t err); -int telephony_terminate_call_rsp(void *telephony_device, cme_error_t err); -int telephony_answer_call_rsp(void *telephony_device, cme_error_t err); -int telephony_dial_number_rsp(void *telephony_device, cme_error_t err); -int telephony_transmit_dtmf_rsp(void *telephony_device, cme_error_t err); -int telephony_subscriber_number_rsp(void *telephony_device, cme_error_t err); -int telephony_list_current_calls_rsp(void *telephony_device, cme_error_t err); -int telephony_operator_selection_rsp(void *telephony_device, cme_error_t err); -int telephony_call_hold_rsp(void *telephony_device, cme_error_t err); -int telephony_nr_and_ec_rsp(void *telephony_device, cme_error_t err); -int telephony_voice_dial_rsp(void *telephony_device, cme_error_t err); -int telephony_key_press_rsp(void *telephony_device, cme_error_t err); - -/* Event indications by AG. These are implemented by headset.c */ -int telephony_event_ind(int index); -int telephony_response_and_hold_ind(int rh); -int telephony_incoming_call_ind(const char *number, int type); -int telephony_calling_stopped_ind(void); -int telephony_ready_ind(uint32_t features, const struct indicator *indicators, - int rh, const char *chld); -int telephony_deinit(void); -int telephony_list_current_call_ind(int idx, int dir, int status, int mode, - int mprty, const char *number, - int type); -int telephony_subscriber_number_ind(const char *number, int type, - int service); -int telephony_call_waiting_ind(const char *number, int type); -int telephony_operator_selection_ind(int mode, const char *oper); - -/* Helper function for quick indicator updates */ -static inline int telephony_update_indicator(struct indicator *indicators, - const char *desc, - int new_val) -{ - int i; - struct indicator *ind = NULL; - - for (i = 0; indicators[i].desc != NULL; i++) { - if (g_str_equal(indicators[i].desc, desc)) { - ind = &indicators[i]; - break; - } - } - - if (!ind) - return -ENOENT; - - DBG("Telephony indicator \"%s\" %d->%d", desc, ind->val, new_val); - - if (ind->ignore_redundant && ind->val == new_val) { - DBG("Ignoring no-change indication"); - return 0; - } - - ind->val = new_val; - - return telephony_event_ind(i); -} - -static inline int telephony_get_indicator(const struct indicator *indicators, - const char *desc) -{ - int i; - - for (i = 0; indicators[i].desc != NULL; i++) { - if (g_str_equal(indicators[i].desc, desc)) - return indicators[i].val; - } - - return -ENOENT; -} - -int telephony_init(void); -void telephony_exit(void); diff -Nru bluez-4.101/audio/telephony-maemo5.c bluez-5.23/audio/telephony-maemo5.c --- bluez-4.101/audio/telephony-maemo5.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/telephony-maemo5.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2105 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2008-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "telephony.h" -#include "error.h" - -/* SSC D-Bus definitions */ -#define SSC_DBUS_NAME "com.nokia.phone.SSC" -#define SSC_DBUS_IFACE "com.nokia.phone.SSC" -#define SSC_DBUS_PATH "/com/nokia/phone/SSC" - -/* libcsnet D-Bus definitions */ -#define NETWORK_BUS_NAME "com.nokia.phone.net" -#define NETWORK_INTERFACE "Phone.Net" -#define NETWORK_PATH "/com/nokia/phone/net" - -/* Mask bits for supported services */ -#define NETWORK_MASK_GPRS_SUPPORT 0x01 -#define NETWORK_MASK_CS_SERVICES 0x02 -#define NETWORK_MASK_EGPRS_SUPPORT 0x04 -#define NETWORK_MASK_HSDPA_AVAIL 0x08 -#define NETWORK_MASK_HSUPA_AVAIL 0x10 - -/* network get cell info: cell type */ -#define NETWORK_UNKNOWN_CELL 0 -#define NETWORK_GSM_CELL 1 -#define NETWORK_WCDMA_CELL 2 - -enum net_registration_status { - NETWORK_REG_STATUS_HOME = 0x00, - NETWORK_REG_STATUS_ROAM, - NETWORK_REG_STATUS_ROAM_BLINK, - NETWORK_REG_STATUS_NOSERV, - NETWORK_REG_STATUS_NOSERV_SEARCHING, - NETWORK_REG_STATUS_NOSERV_NOTSEARCHING, - NETWORK_REG_STATUS_NOSERV_NOSIM, - NETWORK_REG_STATUS_POWER_OFF = 0x08, - NETWORK_REG_STATUS_NSPS, - NETWORK_REG_STATUS_NSPS_NO_COVERAGE, - NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW -}; - -enum network_types { - NETWORK_GSM_HOME_PLMN = 0, - NETWORK_GSM_PREFERRED_PLMN, - NETWORK_GSM_FORBIDDEN_PLMN, - NETWORK_GSM_OTHER_PLMN, - NETWORK_GSM_NO_PLMN_AVAIL -}; - -enum network_alpha_tag_name_type { - NETWORK_HARDCODED_LATIN_OPER_NAME = 0, - NETWORK_HARDCODED_USC2_OPER_NAME, - NETWORK_NITZ_SHORT_OPER_NAME, - NETWORK_NITZ_FULL_OPER_NAME, -}; - -#define TELEPHONY_MAEMO_PATH "/com/nokia/MaemoTelephony" -#define TELEPHONY_MAEMO_INTERFACE "com.nokia.MaemoTelephony" - -#define CALLERID_BASE "/var/lib/bluetooth/maemo-callerid-" -#define ALLOWED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-allowed" -#define RESTRICTED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-restricted" -#define NONE_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-none" - -static uint32_t callerid = 0; - -/* CSD CALL plugin D-Bus definitions */ -#define CSD_CALL_BUS_NAME "com.nokia.csd.Call" -#define CSD_CALL_INTERFACE "com.nokia.csd.Call" -#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance" -#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference" -#define CSD_CALL_PATH "/com/nokia/csd/call" -#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference" - -/* Call status values as exported by the CSD CALL plugin */ -#define CSD_CALL_STATUS_IDLE 0 -#define CSD_CALL_STATUS_CREATE 1 -#define CSD_CALL_STATUS_COMING 2 -#define CSD_CALL_STATUS_PROCEEDING 3 -#define CSD_CALL_STATUS_MO_ALERTING 4 -#define CSD_CALL_STATUS_MT_ALERTING 5 -#define CSD_CALL_STATUS_WAITING 6 -#define CSD_CALL_STATUS_ANSWERED 7 -#define CSD_CALL_STATUS_ACTIVE 8 -#define CSD_CALL_STATUS_MO_RELEASE 9 -#define CSD_CALL_STATUS_MT_RELEASE 10 -#define CSD_CALL_STATUS_HOLD_INITIATED 11 -#define CSD_CALL_STATUS_HOLD 12 -#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13 -#define CSD_CALL_STATUS_RECONNECT_PENDING 14 -#define CSD_CALL_STATUS_TERMINATED 15 -#define CSD_CALL_STATUS_SWAP_INITIATED 16 - -#define CALL_FLAG_NONE 0 -#define CALL_FLAG_PRESENTATION_ALLOWED 0x01 -#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02 - -/* SIM Phonebook D-Bus definitions */ -#define SIM_PHONEBOOK_BUS_NAME "com.nokia.phone.SIM" -#define SIM_PHONEBOOK_INTERFACE "Phone.Sim.Phonebook" -#define SIM_PHONEBOOK_PATH "/com/nokia/phone/SIM/phonebook" - -#define PHONEBOOK_INDEX_FIRST_ENTRY 0xFFFF -#define PHONEBOOK_INDEX_NEXT_FREE_LOCATION 0xFFFE - -enum sim_phonebook_type { - SIM_PHONEBOOK_TYPE_ADN = 0x0, - SIM_PHONEBOOK_TYPE_SDN, - SIM_PHONEBOOK_TYPE_FDN, - SIM_PHONEBOOK_TYPE_VMBX, - SIM_PHONEBOOK_TYPE_MBDN, - SIM_PHONEBOOK_TYPE_EN, - SIM_PHONEBOOK_TYPE_MSISDN -}; - -enum sim_phonebook_location_type { - SIM_PHONEBOOK_LOCATION_EXACT = 0x0, - SIM_PHONEBOOK_LOCATION_NEXT -}; - -struct csd_call { - char *object_path; - int status; - gboolean originating; - gboolean emergency; - gboolean on_hold; - gboolean conference; - char *number; - gboolean setup; -}; - -static struct { - uint8_t status; - uint16_t lac; - uint32_t cell_id; - uint32_t operator_code; - uint32_t country_code; - uint8_t network_type; - uint8_t supported_services; - uint16_t signals_bar; - char *operator_name; -} net = { - .status = NETWORK_REG_STATUS_NOSERV, - .lac = 0, - .cell_id = 0, - .operator_code = 0, - .country_code = 0, - .network_type = NETWORK_GSM_NO_PLMN_AVAIL, - .supported_services = 0, - .signals_bar = 0, - .operator_name = NULL, -}; - -static DBusConnection *connection = NULL; - -static GSList *calls = NULL; - -/* Reference count for determining the call indicator status */ -static GSList *active_calls = NULL; - -static char *msisdn = NULL; /* Subscriber number */ -static char *vmbx = NULL; /* Voice mailbox number */ - -/* HAL battery namespace key values */ -static int battchg_cur = -1; /* "battery.charge_level.current" */ -static int battchg_last = -1; /* "battery.charge_level.last_full" */ -static int battchg_design = -1; /* "battery.charge_level.design" */ - -static gboolean get_calls_active = FALSE; - -static gboolean events_enabled = FALSE; - -/* Supported set of call hold operations */ -static const char *chld_str = "0,1,1x,2,2x,3,4"; - -static char *last_dialed_number = NULL; - -/* Timer for tracking call creation requests */ -static guint create_request_timer = 0; - -static struct indicator maemo_indicators[] = -{ - { "battchg", "0-5", 5, TRUE }, - { "signal", "0-5", 0, TRUE }, - { "service", "0,1", 0, TRUE }, - { "call", "0,1", 0, TRUE }, - { "callsetup", "0-3", 0, TRUE }, - { "callheld", "0-2", 0, FALSE }, - { "roam", "0,1", 0, TRUE }, - { NULL } -}; - -static char *call_status_str[] = { - "IDLE", - "CREATE", - "COMING", - "PROCEEDING", - "MO_ALERTING", - "MT_ALERTING", - "WAITING", - "ANSWERED", - "ACTIVE", - "MO_RELEASE", - "MT_RELEASE", - "HOLD_INITIATED", - "HOLD", - "RETRIEVE_INITIATED", - "RECONNECT_PENDING", - "TERMINATED", - "SWAP_INITIATED", - "???" -}; - -static struct csd_call *find_call(const char *path) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (g_str_equal(call->object_path, path)) - return call; - } - - return NULL; -} - -static struct csd_call *find_non_held_call(void) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (call->status == CSD_CALL_STATUS_IDLE) - continue; - - if (call->status != CSD_CALL_STATUS_HOLD) - return call; - } - - return NULL; -} - -static struct csd_call *find_non_idle_call(void) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (call->status != CSD_CALL_STATUS_IDLE) - return call; - } - - return NULL; -} - -static struct csd_call *find_call_with_status(int status) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (call->status == status) - return call; - } - - return NULL; -} - -static int release_conference(void) -{ - DBusMessage *msg; - - DBG("telephony-maemo: releasing conference call"); - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, - CSD_CALL_CONFERENCE_PATH, - CSD_CALL_INSTANCE, - "Release"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int release_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, - call->object_path, - CSD_CALL_INSTANCE, - "Release"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int answer_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, - call->object_path, - CSD_CALL_INSTANCE, - "Answer"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int split_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, - call->object_path, - CSD_CALL_INSTANCE, - "Split"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int unhold_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Unhold"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int hold_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Hold"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int swap_calls(void) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Swap"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int create_conference(void) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Conference"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int call_transfer(void) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Transfer"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int number_type(const char *number) -{ - if (number == NULL) - return NUMBER_TYPE_TELEPHONY; - - if (number[0] == '+' || strncmp(number, "00", 2) == 0) - return NUMBER_TYPE_INTERNATIONAL; - - return NUMBER_TYPE_TELEPHONY; -} - -void telephony_device_connected(void *telephony_device) -{ - struct csd_call *coming; - - DBG("telephony-maemo: device %p connected", telephony_device); - - coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); - if (coming) { - if (find_call_with_status(CSD_CALL_STATUS_ACTIVE)) - telephony_call_waiting_ind(coming->number, - number_type(coming->number)); - else - telephony_incoming_call_ind(coming->number, - number_type(coming->number)); - } -} - -void telephony_device_disconnected(void *telephony_device) -{ - DBG("telephony-maemo: device %p disconnected", telephony_device); - events_enabled = FALSE; -} - -void telephony_event_reporting_req(void *telephony_device, int ind) -{ - events_enabled = ind == 1 ? TRUE : FALSE; - - telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_response_and_hold_req(void *telephony_device, int rh) -{ - telephony_response_and_hold_rsp(telephony_device, - CME_ERROR_NOT_SUPPORTED); -} - -void telephony_last_dialed_number_req(void *telephony_device) -{ - DBG("telephony-maemo: last dialed number request"); - - if (last_dialed_number) - telephony_dial_number_req(telephony_device, - last_dialed_number); - else - telephony_last_dialed_number_rsp(telephony_device, - CME_ERROR_NOT_ALLOWED); -} - -void telephony_terminate_call_req(void *telephony_device) -{ - struct csd_call *call; - int err; - - call = find_call_with_status(CSD_CALL_STATUS_ACTIVE); - if (!call) - call = find_non_idle_call(); - - if (!call) { - error("No active call"); - telephony_terminate_call_rsp(telephony_device, - CME_ERROR_NOT_ALLOWED); - return; - } - - if (call->conference) - err = release_conference(); - else - err = release_call(call); - - if (err < 0) - telephony_terminate_call_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_answer_call_req(void *telephony_device) -{ - struct csd_call *call; - - call = find_call_with_status(CSD_CALL_STATUS_COMING); - if (!call) - call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); - - if (!call) - call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING); - - if (!call) - call = find_call_with_status(CSD_CALL_STATUS_WAITING); - - if (!call) { - telephony_answer_call_rsp(telephony_device, - CME_ERROR_NOT_ALLOWED); - return; - } - - if (answer_call(call) < 0) - telephony_answer_call_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE); -} - -static int send_method_call(const char *dest, const char *path, - const char *interface, const char *method, - DBusPendingCallNotifyFunction cb, - void *user_data, int type, ...) -{ - DBusMessage *msg; - DBusPendingCall *call; - va_list args; - - msg = dbus_message_new_method_call(dest, path, interface, method); - if (!msg) { - error("Unable to allocate new D-Bus %s message", method); - return -ENOMEM; - } - - va_start(args, type); - - if (!dbus_message_append_args_valist(msg, type, args)) { - dbus_message_unref(msg); - va_end(args); - return -EIO; - } - - va_end(args); - - if (!cb) { - g_dbus_send_message(connection, msg); - return 0; - } - - if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) { - error("Sending %s failed", method); - dbus_message_unref(msg); - return -EIO; - } - - dbus_pending_call_set_notify(call, cb, user_data, NULL); - dbus_pending_call_unref(call); - dbus_message_unref(msg); - - return 0; -} - -static const char *memory_dial_lookup(int location) -{ - if (location == 1) - return vmbx; - else - return NULL; -} - -void telephony_dial_number_req(void *telephony_device, const char *number) -{ - uint32_t flags = callerid; - int ret; - - DBG("telephony-maemo: dial request to %s", number); - - if (strncmp(number, "*31#", 4) == 0) { - number += 4; - flags = CALL_FLAG_PRESENTATION_ALLOWED; - } else if (strncmp(number, "#31#", 4) == 0) { - number += 4; - flags = CALL_FLAG_PRESENTATION_RESTRICTED; - } else if (number[0] == '>') { - const char *location = &number[1]; - - number = memory_dial_lookup(strtol(&number[1], NULL, 0)); - if (!number) { - error("No number at memory location %s", location); - telephony_dial_number_rsp(telephony_device, - CME_ERROR_INVALID_INDEX); - return; - } - } - - ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, "CreateWith", - NULL, NULL, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_UINT32, &flags, - DBUS_TYPE_INVALID); - if (ret < 0) { - telephony_dial_number_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - return; - } - - telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_transmit_dtmf_req(void *telephony_device, char tone) -{ - int ret; - char buf[2] = { tone, '\0' }, *buf_ptr = buf; - - DBG("telephony-maemo: transmit dtmf: %s", buf); - - ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, "SendDTMF", - NULL, NULL, - DBUS_TYPE_STRING, &buf_ptr, - DBUS_TYPE_INVALID); - if (ret < 0) { - telephony_transmit_dtmf_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - return; - } - - telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_subscriber_number_req(void *telephony_device) -{ - DBG("telephony-maemo: subscriber number request"); - if (msisdn) - telephony_subscriber_number_ind(msisdn, - number_type(msisdn), - SUBSCRIBER_SERVICE_VOICE); - telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE); -} - -static int csd_status_to_hfp(struct csd_call *call) -{ - switch (call->status) { - case CSD_CALL_STATUS_IDLE: - case CSD_CALL_STATUS_MO_RELEASE: - case CSD_CALL_STATUS_MT_RELEASE: - case CSD_CALL_STATUS_TERMINATED: - return -1; - case CSD_CALL_STATUS_CREATE: - return CALL_STATUS_DIALING; - case CSD_CALL_STATUS_WAITING: - return CALL_STATUS_WAITING; - case CSD_CALL_STATUS_PROCEEDING: - /* PROCEEDING can happen in outgoing/incoming */ - if (call->originating) - return CALL_STATUS_DIALING; - else - return CALL_STATUS_INCOMING; - case CSD_CALL_STATUS_COMING: - return CALL_STATUS_INCOMING; - case CSD_CALL_STATUS_MO_ALERTING: - return CALL_STATUS_ALERTING; - case CSD_CALL_STATUS_MT_ALERTING: - return CALL_STATUS_INCOMING; - case CSD_CALL_STATUS_ANSWERED: - case CSD_CALL_STATUS_ACTIVE: - case CSD_CALL_STATUS_RECONNECT_PENDING: - case CSD_CALL_STATUS_SWAP_INITIATED: - case CSD_CALL_STATUS_HOLD_INITIATED: - return CALL_STATUS_ACTIVE; - case CSD_CALL_STATUS_RETRIEVE_INITIATED: - case CSD_CALL_STATUS_HOLD: - return CALL_STATUS_HELD; - default: - return -1; - } -} - -void telephony_list_current_calls_req(void *telephony_device) -{ - GSList *l; - int i; - - DBG("telephony-maemo: list current calls request"); - - for (l = calls, i = 1; l != NULL; l = l->next, i++) { - struct csd_call *call = l->data; - int status, direction, multiparty; - - status = csd_status_to_hfp(call); - if (status < 0) - continue; - - direction = call->originating ? - CALL_DIR_OUTGOING : CALL_DIR_INCOMING; - - multiparty = call->conference ? - CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO; - - telephony_list_current_call_ind(i, direction, status, - CALL_MODE_VOICE, multiparty, - call->number, - number_type(call->number)); - } - - telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_operator_selection_req(void *telephony_device) -{ - telephony_operator_selection_ind(OPERATOR_MODE_AUTO, - net.operator_name ? net.operator_name : ""); - telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE); -} - -static void foreach_call_with_status(int status, - int (*func)(struct csd_call *call)) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (call->status == status) - func(call); - } -} - -void telephony_call_hold_req(void *telephony_device, const char *cmd) -{ - const char *idx; - struct csd_call *call; - int err = 0; - - DBG("telephony-maemo: got call hold request %s", cmd); - - if (strlen(cmd) > 1) - idx = &cmd[1]; - else - idx = NULL; - - if (idx) - call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1); - else - call = NULL; - - switch (cmd[0]) { - case '0': - foreach_call_with_status(CSD_CALL_STATUS_HOLD, release_call); - foreach_call_with_status(CSD_CALL_STATUS_WAITING, - release_call); - break; - case '1': - if (idx) { - if (call) - err = release_call(call); - break; - } - foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call); - call = find_call_with_status(CSD_CALL_STATUS_WAITING); - if (call) - err = answer_call(call); - break; - case '2': - if (idx) { - if (call) - err = split_call(call); - } else { - struct csd_call *held, *wait; - - call = find_call_with_status(CSD_CALL_STATUS_ACTIVE); - held = find_call_with_status(CSD_CALL_STATUS_HOLD); - wait = find_call_with_status(CSD_CALL_STATUS_WAITING); - - if (wait) - err = answer_call(wait); - else if (call && held) - err = swap_calls(); - else { - if (call) - err = hold_call(call); - if (held) - err = unhold_call(held); - } - } - break; - case '3': - if (find_call_with_status(CSD_CALL_STATUS_HOLD) || - find_call_with_status(CSD_CALL_STATUS_WAITING)) - err = create_conference(); - break; - case '4': - err = call_transfer(); - break; - default: - DBG("Unknown call hold request"); - break; - } - - if (err) - telephony_call_hold_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_nr_and_ec_req(void *telephony_device, gboolean enable) -{ - DBG("telephony-maemo: got %s NR and EC request", - enable ? "enable" : "disable"); - telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_key_press_req(void *telephony_device, const char *keys) -{ - struct csd_call *active, *waiting; - int err; - - DBG("telephony-maemo: got key press request for %s", keys); - - waiting = find_call_with_status(CSD_CALL_STATUS_COMING); - if (!waiting) - waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); - if (!waiting) - waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING); - - active = find_call_with_status(CSD_CALL_STATUS_ACTIVE); - - if (waiting) - err = answer_call(waiting); - else if (active) - err = release_call(active); - else - err = 0; - - if (err < 0) - telephony_key_press_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_key_press_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_voice_dial_req(void *telephony_device, gboolean enable) -{ - DBG("telephony-maemo: got %s voice dial request", - enable ? "enable" : "disable"); - - telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED); -} - -static void handle_incoming_call(DBusMessage *msg) -{ - const char *number, *call_path; - struct csd_call *call; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &call_path, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in Call.Coming() signal"); - return; - } - - call = find_call(call_path); - if (!call) { - error("Didn't find any matching call object for %s", - call_path); - return; - } - - DBG("Incoming call to %s from number %s", call_path, number); - - g_free(call->number); - call->number = g_strdup(number); - - telephony_update_indicator(maemo_indicators, "callsetup", - EV_CALLSETUP_INCOMING); - - if (find_call_with_status(CSD_CALL_STATUS_ACTIVE)) - telephony_call_waiting_ind(call->number, - number_type(call->number)); - else - telephony_incoming_call_ind(call->number, - number_type(call->number)); -} - -static void handle_outgoing_call(DBusMessage *msg) -{ - const char *number, *call_path; - struct csd_call *call; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &call_path, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in Call.Created() signal"); - return; - } - - call = find_call(call_path); - if (!call) { - error("Didn't find any matching call object for %s", - call_path); - return; - } - - DBG("Outgoing call from %s to number %s", call_path, number); - - g_free(call->number); - call->number = g_strdup(number); - - g_free(last_dialed_number); - last_dialed_number = g_strdup(number); - - if (create_request_timer) { - g_source_remove(create_request_timer); - create_request_timer = 0; - } -} - -static gboolean create_timeout(gpointer user_data) -{ - telephony_update_indicator(maemo_indicators, "callsetup", - EV_CALLSETUP_INACTIVE); - create_request_timer = 0; - return FALSE; -} - -static void handle_create_requested(DBusMessage *msg) -{ - DBG("Call.CreateRequested()"); - - if (create_request_timer) - g_source_remove(create_request_timer); - - create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL); - - telephony_update_indicator(maemo_indicators, "callsetup", - EV_CALLSETUP_OUTGOING); -} - -static void handle_call_status(DBusMessage *msg, const char *call_path) -{ - struct csd_call *call; - dbus_uint32_t status, cause_type, cause; - int callheld = telephony_get_indicator(maemo_indicators, "callheld"); - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_UINT32, &status, - DBUS_TYPE_UINT32, &cause_type, - DBUS_TYPE_UINT32, &cause, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in Instance.CallStatus() signal"); - return; - } - - call = find_call(call_path); - if (!call) { - error("Didn't find any matching call object for %s", - call_path); - return; - } - - if (status > 16) { - error("Invalid call status %u", status); - return; - } - - DBG("Call %s changed from %s to %s", call_path, - call_status_str[call->status], call_status_str[status]); - - if (call->status == (int) status) { - DBG("Ignoring CSD Call state change to existing state"); - return; - } - - call->status = (int) status; - - switch (status) { - case CSD_CALL_STATUS_IDLE: - if (call->setup) { - telephony_update_indicator(maemo_indicators, - "callsetup", - EV_CALLSETUP_INACTIVE); - if (!call->originating) - telephony_calling_stopped_ind(); - } - - g_free(call->number); - call->number = NULL; - call->originating = FALSE; - call->emergency = FALSE; - call->on_hold = FALSE; - call->conference = FALSE; - call->setup = FALSE; - break; - case CSD_CALL_STATUS_CREATE: - call->originating = TRUE; - call->setup = TRUE; - break; - case CSD_CALL_STATUS_COMING: - call->originating = FALSE; - call->setup = TRUE; - break; - case CSD_CALL_STATUS_PROCEEDING: - break; - case CSD_CALL_STATUS_MO_ALERTING: - telephony_update_indicator(maemo_indicators, "callsetup", - EV_CALLSETUP_ALERTING); - break; - case CSD_CALL_STATUS_MT_ALERTING: - break; - case CSD_CALL_STATUS_WAITING: - break; - case CSD_CALL_STATUS_ANSWERED: - break; - case CSD_CALL_STATUS_ACTIVE: - if (call->on_hold) { - call->on_hold = FALSE; - if (find_call_with_status(CSD_CALL_STATUS_HOLD)) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_MULTIPLE); - else - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_NONE); - } else { - if (!g_slist_find(active_calls, call)) - active_calls = g_slist_prepend(active_calls, call); - if (g_slist_length(active_calls) == 1) - telephony_update_indicator(maemo_indicators, - "call", - EV_CALL_ACTIVE); - /* Upgrade callheld status if necessary */ - if (callheld == EV_CALLHELD_ON_HOLD) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_MULTIPLE); - telephony_update_indicator(maemo_indicators, - "callsetup", - EV_CALLSETUP_INACTIVE); - if (!call->originating) - telephony_calling_stopped_ind(); - call->setup = FALSE; - } - break; - case CSD_CALL_STATUS_MO_RELEASE: - case CSD_CALL_STATUS_MT_RELEASE: - active_calls = g_slist_remove(active_calls, call); - if (g_slist_length(active_calls) == 0) - telephony_update_indicator(maemo_indicators, "call", - EV_CALL_INACTIVE); - break; - case CSD_CALL_STATUS_HOLD_INITIATED: - break; - case CSD_CALL_STATUS_HOLD: - call->on_hold = TRUE; - if (find_non_held_call()) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_MULTIPLE); - else - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_ON_HOLD); - break; - case CSD_CALL_STATUS_RETRIEVE_INITIATED: - break; - case CSD_CALL_STATUS_RECONNECT_PENDING: - break; - case CSD_CALL_STATUS_TERMINATED: - if (call->on_hold && - !find_call_with_status(CSD_CALL_STATUS_HOLD)) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_NONE); - else if (callheld == EV_CALLHELD_MULTIPLE && - find_call_with_status(CSD_CALL_STATUS_HOLD)) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_ON_HOLD); - break; - case CSD_CALL_STATUS_SWAP_INITIATED: - break; - default: - error("Unknown call status %u", status); - break; - } -} - -static void handle_conference(DBusMessage *msg, gboolean joined) -{ - const char *path; - struct csd_call *call; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in Conference.%s", - dbus_message_get_member(msg)); - return; - } - - call = find_call(path); - if (!call) { - error("Conference signal for unknown call %s", path); - return; - } - - DBG("Call %s %s the conference", path, joined ? "joined" : "left"); - - call->conference = joined; -} - -static void get_operator_name_reply(DBusPendingCall *pending_call, - void *user_data) -{ - DBusMessage *reply; - DBusError err; - const char *name; - dbus_int32_t net_err; - - reply = dbus_pending_call_steal_reply(pending_call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("get_operator_name failed: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_error_init(&err); - if (!dbus_message_get_args(reply, &err, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INT32, &net_err, - DBUS_TYPE_INVALID)) { - error("Unexpected get_operator_name reply parameters: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - if (net_err != 0) { - error("get_operator_name failed with code %d", net_err); - goto done; - } - - if (strlen(name) == 0) - goto done; - - g_free(net.operator_name); - net.operator_name = g_strdup(name); - - DBG("telephony-maemo: operator name updated: %s", name); - -done: - dbus_message_unref(reply); -} - -static void resolve_operator_name(uint32_t operator, uint32_t country) -{ - uint8_t name_type = NETWORK_HARDCODED_LATIN_OPER_NAME; - - send_method_call(NETWORK_BUS_NAME, NETWORK_PATH, - NETWORK_INTERFACE, "get_operator_name", - get_operator_name_reply, NULL, - DBUS_TYPE_BYTE, &name_type, - DBUS_TYPE_UINT32, &operator, - DBUS_TYPE_UINT32, &country, - DBUS_TYPE_INVALID); -} - -static void update_registration_status(uint8_t status, uint16_t lac, - uint32_t cell_id, - uint32_t operator_code, - uint32_t country_code, - uint8_t network_type, - uint8_t supported_services) -{ - if (net.status != status) { - switch (status) { - case NETWORK_REG_STATUS_HOME: - telephony_update_indicator(maemo_indicators, "roam", - EV_ROAM_INACTIVE); - if (net.status >= NETWORK_REG_STATUS_NOSERV) - telephony_update_indicator(maemo_indicators, - "service", - EV_SERVICE_PRESENT); - break; - case NETWORK_REG_STATUS_ROAM: - case NETWORK_REG_STATUS_ROAM_BLINK: - telephony_update_indicator(maemo_indicators, "roam", - EV_ROAM_ACTIVE); - if (net.status >= NETWORK_REG_STATUS_NOSERV) - telephony_update_indicator(maemo_indicators, - "service", - EV_SERVICE_PRESENT); - break; - case NETWORK_REG_STATUS_NOSERV: - case NETWORK_REG_STATUS_NOSERV_SEARCHING: - case NETWORK_REG_STATUS_NOSERV_NOTSEARCHING: - case NETWORK_REG_STATUS_NOSERV_NOSIM: - case NETWORK_REG_STATUS_POWER_OFF: - case NETWORK_REG_STATUS_NSPS: - case NETWORK_REG_STATUS_NSPS_NO_COVERAGE: - case NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW: - if (net.status < NETWORK_REG_STATUS_NOSERV) - telephony_update_indicator(maemo_indicators, - "service", - EV_SERVICE_NONE); - break; - } - - net.status = status; - } - - net.lac = lac; - net.cell_id = cell_id; - - if (net.operator_code != operator_code || - net.country_code != country_code) { - g_free(net.operator_name); - net.operator_name = NULL; - resolve_operator_name(operator_code, country_code); - net.operator_code = operator_code; - net.country_code = country_code; - } - - net.network_type = network_type; - net.supported_services = supported_services; -} - -static void handle_registration_status_change(DBusMessage *msg) -{ - uint8_t status; - dbus_uint16_t lac, network_type, supported_services; - dbus_uint32_t cell_id, operator_code, country_code; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_BYTE, &status, - DBUS_TYPE_UINT16, &lac, - DBUS_TYPE_UINT32, &cell_id, - DBUS_TYPE_UINT32, &operator_code, - DBUS_TYPE_UINT32, &country_code, - DBUS_TYPE_BYTE, &network_type, - DBUS_TYPE_BYTE, &supported_services, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in registration_status_change"); - return; - } - - update_registration_status(status, lac, cell_id, operator_code, - country_code, network_type, - supported_services); -} - -static void update_signal_strength(uint8_t signals_bar) -{ - int signal; - - if (signals_bar > 100) { - DBG("signals_bar greater than expected: %u", signals_bar); - signals_bar = 100; - } - - if (net.signals_bar == signals_bar) - return; - - /* A simple conversion from 0-100 to 0-5 (used by HFP) */ - signal = (signals_bar + 20) / 21; - - telephony_update_indicator(maemo_indicators, "signal", signal); - - net.signals_bar = signals_bar; - - DBG("Signal strength updated: %u/100, %d/5", signals_bar, signal); -} - -static void handle_signal_strength_change(DBusMessage *msg) -{ - uint8_t signals_bar, rssi_in_dbm; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_BYTE, &signals_bar, - DBUS_TYPE_BYTE, &rssi_in_dbm, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in signal_strength_change"); - return; - } - - update_signal_strength(signals_bar); -} - -static gboolean iter_get_basic_args(DBusMessageIter *iter, - int first_arg_type, ...) -{ - int type; - va_list ap; - - va_start(ap, first_arg_type); - - for (type = first_arg_type; type != DBUS_TYPE_INVALID; - type = va_arg(ap, int)) { - void *value = va_arg(ap, void *); - int real_type = dbus_message_iter_get_arg_type(iter); - - if (real_type != type) { - error("iter_get_basic_args: expected %c but got %c", - (char) type, (char) real_type); - break; - } - - dbus_message_iter_get_basic(iter, value); - dbus_message_iter_next(iter); - } - - va_end(ap); - - return type == DBUS_TYPE_INVALID ? TRUE : FALSE; -} - -static void hal_battery_level_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - dbus_int32_t level; - int *value = user_data; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("hald replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_error_init(&err); - if (dbus_message_get_args(reply, &err, - DBUS_TYPE_INT32, &level, - DBUS_TYPE_INVALID) == FALSE) { - error("Unable to parse GetPropertyInteger reply: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - *value = (int) level; - - if (value == &battchg_last) - DBG("telephony-maemo: battery.charge_level.last_full is %d", - *value); - else if (value == &battchg_design) - DBG("telephony-maemo: battery.charge_level.design is %d", - *value); - else - DBG("telephony-maemo: battery.charge_level.current is %d", - *value); - - if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) { - int new, max; - - if (battchg_last > 0) - max = battchg_last; - else - max = battchg_design; - - new = battchg_cur * 5 / max; - - telephony_update_indicator(maemo_indicators, "battchg", new); - } -done: - dbus_message_unref(reply); -} - -static void hal_get_integer(const char *path, const char *key, void *user_data) -{ - send_method_call("org.freedesktop.Hal", path, - "org.freedesktop.Hal.Device", - "GetPropertyInteger", - hal_battery_level_reply, user_data, - DBUS_TYPE_STRING, &key, - DBUS_TYPE_INVALID); -} - -static void handle_hal_property_modified(DBusMessage *msg) -{ - DBusMessageIter iter, array; - dbus_int32_t num_changes; - const char *path; - - path = dbus_message_get_path(msg); - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) { - error("Unexpected signature in hal PropertyModified signal"); - return; - } - - dbus_message_iter_get_basic(&iter, &num_changes); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature in hal PropertyModified signal"); - return; - } - - dbus_message_iter_recurse(&iter, &array); - - while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) { - DBusMessageIter prop; - const char *name; - dbus_bool_t added, removed; - - dbus_message_iter_recurse(&array, &prop); - - if (!iter_get_basic_args(&prop, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &added, - DBUS_TYPE_BOOLEAN, &removed, - DBUS_TYPE_INVALID)) { - error("Invalid hal PropertyModified parameters"); - break; - } - - if (g_str_equal(name, "battery.charge_level.last_full")) - hal_get_integer(path, name, &battchg_last); - else if (g_str_equal(name, "battery.charge_level.current")) - hal_get_integer(path, name, &battchg_cur); - else if (g_str_equal(name, "battery.charge_level.design")) - hal_get_integer(path, name, &battchg_design); - - dbus_message_iter_next(&array); - } -} - -static void csd_call_free(struct csd_call *call) -{ - if (!call) - return; - - g_free(call->object_path); - g_free(call->number); - - g_free(call); -} - -static void parse_call_list(DBusMessageIter *iter) -{ - do { - DBusMessageIter call_iter; - struct csd_call *call; - const char *object_path, *number; - dbus_uint32_t status; - dbus_bool_t originating, terminating, emerg, on_hold, conf; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) { - error("Unexpected signature in GetCallInfoAll reply"); - break; - } - - dbus_message_iter_recurse(iter, &call_iter); - - if (!iter_get_basic_args(&call_iter, - DBUS_TYPE_OBJECT_PATH, &object_path, - DBUS_TYPE_UINT32, &status, - DBUS_TYPE_BOOLEAN, &originating, - DBUS_TYPE_BOOLEAN, &terminating, - DBUS_TYPE_BOOLEAN, &emerg, - DBUS_TYPE_BOOLEAN, &on_hold, - DBUS_TYPE_BOOLEAN, &conf, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) { - error("Parsing call D-Bus parameters failed"); - break; - } - - call = find_call(object_path); - if (!call) { - call = g_new0(struct csd_call, 1); - call->object_path = g_strdup(object_path); - call->status = (int) status; - calls = g_slist_append(calls, call); - DBG("telephony-maemo: new csd call instance at %s", - object_path); - } - - if (call->status == CSD_CALL_STATUS_IDLE) - continue; - - /* CSD gives incorrect call_hold property sometimes */ - if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) || - (call->status == CSD_CALL_STATUS_HOLD && - !on_hold)) { - error("Conflicting call status and on_hold property!"); - on_hold = call->status == CSD_CALL_STATUS_HOLD; - } - - call->originating = originating; - call->on_hold = on_hold; - call->conference = conf; - g_free(call->number); - call->number = g_strdup(number); - - } while (dbus_message_iter_next(iter)); -} - -static void signal_strength_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - uint8_t signals_bar, rssi_in_dbm; - dbus_int32_t net_err; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("Unable to get signal strength: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_error_init(&err); - if (!dbus_message_get_args(reply, &err, - DBUS_TYPE_BYTE, &signals_bar, - DBUS_TYPE_BYTE, &rssi_in_dbm, - DBUS_TYPE_INT32, &net_err, - DBUS_TYPE_INVALID)) { - error("Unable to parse signal_strength reply: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - if (net_err != 0) { - error("get_signal_strength failed with code %d", net_err); - goto done; - } - - update_signal_strength(signals_bar); - -done: - dbus_message_unref(reply); -} - -static int get_signal_strength(void) -{ - return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH, - NETWORK_INTERFACE, "get_signal_strength", - signal_strength_reply, NULL, - DBUS_TYPE_INVALID); -} - -static void registration_status_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - uint8_t status; - dbus_uint16_t lac, network_type, supported_services; - dbus_uint32_t cell_id, operator_code, country_code; - dbus_int32_t net_err; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("Unable to get registration status: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_error_init(&err); - if (!dbus_message_get_args(reply, &err, - DBUS_TYPE_BYTE, &status, - DBUS_TYPE_UINT16, &lac, - DBUS_TYPE_UINT32, &cell_id, - DBUS_TYPE_UINT32, &operator_code, - DBUS_TYPE_UINT32, &country_code, - DBUS_TYPE_BYTE, &network_type, - DBUS_TYPE_BYTE, &supported_services, - DBUS_TYPE_INT32, &net_err, - DBUS_TYPE_INVALID)) { - error("Unable to parse registration_status_change reply:" - " %s, %s", err.name, err.message); - dbus_error_free(&err); - goto done; - } - - if (net_err != 0) { - error("get_registration_status failed with code %d", net_err); - goto done; - } - - update_registration_status(status, lac, cell_id, operator_code, - country_code, network_type, - supported_services); - - get_signal_strength(); - -done: - dbus_message_unref(reply); -} - -static int get_registration_status(void) -{ - return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH, - NETWORK_INTERFACE, "get_registration_status", - registration_status_reply, NULL, - DBUS_TYPE_INVALID); -} - -static void call_info_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - DBusMessageIter iter, sub; - - get_calls_active = FALSE; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("csd replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature in GetCallInfoAll return"); - goto done; - } - - dbus_message_iter_recurse(&iter, &sub); - - parse_call_list(&sub); - - get_registration_status(); - -done: - dbus_message_unref(reply); -} - -static void hal_find_device_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - DBusMessageIter iter, sub; - const char *path; - char match_string[256]; - int type; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("hald replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature in FindDeviceByCapability return"); - goto done; - } - - dbus_message_iter_recurse(&iter, &sub); - - type = dbus_message_iter_get_arg_type(&sub); - - if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) { - error("No hal device with battery capability found"); - goto done; - } - - dbus_message_iter_get_basic(&sub, &path); - - DBG("telephony-maemo: found battery device at %s", path); - - snprintf(match_string, sizeof(match_string), - "type='signal'," - "path='%s'," - "interface='org.freedesktop.Hal.Device'," - "member='PropertyModified'", path); - dbus_bus_add_match(connection, match_string, NULL); - - hal_get_integer(path, "battery.charge_level.last_full", &battchg_last); - hal_get_integer(path, "battery.charge_level.current", &battchg_cur); - hal_get_integer(path, "battery.charge_level.design", &battchg_design); - -done: - dbus_message_unref(reply); -} - -static void phonebook_read_reply(DBusPendingCall *call, void *user_data) -{ - DBusError derr; - DBusMessage *reply; - const char *name, *number; - char **number_type = user_data; - dbus_int32_t current_location, err; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - error("SIM.Phonebook replied with an error: %s, %s", - derr.name, derr.message); - dbus_error_free(&derr); - goto done; - } - - dbus_error_init(&derr); - if (dbus_message_get_args(reply, &derr, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_INT32, ¤t_location, - DBUS_TYPE_INT32, &err, - DBUS_TYPE_INVALID) == FALSE) { - error("Unable to parse SIM.Phonebook.read arguments: %s, %s", - derr.name, derr.message); - dbus_error_free(&derr); - goto done; - } - - if (err != 0) { - error("SIM.Phonebook.read failed with error %d", err); - if (number_type == &vmbx) - vmbx = g_strdup(getenv("VMBX_NUMBER")); - goto done; - } - - if (number_type == &msisdn) { - g_free(msisdn); - msisdn = g_strdup(number); - DBG("Got MSISDN %s (%s)", number, name); - } else { - g_free(vmbx); - vmbx = g_strdup(number); - DBG("Got voice mailbox number %s (%s)", number, name); - } - -done: - dbus_message_unref(reply); -} - -static void csd_init(void) -{ - dbus_uint32_t location; - uint8_t pb_type, location_type; - int ret; - - ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, "GetCallInfoAll", - call_info_reply, NULL, DBUS_TYPE_INVALID); - if (ret < 0) { - error("Unable to sent GetCallInfoAll method call"); - return; - } - - get_calls_active = TRUE; - - pb_type = SIM_PHONEBOOK_TYPE_MSISDN; - location = PHONEBOOK_INDEX_FIRST_ENTRY; - location_type = SIM_PHONEBOOK_LOCATION_NEXT; - - ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH, - SIM_PHONEBOOK_INTERFACE, "read", - phonebook_read_reply, &msisdn, - DBUS_TYPE_BYTE, &pb_type, - DBUS_TYPE_INT32, &location, - DBUS_TYPE_BYTE, &location_type, - DBUS_TYPE_INVALID); - if (ret < 0) { - error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()"); - return; - } - - pb_type = SIM_PHONEBOOK_TYPE_MBDN; - location = PHONEBOOK_INDEX_FIRST_ENTRY; - location_type = SIM_PHONEBOOK_LOCATION_NEXT; - - ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH, - SIM_PHONEBOOK_INTERFACE, "read", - phonebook_read_reply, &vmbx, - DBUS_TYPE_BYTE, &pb_type, - DBUS_TYPE_INT32, &location, - DBUS_TYPE_BYTE, &location_type, - DBUS_TYPE_INVALID); - if (ret < 0) { - error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()"); - return; - } -} - -static uint32_t get_callflag(const char *callerid_setting) -{ - if (callerid_setting != NULL) { - if (g_str_equal(callerid_setting, "allowed")) - return CALL_FLAG_PRESENTATION_ALLOWED; - else if (g_str_equal(callerid_setting, "restricted")) - return CALL_FLAG_PRESENTATION_RESTRICTED; - else - return CALL_FLAG_NONE; - } else - return CALL_FLAG_NONE; -} - -static void generate_flag_file(const char *filename) -{ - int fd; - - if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS) || - g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS) || - g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS)) - return; - - fd = open(filename, O_WRONLY | O_CREAT, 0); - if (fd >= 0) - close(fd); -} - -static void save_callerid_to_file(const char *callerid_setting) -{ - char callerid_file[FILENAME_MAX]; - - snprintf(callerid_file, sizeof(callerid_file), "%s%s", - CALLERID_BASE, callerid_setting); - - if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS)) - rename(ALLOWED_FLAG_FILE, callerid_file); - else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS)) - rename(RESTRICTED_FLAG_FILE, callerid_file); - else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS)) - rename(NONE_FLAG_FILE, callerid_file); - else - generate_flag_file(callerid_file); -} - -static uint32_t callerid_from_file(void) -{ - if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS)) - return CALL_FLAG_PRESENTATION_ALLOWED; - else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS)) - return CALL_FLAG_PRESENTATION_RESTRICTED; - else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS)) - return CALL_FLAG_NONE; - else - return CALL_FLAG_NONE; -} - -static DBusMessage *set_callerid(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - const char *callerid_setting; - - if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, - &callerid_setting, - DBUS_TYPE_INVALID) == FALSE) - return btd_error_invalid_args(msg); - - if (g_str_equal(callerid_setting, "allowed") || - g_str_equal(callerid_setting, "restricted") || - g_str_equal(callerid_setting, "none")) { - save_callerid_to_file(callerid_setting); - callerid = get_callflag(callerid_setting); - DBG("telephony-maemo setting callerid flag: %s", - callerid_setting); - return dbus_message_new_method_return(msg); - } - - error("telephony-maemo: invalid argument %s for method call" - " SetCallerId", callerid_setting); - return btd_error_invalid_args(msg); -} - -static const GDBusMethodTable telephony_maemo_methods[] = { - { GDBUS_ASYNC_METHOD("SetCallerId", - GDBUS_ARGS({ "id", "s" }), NULL, - set_callerid) }, - { } -}; - -static void handle_modem_state(DBusMessage *msg) -{ - const char *state; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state, - DBUS_TYPE_INVALID)) { - error("Unexpected modem state parameters"); - return; - } - - DBG("SSC modem state: %s", state); - - if (calls != NULL || get_calls_active) - return; - - if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online")) - csd_init(); -} - -static void modem_state_reply(DBusPendingCall *call, void *user_data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("get_modem_status: %s, %s", err.name, err.message); - dbus_error_free(&err); - } else - handle_modem_state(reply); - - dbus_message_unref(reply); -} - -static DBusHandlerResult signal_filter(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *path = dbus_message_get_path(msg); - - if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming")) - handle_incoming_call(msg); - else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created")) - handle_outgoing_call(msg); - else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, - "CreateRequested")) - handle_create_requested(msg); - else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus")) - handle_call_status(msg, path); - else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined")) - handle_conference(msg, TRUE); - else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left")) - handle_conference(msg, FALSE); - else if (dbus_message_is_signal(msg, NETWORK_INTERFACE, - "registration_status_change")) - handle_registration_status_change(msg); - else if (dbus_message_is_signal(msg, NETWORK_INTERFACE, - "signal_strength_change")) - handle_signal_strength_change(msg); - else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device", - "PropertyModified")) - handle_hal_property_modified(msg); - else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE, - "modem_state_changed_ind")) - handle_modem_state(msg); - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -int telephony_init(void) -{ - const char *battery_cap = "battery"; - uint32_t features = AG_FEATURE_EC_ANDOR_NR | - AG_FEATURE_INBAND_RINGTONE | - AG_FEATURE_REJECT_A_CALL | - AG_FEATURE_ENHANCED_CALL_STATUS | - AG_FEATURE_ENHANCED_CALL_CONTROL | - AG_FEATURE_EXTENDED_ERROR_RESULT_CODES | - AG_FEATURE_THREE_WAY_CALLING; - - connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - - if (!dbus_connection_add_filter(connection, signal_filter, - NULL, NULL)) - error("Can't add signal filter"); - - dbus_bus_add_match(connection, - "type=signal,interface=" CSD_CALL_INTERFACE, NULL); - dbus_bus_add_match(connection, - "type=signal,interface=" CSD_CALL_INSTANCE, NULL); - dbus_bus_add_match(connection, - "type=signal,interface=" CSD_CALL_CONFERENCE, NULL); - dbus_bus_add_match(connection, - "type=signal,interface=" NETWORK_INTERFACE, NULL); - dbus_bus_add_match(connection, - "type=signal,interface=" SSC_DBUS_IFACE - ",member=modem_state_changed_ind", NULL); - - if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE, - "get_modem_state", modem_state_reply, - NULL, DBUS_TYPE_INVALID) < 0) - error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()"); - - generate_flag_file(NONE_FLAG_FILE); - callerid = callerid_from_file(); - - if (!g_dbus_register_interface(connection, TELEPHONY_MAEMO_PATH, - TELEPHONY_MAEMO_INTERFACE, telephony_maemo_methods, - NULL, NULL, NULL, NULL)) { - error("telephony-maemo interface %s init failed on path %s", - TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH); - } - - DBG("telephony-maemo registering %s interface on path %s", - TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH); - - telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED, - chld_str); - if (send_method_call("org.freedesktop.Hal", - "/org/freedesktop/Hal/Manager", - "org.freedesktop.Hal.Manager", - "FindDeviceByCapability", - hal_find_device_reply, NULL, - DBUS_TYPE_STRING, &battery_cap, - DBUS_TYPE_INVALID) < 0) - error("Unable to send HAL method call"); - - return 0; -} - -void telephony_exit(void) -{ - g_slist_foreach(calls, (GFunc) csd_call_free, NULL); - g_slist_free(calls); - calls = NULL; - - dbus_connection_remove_filter(connection, signal_filter, NULL); - - dbus_connection_unref(connection); - connection = NULL; - - telephony_deinit(); -} diff -Nru bluez-4.101/audio/telephony-maemo6.c bluez-5.23/audio/telephony-maemo6.c --- bluez-4.101/audio/telephony-maemo6.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/telephony-maemo6.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2200 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2008-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "log.h" -#include "telephony.h" -#include "error.h" - -/* SSC D-Bus definitions */ -#define SSC_DBUS_NAME "com.nokia.phone.SSC" -#define SSC_DBUS_IFACE "com.nokia.phone.SSC" -#define SSC_DBUS_PATH "/com/nokia/phone/SSC" - -/* libcsnet D-Bus definitions */ -#define CSD_CSNET_BUS_NAME "com.nokia.csd.CSNet" -#define CSD_CSNET_PATH "/com/nokia/csd/csnet" -#define CSD_CSNET_IFACE "com.nokia.csd.CSNet" -#define CSD_CSNET_REGISTRATION "com.nokia.csd.CSNet.NetworkRegistration" -#define CSD_CSNET_OPERATOR "com.nokia.csd.CSNet.NetworkOperator" -#define CSD_CSNET_SIGNAL "com.nokia.csd.CSNet.SignalStrength" - -enum net_registration_status { - NETWORK_REG_STATUS_HOME, - NETWORK_REG_STATUS_ROAMING, - NETWORK_REG_STATUS_OFFLINE, - NETWORK_REG_STATUS_SEARCHING, - NETWORK_REG_STATUS_NO_SIM, - NETWORK_REG_STATUS_POWEROFF, - NETWORK_REG_STATUS_POWERSAFE, - NETWORK_REG_STATUS_NO_COVERAGE, - NETWORK_REG_STATUS_REJECTED, - NETWORK_REG_STATUS_UNKOWN -}; - -/* CSD CALL plugin D-Bus definitions */ -#define CSD_CALL_BUS_NAME "com.nokia.csd.Call" -#define CSD_CALL_INTERFACE "com.nokia.csd.Call" -#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance" -#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference" -#define CSD_CALL_PATH "/com/nokia/csd/call" -#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference" - -/* Call status values as exported by the CSD CALL plugin */ -#define CSD_CALL_STATUS_IDLE 0 -#define CSD_CALL_STATUS_CREATE 1 -#define CSD_CALL_STATUS_COMING 2 -#define CSD_CALL_STATUS_PROCEEDING 3 -#define CSD_CALL_STATUS_MO_ALERTING 4 -#define CSD_CALL_STATUS_MT_ALERTING 5 -#define CSD_CALL_STATUS_WAITING 6 -#define CSD_CALL_STATUS_ANSWERED 7 -#define CSD_CALL_STATUS_ACTIVE 8 -#define CSD_CALL_STATUS_MO_RELEASE 9 -#define CSD_CALL_STATUS_MT_RELEASE 10 -#define CSD_CALL_STATUS_HOLD_INITIATED 11 -#define CSD_CALL_STATUS_HOLD 12 -#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13 -#define CSD_CALL_STATUS_RECONNECT_PENDING 14 -#define CSD_CALL_STATUS_TERMINATED 15 -#define CSD_CALL_STATUS_SWAP_INITIATED 16 - -#define CALL_FLAG_NONE 0 -#define CALL_FLAG_PRESENTATION_ALLOWED 0x01 -#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02 - -/* SIM Phonebook D-Bus definitions */ -#define CSD_SIMPB_BUS_NAME "com.nokia.csd.SIM" -#define CSD_SIMPB_INTERFACE "com.nokia.csd.SIM.Phonebook" -#define CSD_SIMPB_PATH "/com/nokia/csd/sim/phonebook" - -#define CSD_SIMPB_TYPE_ADN "ADN" -#define CSD_SIMPB_TYPE_FDN "FDN" -#define CSD_SIMPB_TYPE_SDN "SDN" -#define CSD_SIMPB_TYPE_VMBX "VMBX" -#define CSD_SIMPB_TYPE_MBDN "MBDN" -#define CSD_SIMPB_TYPE_EN "EN" -#define CSD_SIMPB_TYPE_MSISDN "MSISDN" - -/* OHM plugin D-Bus definitions */ -#define OHM_BUS_NAME "com.nokia.NonGraphicFeedback1" -#define OHM_INTERFACE "com.nokia.NonGraphicFeedback1" -#define OHM_PATH "/com/nokia/NonGraphicFeedback1" - -/* tone-genenerator D-Bus definitions */ -#define TONEGEN_BUS_NAME "com.Nokia.Telephony.Tones" -#define TONEGEN_INTERFACE "com.Nokia.Telephony.Tones" -#define TONEGEN_PATH "/com/Nokia/Telephony/Tones" - -/* tone-generator DTMF definitions */ -#define DTMF_ASTERISK 10 -#define DTMF_HASHMARK 11 -#define DTMF_A 12 -#define DTMF_B 13 -#define DTMF_C 14 -#define DTMF_D 15 - -#define FEEDBACK_TONE_DURATION 200 - -struct csd_call { - char *object_path; - int status; - gboolean originating; - gboolean emergency; - gboolean on_hold; - gboolean conference; - char *number; - gboolean setup; -}; - -static struct { - char *operator_name; - uint8_t status; - int32_t signal_bars; -} net = { - .operator_name = NULL, - .status = NETWORK_REG_STATUS_UNKOWN, - /* Init as 0 meaning inactive mode. In modem power off state - * can be be -1, but we treat all values as 0s regardless - * inactive or power off. */ - .signal_bars = 0, -}; - -struct pending_req { - DBusPendingCall *call; - void *user_data; -}; - -static int get_property(const char *iface, const char *prop); - -static DBusConnection *connection = NULL; - -static GSList *calls = NULL; -static GSList *watches = NULL; -static GSList *pending = NULL; - -/* Reference count for determining the call indicator status */ -static GSList *active_calls = NULL; - -/* Queue of DTMF tones to play */ -static GSList *tones = NULL; -static guint create_tones_timer = 0; - -static char *msisdn = NULL; /* Subscriber number */ -static char *vmbx = NULL; /* Voice mailbox number */ - -/* HAL battery namespace key values */ -static int battchg_cur = -1; /* "battery.charge_level.current" */ -static int battchg_last = -1; /* "battery.charge_level.last_full" */ -static int battchg_design = -1; /* "battery.charge_level.design" */ - -static gboolean get_calls_active = FALSE; - -static gboolean events_enabled = FALSE; - -/* Supported set of call hold operations */ -static const char *chld_str = "0,1,1x,2,2x,3,4"; - -/* Timer for tracking call creation requests */ -static guint create_request_timer = 0; - -static struct indicator maemo_indicators[] = -{ - { "battchg", "0-5", 5, TRUE }, - /* signal strength in terms of bars */ - { "signal", "0-5", 0, TRUE }, - { "service", "0,1", 0, TRUE }, - { "call", "0,1", 0, TRUE }, - { "callsetup", "0-3", 0, TRUE }, - { "callheld", "0-2", 0, FALSE }, - { "roam", "0,1", 0, TRUE }, - { NULL } -}; - -static char *call_status_str[] = { - "IDLE", - "CREATE", - "COMING", - "PROCEEDING", - "MO_ALERTING", - "MT_ALERTING", - "WAITING", - "ANSWERED", - "ACTIVE", - "MO_RELEASE", - "MT_RELEASE", - "HOLD_INITIATED", - "HOLD", - "RETRIEVE_INITIATED", - "RECONNECT_PENDING", - "TERMINATED", - "SWAP_INITIATED", - "???" -}; - -static int send_method_call(const char *dest, const char *path, - const char *interface, const char *method, - DBusPendingCallNotifyFunction cb, - void *user_data, int type, ...) -{ - DBusMessage *msg; - DBusPendingCall *call; - va_list args; - struct pending_req *req; - - msg = dbus_message_new_method_call(dest, path, interface, method); - if (!msg) { - error("Unable to allocate new D-Bus %s message", method); - return -ENOMEM; - } - - va_start(args, type); - - if (!dbus_message_append_args_valist(msg, type, args)) { - dbus_message_unref(msg); - va_end(args); - return -EIO; - } - - va_end(args); - - if (!cb) { - g_dbus_send_message(connection, msg); - return 0; - } - - if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) { - error("Sending %s failed", method); - dbus_message_unref(msg); - return -EIO; - } - - dbus_pending_call_set_notify(call, cb, user_data, NULL); - - req = g_new0(struct pending_req, 1); - req->call = call; - req->user_data = user_data; - - pending = g_slist_prepend(pending, req); - dbus_message_unref(msg); - - return 0; -} - -static struct csd_call *find_call(const char *path) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (g_str_equal(call->object_path, path)) - return call; - } - - return NULL; -} - -static struct csd_call *find_non_held_call(void) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (call->status == CSD_CALL_STATUS_IDLE) - continue; - - if (call->status != CSD_CALL_STATUS_HOLD) - return call; - } - - return NULL; -} - -static struct csd_call *find_non_idle_call(void) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (call->status != CSD_CALL_STATUS_IDLE) - return call; - } - - return NULL; -} - -static struct csd_call *find_call_with_status(int status) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (call->status == status) - return call; - } - - return NULL; -} - -static int release_conference(void) -{ - DBusMessage *msg; - - DBG("telephony-maemo6: releasing conference call"); - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, - CSD_CALL_CONFERENCE_PATH, - CSD_CALL_INSTANCE, - "Release"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int release_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, - call->object_path, - CSD_CALL_INSTANCE, - "Release"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int answer_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, - call->object_path, - CSD_CALL_INSTANCE, - "Answer"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static struct pending_req *find_request(const DBusPendingCall *call) -{ - GSList *l; - - for (l = pending; l; l = l->next) { - struct pending_req *req = l->data; - - if (req->call == call) - return req; - } - - return NULL; -} - -static void pending_req_finalize(void *data) -{ - struct pending_req *req = data; - - if (!dbus_pending_call_get_completed(req->call)) - dbus_pending_call_cancel(req->call); - - dbus_pending_call_unref(req->call); - g_free(req); -} - -static void remove_pending(DBusPendingCall *call) -{ - struct pending_req *req = find_request(call); - - pending = g_slist_remove(pending, req); - pending_req_finalize(req); -} - -static void stop_ringtone_reply(DBusPendingCall *call, void *user_data) -{ - struct csd_call *coming = user_data; - - remove_pending(call); - answer_call(coming); -} - -static int stop_ringtone_and_answer(struct csd_call *call) -{ - int ret; - - ret = send_method_call(OHM_BUS_NAME, OHM_PATH, - OHM_INTERFACE, "StopRingtone", - stop_ringtone_reply, call, - DBUS_TYPE_INVALID); - if (ret < 0) - return answer_call(call); - - return 0; -} - -static int split_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, - call->object_path, - CSD_CALL_INSTANCE, - "Split"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int unhold_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Unhold"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int hold_call(struct csd_call *call) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Hold"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int swap_calls(void) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Swap"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int create_conference(void) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Conference"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int call_transfer(void) -{ - DBusMessage *msg; - - msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, - "Transfer"); - if (!msg) { - error("Unable to allocate new D-Bus message"); - return -ENOMEM; - } - - g_dbus_send_message(connection, msg); - - return 0; -} - -static int number_type(const char *number) -{ - if (number == NULL) - return NUMBER_TYPE_TELEPHONY; - - if (number[0] == '+' || strncmp(number, "00", 2) == 0) - return NUMBER_TYPE_INTERNATIONAL; - - return NUMBER_TYPE_TELEPHONY; -} - -void telephony_device_connected(void *telephony_device) -{ - struct csd_call *coming; - - DBG("telephony-maemo6: device %p connected", telephony_device); - - coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); - if (coming) { - if (find_call_with_status(CSD_CALL_STATUS_ACTIVE)) - telephony_call_waiting_ind(coming->number, - number_type(coming->number)); - else - telephony_incoming_call_ind(coming->number, - number_type(coming->number)); - } -} - -static void remove_pending_by_data(gpointer data, gpointer user_data) -{ - struct pending_req *req = data; - - if (req->user_data == user_data) { - pending = g_slist_remove(pending, req); - pending_req_finalize(req); - } -} - -void telephony_device_disconnected(void *telephony_device) -{ - DBG("telephony-maemo6: device %p disconnected", telephony_device); - events_enabled = FALSE; - - g_slist_foreach(pending, remove_pending_by_data, telephony_device); -} - -void telephony_event_reporting_req(void *telephony_device, int ind) -{ - events_enabled = ind == 1 ? TRUE : FALSE; - - telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_response_and_hold_req(void *telephony_device, int rh) -{ - telephony_response_and_hold_rsp(telephony_device, - CME_ERROR_NOT_SUPPORTED); -} - -void telephony_terminate_call_req(void *telephony_device) -{ - struct csd_call *call; - struct csd_call *alerting; - int err; - - call = find_call_with_status(CSD_CALL_STATUS_ACTIVE); - if (!call) - call = find_non_idle_call(); - - if (!call) { - error("No active call"); - telephony_terminate_call_rsp(telephony_device, - CME_ERROR_NOT_ALLOWED); - return; - } - - alerting = find_call_with_status(CSD_CALL_STATUS_MO_ALERTING); - if (call->on_hold && alerting) - err = release_call(alerting); - else if (call->conference) - err = release_conference(); - else - err = release_call(call); - - if (err < 0) - telephony_terminate_call_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_answer_call_req(void *telephony_device) -{ - struct csd_call *call; - - call = find_call_with_status(CSD_CALL_STATUS_COMING); - if (!call) - call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); - - if (!call) - call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING); - - if (!call) - call = find_call_with_status(CSD_CALL_STATUS_WAITING); - - if (!call) { - telephony_answer_call_rsp(telephony_device, - CME_ERROR_NOT_ALLOWED); - return; - } - - if (stop_ringtone_and_answer(call) < 0) - telephony_answer_call_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE); -} - -static void create_call_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - void *telephony_device = user_data; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("csd replied with an error: %s, %s", - err.name, err.message); - if (g_strcmp0(err.name, - "com.nokia.csd.Call.Error.CSInactive") == 0) - telephony_dial_number_rsp(telephony_device, - CME_ERROR_NO_NETWORK_SERVICE); - else - telephony_dial_number_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - dbus_error_free(&err); - } else - telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE); - - dbus_message_unref(reply); - remove_pending(call); -} - -void telephony_last_dialed_number_req(void *telephony_device) -{ - int ret; - - DBG("telephony-maemo6: last dialed number request"); - - ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, "CreateFromLast", - create_call_reply, telephony_device, - DBUS_TYPE_INVALID); - if (ret < 0) - telephony_dial_number_rsp(telephony_device, - CME_ERROR_AG_FAILURE); -} - -static const char *memory_dial_lookup(int location) -{ - if (location == 1) - return vmbx; - else - return NULL; -} - -void telephony_dial_number_req(void *telephony_device, const char *number) -{ - int ret; - - DBG("telephony-maemo6: dial request to %s", number); - - if (strncmp(number, "*31#", 4) == 0) - number += 4; - else if (strncmp(number, "#31#", 4) == 0) - number += 4; - else if (number[0] == '>') { - const char *location = &number[1]; - - number = memory_dial_lookup(strtol(&number[1], NULL, 0)); - if (!number) { - error("No number at memory location %s", location); - telephony_dial_number_rsp(telephony_device, - CME_ERROR_INVALID_INDEX); - return; - } - } - - ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, "Create", - create_call_reply, telephony_device, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID); - if (ret < 0) - telephony_dial_number_rsp(telephony_device, - CME_ERROR_AG_FAILURE); -} - -static void start_dtmf_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("csd replied with an error: %s, %s", - err.name, err.message); - - dbus_error_free(&err); - } else - send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, "StopDTMF", - NULL, NULL, - DBUS_TYPE_INVALID); - - dbus_message_unref(reply); - remove_pending(call); -} - -static void start_dtmf(void *telephony_device, char tone) -{ - int ret; - - /* - * Stop tone immediately, modem will place it in queue and play - * required time. - */ - ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, "StartDTMF", - start_dtmf_reply, NULL, - DBUS_TYPE_BYTE, &tone, - DBUS_TYPE_INVALID); - if (ret < 0) { - telephony_transmit_dtmf_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - return; - } - - telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE); -} - -static int tonegen_startevent(char tone) -{ - int ret; - dbus_uint32_t event_tone; - dbus_int32_t dbm0 = -15; - dbus_uint32_t duration = 150; - - switch (tone) { - case '*': - event_tone = DTMF_ASTERISK; - break; - case '#': - event_tone = DTMF_HASHMARK; - break; - case 'A': - event_tone = DTMF_A; - break; - case 'B': - event_tone = DTMF_B; - break; - case 'C': - event_tone = DTMF_C; - break; - case 'D': - event_tone = DTMF_D; - break; - default: - ret = g_ascii_digit_value(tone); - if (ret < 0) - return -EINVAL; - event_tone = ret; - } - - ret = send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH, - TONEGEN_INTERFACE, "StartEventTone", - NULL, NULL, - DBUS_TYPE_UINT32, &event_tone, - DBUS_TYPE_INT32, &dbm0, - DBUS_TYPE_UINT32, &duration, - DBUS_TYPE_INVALID); - return ret; -} - -static gboolean stop_feedback_tone(gpointer user_data) -{ - if (g_slist_length(tones) > 0) { - gpointer ptone; - int ret; - - send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH, - TONEGEN_INTERFACE, "StopTone", - NULL, NULL, - DBUS_TYPE_INVALID); - - ptone = g_slist_nth_data(tones, 0); - tones = g_slist_remove(tones, ptone); - - ret = tonegen_startevent(GPOINTER_TO_UINT(ptone)); - if (ret < 0) - goto done; - - return TRUE; - } -done: - return FALSE; -} - -static void tones_timer_notify(gpointer data) -{ - send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH, - TONEGEN_INTERFACE, "StopTone", - NULL, NULL, - DBUS_TYPE_INVALID); - g_slist_free(tones); - tones = NULL; - - create_tones_timer = 0; -} - -static void start_feedback_tone(char tone) -{ - if (!create_tones_timer) { - int ret; - - ret = tonegen_startevent(tone); - if (ret < 0) - return; - - create_tones_timer = g_timeout_add_full(G_PRIORITY_DEFAULT, - FEEDBACK_TONE_DURATION, - stop_feedback_tone, - NULL, - tones_timer_notify); - } else { - glong dtmf_tone = tone; - - DBG("add %c to queue", tone); - tones = g_slist_append(tones, GUINT_TO_POINTER(dtmf_tone)); - } -} - -void telephony_transmit_dtmf_req(void *telephony_device, char tone) -{ - DBG("telephony-maemo6: transmit dtmf: %c", tone); - - start_dtmf(telephony_device, tone); - - if (!find_call_with_status(CSD_CALL_STATUS_ACTIVE)) - error("No active call"); - else - start_feedback_tone(tone); -} - -void telephony_subscriber_number_req(void *telephony_device) -{ - DBG("telephony-maemo6: subscriber number request"); - if (msisdn) - telephony_subscriber_number_ind(msisdn, - number_type(msisdn), - SUBSCRIBER_SERVICE_VOICE); - telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE); -} - -static int csd_status_to_hfp(struct csd_call *call) -{ - switch (call->status) { - case CSD_CALL_STATUS_IDLE: - case CSD_CALL_STATUS_MO_RELEASE: - case CSD_CALL_STATUS_MT_RELEASE: - case CSD_CALL_STATUS_TERMINATED: - return -1; - case CSD_CALL_STATUS_CREATE: - return CALL_STATUS_DIALING; - case CSD_CALL_STATUS_WAITING: - return CALL_STATUS_WAITING; - case CSD_CALL_STATUS_PROCEEDING: - /* PROCEEDING can happen in outgoing/incoming */ - if (call->originating) - return CALL_STATUS_DIALING; - - /* - * PROCEEDING is followed by WAITING CSD status, therefore - * second incoming call status indication is set immediately - * to waiting. - */ - if (g_slist_length(active_calls) > 0) - return CALL_STATUS_WAITING; - - return CALL_STATUS_INCOMING; - case CSD_CALL_STATUS_COMING: - if (g_slist_length(active_calls) > 0) - return CALL_STATUS_WAITING; - - return CALL_STATUS_INCOMING; - case CSD_CALL_STATUS_MO_ALERTING: - return CALL_STATUS_ALERTING; - case CSD_CALL_STATUS_MT_ALERTING: - return CALL_STATUS_INCOMING; - case CSD_CALL_STATUS_ANSWERED: - case CSD_CALL_STATUS_ACTIVE: - case CSD_CALL_STATUS_RECONNECT_PENDING: - case CSD_CALL_STATUS_SWAP_INITIATED: - case CSD_CALL_STATUS_HOLD_INITIATED: - return CALL_STATUS_ACTIVE; - case CSD_CALL_STATUS_RETRIEVE_INITIATED: - case CSD_CALL_STATUS_HOLD: - return CALL_STATUS_HELD; - default: - return -1; - } -} - -void telephony_list_current_calls_req(void *telephony_device) -{ - GSList *l; - int i; - - DBG("telephony-maemo6: list current calls request"); - - for (l = calls, i = 1; l != NULL; l = l->next, i++) { - struct csd_call *call = l->data; - int status, direction, multiparty; - - status = csd_status_to_hfp(call); - if (status < 0) - continue; - - direction = call->originating ? - CALL_DIR_OUTGOING : CALL_DIR_INCOMING; - - multiparty = call->conference ? - CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO; - - telephony_list_current_call_ind(i, direction, status, - CALL_MODE_VOICE, multiparty, - call->number, - number_type(call->number)); - } - - telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_operator_selection_req(void *telephony_device) -{ - telephony_operator_selection_ind(OPERATOR_MODE_AUTO, - net.operator_name ? net.operator_name : ""); - telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE); -} - -static void foreach_call_with_status(int status, - int (*func)(struct csd_call *call)) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct csd_call *call = l->data; - - if (call->status == status) - func(call); - } -} - -void telephony_call_hold_req(void *telephony_device, const char *cmd) -{ - const char *idx; - struct csd_call *call; - int err = 0; - - DBG("telephony-maemo6: got call hold request %s", cmd); - - if (strlen(cmd) > 1) - idx = &cmd[1]; - else - idx = NULL; - - if (idx) - call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1); - else - call = NULL; - - switch (cmd[0]) { - case '0': - if (find_call_with_status(CSD_CALL_STATUS_WAITING)) - foreach_call_with_status(CSD_CALL_STATUS_WAITING, - release_call); - else - foreach_call_with_status(CSD_CALL_STATUS_HOLD, - release_call); - break; - case '1': - if (idx) { - if (call) - err = release_call(call); - break; - } - foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call); - call = find_call_with_status(CSD_CALL_STATUS_WAITING); - if (call) - err = answer_call(call); - break; - case '2': - if (idx) { - if (call) - err = split_call(call); - } else { - struct csd_call *held, *wait; - - call = find_call_with_status(CSD_CALL_STATUS_ACTIVE); - held = find_call_with_status(CSD_CALL_STATUS_HOLD); - wait = find_call_with_status(CSD_CALL_STATUS_WAITING); - - if (wait) - err = answer_call(wait); - else if (call && held) - err = swap_calls(); - else { - if (call) - err = hold_call(call); - if (held) - err = unhold_call(held); - } - } - break; - case '3': - if (find_call_with_status(CSD_CALL_STATUS_HOLD) || - find_call_with_status(CSD_CALL_STATUS_WAITING)) - err = create_conference(); - break; - case '4': - err = call_transfer(); - break; - default: - DBG("Unknown call hold request"); - break; - } - - if (err) - telephony_call_hold_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_nr_and_ec_req(void *telephony_device, gboolean enable) -{ - DBG("telephony-maemo6: got %s NR and EC request", - enable ? "enable" : "disable"); - telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_key_press_req(void *telephony_device, const char *keys) -{ - struct csd_call *active, *waiting; - int err; - - DBG("telephony-maemo6: got key press request for %s", keys); - - waiting = find_call_with_status(CSD_CALL_STATUS_COMING); - if (!waiting) - waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING); - if (!waiting) - waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING); - - active = find_call_with_status(CSD_CALL_STATUS_ACTIVE); - - if (waiting) - err = answer_call(waiting); - else if (active) - err = release_call(active); - else - err = 0; - - if (err < 0) - telephony_key_press_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_key_press_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_voice_dial_req(void *telephony_device, gboolean enable) -{ - DBG("telephony-maemo6: got %s voice dial request", - enable ? "enable" : "disable"); - - telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED); -} - -static void handle_incoming_call(DBusMessage *msg) -{ - const char *number, *call_path; - struct csd_call *call; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &call_path, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in Call.Coming() signal"); - return; - } - - call = find_call(call_path); - if (!call) { - error("Didn't find any matching call object for %s", - call_path); - return; - } - - DBG("Incoming call to %s from number %s", call_path, number); - - g_free(call->number); - call->number = g_strdup(number); - - if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) || - find_call_with_status(CSD_CALL_STATUS_HOLD)) - telephony_call_waiting_ind(call->number, - number_type(call->number)); - else - telephony_incoming_call_ind(call->number, - number_type(call->number)); - - telephony_update_indicator(maemo_indicators, "callsetup", - EV_CALLSETUP_INCOMING); -} - -static void handle_outgoing_call(DBusMessage *msg) -{ - const char *number, *call_path; - struct csd_call *call; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &call_path, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in Call.Created() signal"); - return; - } - - call = find_call(call_path); - if (!call) { - error("Didn't find any matching call object for %s", - call_path); - return; - } - - DBG("Outgoing call from %s to number %s", call_path, number); - - g_free(call->number); - call->number = g_strdup(number); - - if (create_request_timer) { - g_source_remove(create_request_timer); - create_request_timer = 0; - } -} - -static gboolean create_timeout(gpointer user_data) -{ - telephony_update_indicator(maemo_indicators, "callsetup", - EV_CALLSETUP_INACTIVE); - create_request_timer = 0; - return FALSE; -} - -static void handle_create_requested(DBusMessage *msg) -{ - DBG("Call.CreateRequested()"); - - if (create_request_timer) - g_source_remove(create_request_timer); - - create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL); - - telephony_update_indicator(maemo_indicators, "callsetup", - EV_CALLSETUP_OUTGOING); -} - -static void call_set_status(struct csd_call *call, dbus_uint32_t status) -{ - dbus_uint32_t prev_status; - int callheld = telephony_get_indicator(maemo_indicators, "callheld"); - - prev_status = call->status; - DBG("Call %s changed from %s to %s", call->object_path, - call_status_str[prev_status], call_status_str[status]); - - if (prev_status == status) { - DBG("Ignoring CSD Call state change to existing state"); - return; - } - - call->status = (int) status; - - switch (status) { - case CSD_CALL_STATUS_IDLE: - if (call->setup) { - telephony_update_indicator(maemo_indicators, - "callsetup", - EV_CALLSETUP_INACTIVE); - if (!call->originating) - telephony_calling_stopped_ind(); - } - - g_free(call->number); - call->number = NULL; - call->originating = FALSE; - call->emergency = FALSE; - call->on_hold = FALSE; - call->conference = FALSE; - call->setup = FALSE; - break; - case CSD_CALL_STATUS_CREATE: - call->originating = TRUE; - call->setup = TRUE; - break; - case CSD_CALL_STATUS_COMING: - call->originating = FALSE; - call->setup = TRUE; - break; - case CSD_CALL_STATUS_PROCEEDING: - break; - case CSD_CALL_STATUS_MO_ALERTING: - telephony_update_indicator(maemo_indicators, "callsetup", - EV_CALLSETUP_ALERTING); - break; - case CSD_CALL_STATUS_MT_ALERTING: - /* Some headsets expect incoming call notification before they - * can send ATA command. When call changed status from waiting - * to alerting we need to send missing notification. Otherwise - * headsets like Nokia BH-108 or BackBeat 903 are unable to - * answer incoming call that was previously waiting. */ - if (prev_status == CSD_CALL_STATUS_WAITING) - telephony_incoming_call_ind(call->number, - number_type(call->number)); - break; - case CSD_CALL_STATUS_WAITING: - break; - case CSD_CALL_STATUS_ANSWERED: - break; - case CSD_CALL_STATUS_ACTIVE: - if (call->on_hold) { - call->on_hold = FALSE; - if (find_call_with_status(CSD_CALL_STATUS_HOLD)) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_MULTIPLE); - else - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_NONE); - } else { - if (!g_slist_find(active_calls, call)) - active_calls = g_slist_prepend(active_calls, call); - if (g_slist_length(active_calls) == 1) - telephony_update_indicator(maemo_indicators, - "call", - EV_CALL_ACTIVE); - /* Upgrade callheld status if necessary */ - if (callheld == EV_CALLHELD_ON_HOLD) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_MULTIPLE); - telephony_update_indicator(maemo_indicators, - "callsetup", - EV_CALLSETUP_INACTIVE); - if (!call->originating) - telephony_calling_stopped_ind(); - call->setup = FALSE; - } - break; - case CSD_CALL_STATUS_MO_RELEASE: - case CSD_CALL_STATUS_MT_RELEASE: - active_calls = g_slist_remove(active_calls, call); - if (g_slist_length(active_calls) == 0) - telephony_update_indicator(maemo_indicators, "call", - EV_CALL_INACTIVE); - - if (create_tones_timer) - g_source_remove(create_tones_timer); - break; - case CSD_CALL_STATUS_HOLD_INITIATED: - break; - case CSD_CALL_STATUS_HOLD: - call->on_hold = TRUE; - if (find_non_held_call()) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_MULTIPLE); - else - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_ON_HOLD); - break; - case CSD_CALL_STATUS_RETRIEVE_INITIATED: - break; - case CSD_CALL_STATUS_RECONNECT_PENDING: - break; - case CSD_CALL_STATUS_TERMINATED: - if (call->on_hold && - !find_call_with_status(CSD_CALL_STATUS_HOLD)) { - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_NONE); - return; - } - - if (callheld == EV_CALLHELD_MULTIPLE && - find_call_with_status(CSD_CALL_STATUS_HOLD) && - !find_call_with_status(CSD_CALL_STATUS_ACTIVE)) - telephony_update_indicator(maemo_indicators, - "callheld", - EV_CALLHELD_ON_HOLD); - break; - case CSD_CALL_STATUS_SWAP_INITIATED: - break; - default: - error("Unknown call status %u", status); - break; - } -} - -static void handle_call_status(DBusMessage *msg, const char *call_path) -{ - struct csd_call *call; - dbus_uint32_t status, cause_type, cause; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_UINT32, &status, - DBUS_TYPE_UINT32, &cause_type, - DBUS_TYPE_UINT32, &cause, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in Instance.CallStatus() signal"); - return; - } - - call = find_call(call_path); - if (!call) { - error("Didn't find any matching call object for %s", - call_path); - return; - } - - if (status > 16) { - error("Invalid call status %u", status); - return; - } - - call_set_status(call, status); -} - -static void handle_conference(DBusMessage *msg, gboolean joined) -{ - const char *path; - struct csd_call *call; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in Conference.%s", - dbus_message_get_member(msg)); - return; - } - - call = find_call(path); - if (!call) { - error("Conference signal for unknown call %s", path); - return; - } - - DBG("Call %s %s the conference", path, joined ? "joined" : "left"); - - call->conference = joined; -} - -static uint8_t str2status(const char *state) -{ - if (g_strcmp0(state, "Home") == 0) - return NETWORK_REG_STATUS_HOME; - else if (g_strcmp0(state, "Roaming") == 0) - return NETWORK_REG_STATUS_ROAMING; - else if (g_strcmp0(state, "Offline") == 0) - return NETWORK_REG_STATUS_OFFLINE; - else if (g_strcmp0(state, "Searching") == 0) - return NETWORK_REG_STATUS_SEARCHING; - else if (g_strcmp0(state, "NoSim") == 0) - return NETWORK_REG_STATUS_NO_SIM; - else if (g_strcmp0(state, "Poweroff") == 0) - return NETWORK_REG_STATUS_POWEROFF; - else if (g_strcmp0(state, "Powersafe") == 0) - return NETWORK_REG_STATUS_POWERSAFE; - else if (g_strcmp0(state, "NoCoverage") == 0) - return NETWORK_REG_STATUS_NO_COVERAGE; - else if (g_strcmp0(state, "Reject") == 0) - return NETWORK_REG_STATUS_REJECTED; - else - return NETWORK_REG_STATUS_UNKOWN; -} - -static void update_registration_status(const char *status) -{ - uint8_t new_status; - - new_status = str2status(status); - - if (net.status == new_status) - return; - - switch (new_status) { - case NETWORK_REG_STATUS_HOME: - telephony_update_indicator(maemo_indicators, "roam", - EV_ROAM_INACTIVE); - if (net.status > NETWORK_REG_STATUS_ROAMING) - telephony_update_indicator(maemo_indicators, - "service", - EV_SERVICE_PRESENT); - break; - case NETWORK_REG_STATUS_ROAMING: - telephony_update_indicator(maemo_indicators, "roam", - EV_ROAM_ACTIVE); - if (net.status > NETWORK_REG_STATUS_ROAMING) - telephony_update_indicator(maemo_indicators, - "service", - EV_SERVICE_PRESENT); - break; - case NETWORK_REG_STATUS_OFFLINE: - case NETWORK_REG_STATUS_SEARCHING: - case NETWORK_REG_STATUS_NO_SIM: - case NETWORK_REG_STATUS_POWEROFF: - case NETWORK_REG_STATUS_POWERSAFE: - case NETWORK_REG_STATUS_NO_COVERAGE: - case NETWORK_REG_STATUS_REJECTED: - case NETWORK_REG_STATUS_UNKOWN: - if (net.status < NETWORK_REG_STATUS_OFFLINE) - telephony_update_indicator(maemo_indicators, - "service", - EV_SERVICE_NONE); - break; - } - - net.status = new_status; - - DBG("telephony-maemo6: registration status changed: %s", status); -} - -static void handle_registration_changed(DBusMessage *msg) -{ - const char *status; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &status, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in RegistrationChanged"); - return; - } - - update_registration_status(status); -} - -static void update_signal_strength(int32_t signal_bars) -{ - if (signal_bars < 0) { - DBG("signal strength smaller than expected: %d < 0", - signal_bars); - signal_bars = 0; - } else if (signal_bars > 5) { - DBG("signal strength greater than expected: %d > 5", - signal_bars); - signal_bars = 5; - } - - if (net.signal_bars == signal_bars) - return; - - telephony_update_indicator(maemo_indicators, "signal", signal_bars); - - net.signal_bars = signal_bars; - DBG("telephony-maemo6: signal strength updated: %d/5", signal_bars); -} - -static void handle_signal_bars_changed(DBusMessage *msg) -{ - int32_t signal_bars; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_INT32, &signal_bars, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in SignalBarsChanged"); - return; - } - - update_signal_strength(signal_bars); -} - -static gboolean iter_get_basic_args(DBusMessageIter *iter, - int first_arg_type, ...) -{ - int type; - va_list ap; - - va_start(ap, first_arg_type); - - for (type = first_arg_type; type != DBUS_TYPE_INVALID; - type = va_arg(ap, int)) { - void *value = va_arg(ap, void *); - int real_type = dbus_message_iter_get_arg_type(iter); - - if (real_type != type) { - error("iter_get_basic_args: expected %c but got %c", - (char) type, (char) real_type); - break; - } - - dbus_message_iter_get_basic(iter, value); - dbus_message_iter_next(iter); - } - - va_end(ap); - - return type == DBUS_TYPE_INVALID ? TRUE : FALSE; -} - -static void hal_battery_level_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - dbus_int32_t level; - int *value = user_data; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("hald replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - if (!dbus_message_get_args(reply, NULL, - DBUS_TYPE_INT32, &level, - DBUS_TYPE_INVALID)) { - error("Unexpected args in hald reply"); - goto done; - } - - *value = (int) level; - - if (value == &battchg_last) - DBG("telephony-maemo6: battery.charge_level.last_full is %d", - *value); - else if (value == &battchg_design) - DBG("telephony-maemo6: battery.charge_level.design is %d", - *value); - else - DBG("telephony-maemo6: battery.charge_level.current is %d", - *value); - - if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) { - int new, max; - - if (battchg_last > 0) - max = battchg_last; - else - max = battchg_design; - - new = battchg_cur * 5 / max; - - telephony_update_indicator(maemo_indicators, "battchg", new); - } - -done: - dbus_message_unref(reply); - remove_pending(call); -} - -static void hal_get_integer(const char *path, const char *key, void *user_data) -{ - send_method_call("org.freedesktop.Hal", path, - "org.freedesktop.Hal.Device", - "GetPropertyInteger", - hal_battery_level_reply, user_data, - DBUS_TYPE_STRING, &key, - DBUS_TYPE_INVALID); -} - -static void handle_hal_property_modified(DBusMessage *msg) -{ - DBusMessageIter iter, array; - dbus_int32_t num_changes; - const char *path; - - path = dbus_message_get_path(msg); - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) { - error("Unexpected signature in hal PropertyModified signal"); - return; - } - - dbus_message_iter_get_basic(&iter, &num_changes); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature in hal PropertyModified signal"); - return; - } - - dbus_message_iter_recurse(&iter, &array); - - while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) { - DBusMessageIter prop; - const char *name; - dbus_bool_t added, removed; - - dbus_message_iter_recurse(&array, &prop); - - if (!iter_get_basic_args(&prop, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &added, - DBUS_TYPE_BOOLEAN, &removed, - DBUS_TYPE_INVALID)) { - error("Invalid hal PropertyModified parameters"); - break; - } - - if (g_str_equal(name, "battery.charge_level.last_full")) - hal_get_integer(path, name, &battchg_last); - else if (g_str_equal(name, "battery.charge_level.current")) - hal_get_integer(path, name, &battchg_cur); - else if (g_str_equal(name, "battery.charge_level.design")) - hal_get_integer(path, name, &battchg_design); - - dbus_message_iter_next(&array); - } -} - -static void csd_call_free(void *data) -{ - struct csd_call *call = data; - - if (!call) - return; - - g_free(call->object_path); - g_free(call->number); - - g_slist_foreach(pending, remove_pending_by_data, call); - - g_free(call); -} - -static void parse_call_list(DBusMessageIter *iter) -{ - do { - DBusMessageIter call_iter; - struct csd_call *call; - const char *object_path, *number; - dbus_uint32_t status; - dbus_bool_t originating, terminating, emerg, on_hold, conf; - - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) { - error("Unexpected signature in GetCallInfoAll reply"); - break; - } - - dbus_message_iter_recurse(iter, &call_iter); - - if (!iter_get_basic_args(&call_iter, - DBUS_TYPE_OBJECT_PATH, &object_path, - DBUS_TYPE_UINT32, &status, - DBUS_TYPE_BOOLEAN, &originating, - DBUS_TYPE_BOOLEAN, &terminating, - DBUS_TYPE_BOOLEAN, &emerg, - DBUS_TYPE_BOOLEAN, &on_hold, - DBUS_TYPE_BOOLEAN, &conf, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID)) { - error("Parsing call D-Bus parameters failed"); - break; - } - - call = find_call(object_path); - if (!call) { - call = g_new0(struct csd_call, 1); - call->object_path = g_strdup(object_path); - calls = g_slist_append(calls, call); - DBG("telephony-maemo6: new csd call instance at %s", - object_path); - } - - if (status == CSD_CALL_STATUS_IDLE) - continue; - - /* CSD gives incorrect call_hold property sometimes */ - if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) || - (call->status == CSD_CALL_STATUS_HOLD && - !on_hold)) { - error("Conflicting call status and on_hold property!"); - on_hold = call->status == CSD_CALL_STATUS_HOLD; - } - - call->originating = originating; - call->on_hold = on_hold; - call->conference = conf; - g_free(call->number); - call->number = g_strdup(number); - - /* Update indicators */ - call_set_status(call, status); - - } while (dbus_message_iter_next(iter)); -} - -static void update_operator_name(const char *name) -{ - if (name == NULL) - return; - - g_free(net.operator_name); - net.operator_name = g_strndup(name, 16); - DBG("telephony-maemo6: operator name updated: %s", name); -} - -static void get_property_reply(DBusPendingCall *call, void *user_data) -{ - char *prop = user_data; - DBusError err; - DBusMessage *reply; - DBusMessageIter iter, sub; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("csd replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) { - error("Unexpected signature in Get return"); - goto done; - } - - dbus_message_iter_recurse(&iter, &sub); - - if (g_strcmp0(prop, "RegistrationStatus") == 0) { - const char *status; - - dbus_message_iter_get_basic(&sub, &status); - update_registration_status(status); - - get_property(CSD_CSNET_OPERATOR, "OperatorName"); - get_property(CSD_CSNET_SIGNAL, "SignalBars"); - } else if (g_strcmp0(prop, "OperatorName") == 0) { - const char *name; - - dbus_message_iter_get_basic(&sub, &name); - update_operator_name(name); - } else if (g_strcmp0(prop, "SignalBars") == 0) { - int32_t signal_bars; - - dbus_message_iter_get_basic(&sub, &signal_bars); - update_signal_strength(signal_bars); - } - -done: - g_free(prop); - dbus_message_unref(reply); - remove_pending(call); -} - -static int get_property(const char *iface, const char *prop) -{ - return send_method_call(CSD_CSNET_BUS_NAME, CSD_CSNET_PATH, - DBUS_INTERFACE_PROPERTIES, "Get", - get_property_reply, g_strdup(prop), - DBUS_TYPE_STRING, &iface, - DBUS_TYPE_STRING, &prop, - DBUS_TYPE_INVALID); -} - -static void handle_operator_name_changed(DBusMessage *msg) -{ - const char *name; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_INVALID)) { - error("Unexpected parameters in OperatorNameChanged"); - return; - } - - update_operator_name(name); -} - -static void call_info_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - DBusMessageIter iter, sub; - - get_calls_active = FALSE; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("csd replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature in GetCallInfoAll return"); - goto done; - } - - dbus_message_iter_recurse(&iter, &sub); - - parse_call_list(&sub); - - get_property(CSD_CSNET_REGISTRATION, "RegistrationStatus"); - -done: - dbus_message_unref(reply); - remove_pending(call); -} - - -static void phonebook_read_reply(DBusPendingCall *call, void *user_data) -{ - DBusError derr; - DBusMessage *reply; - const char *name, *number, *secondname, *additionalnumber, *email; - int index; - char **number_type = user_data; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&derr); - if (dbus_set_error_from_message(&derr, reply)) { - error("%s.ReadFirst replied with an error: %s, %s", - CSD_SIMPB_INTERFACE, derr.name, derr.message); - dbus_error_free(&derr); - if (number_type == &vmbx) - vmbx = g_strdup(getenv("VMBX_NUMBER")); - goto done; - } - - dbus_error_init(&derr); - if (dbus_message_get_args(reply, NULL, - DBUS_TYPE_INT32, &index, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_STRING, &secondname, - DBUS_TYPE_STRING, &additionalnumber, - DBUS_TYPE_STRING, &email, - DBUS_TYPE_INVALID) == FALSE) { - error("Unable to parse %s.ReadFirst arguments: %s, %s", - CSD_SIMPB_INTERFACE, derr.name, derr.message); - dbus_error_free(&derr); - goto done; - } - - if (number_type == &msisdn) { - g_free(msisdn); - msisdn = g_strdup(number); - DBG("Got MSISDN %s (%s)", number, name); - } else { - g_free(vmbx); - vmbx = g_strdup(number); - DBG("Got voice mailbox number %s (%s)", number, name); - } - -done: - dbus_message_unref(reply); - remove_pending(call); -} - -static void csd_init(void) -{ - const char *pb_type; - int ret; - - ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH, - CSD_CALL_INTERFACE, "GetCallInfoAll", - call_info_reply, NULL, DBUS_TYPE_INVALID); - if (ret < 0) { - error("Unable to sent GetCallInfoAll method call"); - return; - } - - get_calls_active = TRUE; - - pb_type = CSD_SIMPB_TYPE_MSISDN; - - ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH, - CSD_SIMPB_INTERFACE, "ReadFirst", - phonebook_read_reply, &msisdn, - DBUS_TYPE_STRING, &pb_type, - DBUS_TYPE_INVALID); - if (ret < 0) { - error("Unable to send " CSD_SIMPB_INTERFACE ".read()"); - return; - } - - /* Voicemail should be in MBDN index 0 */ - pb_type = CSD_SIMPB_TYPE_MBDN; - - ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH, - CSD_SIMPB_INTERFACE, "ReadFirst", - phonebook_read_reply, &vmbx, - DBUS_TYPE_STRING, &pb_type, - DBUS_TYPE_INVALID); - if (ret < 0) { - error("Unable to send " CSD_SIMPB_INTERFACE ".read()"); - return; - } -} - -static void handle_modem_state(DBusMessage *msg) -{ - const char *state; - - if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state, - DBUS_TYPE_INVALID)) { - error("Unexpected modem state parameters"); - return; - } - - DBG("SSC modem state: %s", state); - - if (calls != NULL || get_calls_active) - return; - - if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online")) - csd_init(); -} - -static void modem_state_reply(DBusPendingCall *call, void *user_data) -{ - DBusMessage *reply = dbus_pending_call_steal_reply(call); - DBusError err; - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("get_modem_state: %s, %s", err.name, err.message); - dbus_error_free(&err); - } else - handle_modem_state(reply); - - dbus_message_unref(reply); - remove_pending(call); -} - -static gboolean signal_filter(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - const char *path = dbus_message_get_path(msg); - - if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming")) - handle_incoming_call(msg); - else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created")) - handle_outgoing_call(msg); - else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, - "CreateRequested")) - handle_create_requested(msg); - else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus")) - handle_call_status(msg, path); - else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined")) - handle_conference(msg, TRUE); - else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left")) - handle_conference(msg, FALSE); - else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION, - "RegistrationChanged")) - handle_registration_changed(msg); - else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR, - "OperatorNameChanged")) - handle_operator_name_changed(msg); - else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL, - "SignalBarsChanged")) - handle_signal_bars_changed(msg); - else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device", - "PropertyModified")) - handle_hal_property_modified(msg); - else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE, - "modem_state_changed_ind")) - handle_modem_state(msg); - - return TRUE; -} - -static void add_watch(const char *sender, const char *path, - const char *interface, const char *member) -{ - guint watch; - - watch = g_dbus_add_signal_watch(connection, sender, path, interface, - member, signal_filter, NULL, NULL); - - watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch)); -} - -static void hal_find_device_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - DBusMessageIter iter, sub; - const char *path; - int type; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("hald replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature in FindDeviceByCapability return"); - goto done; - } - - dbus_message_iter_recurse(&iter, &sub); - - type = dbus_message_iter_get_arg_type(&sub); - - if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) { - error("No hal device with battery capability found"); - goto done; - } - - dbus_message_iter_get_basic(&sub, &path); - - DBG("telephony-maemo6: found battery device at %s", path); - - add_watch(NULL, path, "org.freedesktop.Hal.Device", - "PropertyModified"); - - hal_get_integer(path, "battery.charge_level.last_full", &battchg_last); - hal_get_integer(path, "battery.charge_level.current", &battchg_cur); - hal_get_integer(path, "battery.charge_level.design", &battchg_design); - -done: - dbus_message_unref(reply); - remove_pending(call); -} - -int telephony_init(void) -{ - const char *battery_cap = "battery"; - uint32_t features = AG_FEATURE_EC_ANDOR_NR | - AG_FEATURE_INBAND_RINGTONE | - AG_FEATURE_REJECT_A_CALL | - AG_FEATURE_ENHANCED_CALL_STATUS | - AG_FEATURE_ENHANCED_CALL_CONTROL | - AG_FEATURE_EXTENDED_ERROR_RESULT_CODES | - AG_FEATURE_THREE_WAY_CALLING; - int i; - - DBG(""); - - connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - - add_watch(NULL, NULL, CSD_CALL_INTERFACE, NULL); - add_watch(NULL, NULL, CSD_CALL_INSTANCE, NULL); - add_watch(NULL, NULL, CSD_CALL_CONFERENCE, NULL); - add_watch(NULL, NULL, CSD_CSNET_REGISTRATION, "RegistrationChanged"); - add_watch(NULL, NULL, CSD_CSNET_OPERATOR, "OperatorNameChanged"); - add_watch(NULL, NULL, CSD_CSNET_SIGNAL, "SignalBarsChanged"); - add_watch(NULL, NULL, SSC_DBUS_IFACE, "modem_state_changed_ind"); - - if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE, - "get_modem_state", modem_state_reply, - NULL, DBUS_TYPE_INVALID) < 0) - error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()"); - - /* Reset indicators */ - for (i = 0; maemo_indicators[i].desc != NULL; i++) { - if (g_str_equal(maemo_indicators[i].desc, "battchg")) - maemo_indicators[i].val = 5; - else - maemo_indicators[i].val = 0; - } - - telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED, - chld_str); - if (send_method_call("org.freedesktop.Hal", - "/org/freedesktop/Hal/Manager", - "org.freedesktop.Hal.Manager", - "FindDeviceByCapability", - hal_find_device_reply, NULL, - DBUS_TYPE_STRING, &battery_cap, - DBUS_TYPE_INVALID) < 0) - error("Unable to send HAL method call"); - - return 0; -} - -static void remove_watch(gpointer data) -{ - g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data)); -} - -void telephony_exit(void) -{ - DBG(""); - - g_free(net.operator_name); - net.operator_name = NULL; - - net.status = NETWORK_REG_STATUS_UNKOWN; - net.signal_bars = 0; - - g_slist_free(active_calls); - active_calls = NULL; - - g_slist_free_full(calls, csd_call_free); - calls = NULL; - - g_slist_free_full(pending, pending_req_finalize); - pending = NULL; - - g_slist_free_full(watches, remove_watch); - watches = NULL; - - dbus_connection_unref(connection); - connection = NULL; - - telephony_deinit(); -} diff -Nru bluez-4.101/audio/telephony-ofono.c bluez-5.23/audio/telephony-ofono.c --- bluez-4.101/audio/telephony-ofono.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/telephony-ofono.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1637 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2009-2010 Intel Corporation - * Copyright (C) 2006-2009 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "log.h" -#include "telephony.h" - -enum net_registration_status { - NETWORK_REG_STATUS_HOME = 0x00, - NETWORK_REG_STATUS_ROAM, - NETWORK_REG_STATUS_NOSERV -}; - -struct voice_call { - char *obj_path; - int status; - gboolean originating; - gboolean conference; - char *number; - guint watch; -}; - -static DBusConnection *connection = NULL; -static char *modem_obj_path = NULL; -static char *last_dialed_number = NULL; -static GSList *calls = NULL; -static GSList *watches = NULL; -static GSList *pending = NULL; - -#define OFONO_BUS_NAME "org.ofono" -#define OFONO_PATH "/" -#define OFONO_MODEM_INTERFACE "org.ofono.Modem" -#define OFONO_MANAGER_INTERFACE "org.ofono.Manager" -#define OFONO_NETWORKREG_INTERFACE "org.ofono.NetworkRegistration" -#define OFONO_VCMANAGER_INTERFACE "org.ofono.VoiceCallManager" -#define OFONO_VC_INTERFACE "org.ofono.VoiceCall" - -/* HAL battery namespace key values */ -static int battchg_cur = -1; /* "battery.charge_level.current" */ -static int battchg_last = -1; /* "battery.charge_level.last_full" */ -static int battchg_design = -1; /* "battery.charge_level.design" */ - -static struct { - uint8_t status; - uint32_t signals_bar; - char *operator_name; -} net = { - .status = NETWORK_REG_STATUS_NOSERV, - .signals_bar = 0, - .operator_name = NULL, -}; - -static const char *chld_str = "0,1,1x,2,2x,3,4"; -static char *subscriber_number = NULL; - -static gboolean events_enabled = FALSE; - -static struct indicator ofono_indicators[] = -{ - { "battchg", "0-5", 5, TRUE }, - { "signal", "0-5", 5, TRUE }, - { "service", "0,1", 1, TRUE }, - { "call", "0,1", 0, TRUE }, - { "callsetup", "0-3", 0, TRUE }, - { "callheld", "0-2", 0, FALSE }, - { "roam", "0,1", 0, TRUE }, - { NULL } -}; - -static struct voice_call *find_vc(const char *path) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct voice_call *vc = l->data; - - if (g_str_equal(vc->obj_path, path)) - return vc; - } - - return NULL; -} - -static struct voice_call *find_vc_with_status(int status) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct voice_call *vc = l->data; - - if (vc->status == status) - return vc; - } - - return NULL; -} - -static struct voice_call *find_vc_without_status(int status) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct voice_call *call = l->data; - - if (call->status != status) - return call; - } - - return NULL; -} - -static int number_type(const char *number) -{ - if (number == NULL) - return NUMBER_TYPE_TELEPHONY; - - if (number[0] == '+' || strncmp(number, "00", 2) == 0) - return NUMBER_TYPE_INTERNATIONAL; - - return NUMBER_TYPE_TELEPHONY; -} - -void telephony_device_connected(void *telephony_device) -{ - struct voice_call *coming; - - DBG("telephony-ofono: device %p connected", telephony_device); - - coming = find_vc_with_status(CALL_STATUS_ALERTING); - if (coming) { - if (find_vc_with_status(CALL_STATUS_ACTIVE)) - telephony_call_waiting_ind(coming->number, - number_type(coming->number)); - else - telephony_incoming_call_ind(coming->number, - number_type(coming->number)); - } -} - -void telephony_device_disconnected(void *telephony_device) -{ - DBG("telephony-ofono: device %p disconnected", telephony_device); - events_enabled = FALSE; -} - -void telephony_event_reporting_req(void *telephony_device, int ind) -{ - events_enabled = ind == 1 ? TRUE : FALSE; - - telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_response_and_hold_req(void *telephony_device, int rh) -{ - telephony_response_and_hold_rsp(telephony_device, - CME_ERROR_NOT_SUPPORTED); -} - -void telephony_last_dialed_number_req(void *telephony_device) -{ - DBG("telephony-ofono: last dialed number request"); - - if (last_dialed_number) - telephony_dial_number_req(telephony_device, last_dialed_number); - else - telephony_last_dialed_number_rsp(telephony_device, - CME_ERROR_NOT_ALLOWED); -} - -static int send_method_call(const char *dest, const char *path, - const char *interface, const char *method, - DBusPendingCallNotifyFunction cb, - void *user_data, int type, ...) -{ - DBusMessage *msg; - DBusPendingCall *call; - va_list args; - - msg = dbus_message_new_method_call(dest, path, interface, method); - if (!msg) { - error("Unable to allocate new D-Bus %s message", method); - return -ENOMEM; - } - - va_start(args, type); - - if (!dbus_message_append_args_valist(msg, type, args)) { - dbus_message_unref(msg); - va_end(args); - return -EIO; - } - - va_end(args); - - if (!cb) { - g_dbus_send_message(connection, msg); - return 0; - } - - if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) { - error("Sending %s failed", method); - dbus_message_unref(msg); - return -EIO; - } - - dbus_pending_call_set_notify(call, cb, user_data, NULL); - pending = g_slist_prepend(pending, call); - dbus_message_unref(msg); - - return 0; -} - -static int answer_call(struct voice_call *vc) -{ - DBG("%s", vc->number); - return send_method_call(OFONO_BUS_NAME, vc->obj_path, - OFONO_VC_INTERFACE, "Answer", - NULL, NULL, DBUS_TYPE_INVALID); -} - -static int release_call(struct voice_call *vc) -{ - DBG("%s", vc->number); - return send_method_call(OFONO_BUS_NAME, vc->obj_path, - OFONO_VC_INTERFACE, "Hangup", - NULL, NULL, DBUS_TYPE_INVALID); -} - -static int release_answer_calls(void) -{ - DBG(""); - return send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, - "ReleaseAndAnswer", - NULL, NULL, DBUS_TYPE_INVALID); -} - -static int split_call(struct voice_call *call) -{ - DBG("%s", call->number); - return send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, - "PrivateChat", - NULL, NULL, - DBUS_TYPE_OBJECT_PATH, - call->obj_path, - DBUS_TYPE_INVALID); - return -1; -} - -static int swap_calls(void) -{ - DBG(""); - return send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, - "SwapCalls", - NULL, NULL, DBUS_TYPE_INVALID); -} - -static int create_conference(void) -{ - DBG(""); - return send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, - "CreateMultiparty", - NULL, NULL, DBUS_TYPE_INVALID); -} - -static int release_conference(void) -{ - DBG(""); - return send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, - "HangupMultiparty", - NULL, NULL, DBUS_TYPE_INVALID); -} - -static int call_transfer(void) -{ - DBG(""); - return send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, - "Transfer", - NULL, NULL, DBUS_TYPE_INVALID); -} - -void telephony_terminate_call_req(void *telephony_device) -{ - struct voice_call *call; - struct voice_call *alerting; - int err; - - call = find_vc_with_status(CALL_STATUS_ACTIVE); - if (!call) - call = calls->data; - - if (!call) { - error("No active call"); - telephony_terminate_call_rsp(telephony_device, - CME_ERROR_NOT_ALLOWED); - return; - } - - alerting = find_vc_with_status(CALL_STATUS_ALERTING); - if (call->status == CALL_STATUS_HELD && alerting) - err = release_call(alerting); - else if (call->conference) - err = release_conference(); - else - err = release_call(call); - - if (err < 0) - telephony_terminate_call_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_answer_call_req(void *telephony_device) -{ - struct voice_call *vc; - int ret; - - vc = find_vc_with_status(CALL_STATUS_INCOMING); - if (!vc) - vc = find_vc_with_status(CALL_STATUS_ALERTING); - - if (!vc) - vc = find_vc_with_status(CALL_STATUS_WAITING); - - if (!vc) { - telephony_answer_call_rsp(telephony_device, - CME_ERROR_NOT_ALLOWED); - return; - } - - ret = answer_call(vc); - if (ret < 0) { - telephony_answer_call_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - return; - } - - telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_dial_number_req(void *telephony_device, const char *number) -{ - const char *clir; - int ret; - - DBG("telephony-ofono: dial request to %s", number); - - if (!modem_obj_path) { - telephony_dial_number_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - return; - } - - if (!strncmp(number, "*31#", 4)) { - number += 4; - clir = "enabled"; - } else if (!strncmp(number, "#31#", 4)) { - number += 4; - clir = "disabled"; - } else - clir = "default"; - - ret = send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, - "Dial", NULL, NULL, - DBUS_TYPE_STRING, &number, - DBUS_TYPE_STRING, &clir, - DBUS_TYPE_INVALID); - - if (ret < 0) - telephony_dial_number_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_transmit_dtmf_req(void *telephony_device, char tone) -{ - char *tone_string; - int ret; - - DBG("telephony-ofono: transmit dtmf: %c", tone); - - if (!modem_obj_path) { - telephony_transmit_dtmf_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - return; - } - - tone_string = g_strdup_printf("%c", tone); - ret = send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, - "SendTones", NULL, NULL, - DBUS_TYPE_STRING, &tone_string, - DBUS_TYPE_INVALID); - g_free(tone_string); - - if (ret < 0) - telephony_transmit_dtmf_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_subscriber_number_req(void *telephony_device) -{ - DBG("telephony-ofono: subscriber number request"); - - if (subscriber_number) - telephony_subscriber_number_ind(subscriber_number, - NUMBER_TYPE_TELEPHONY, - SUBSCRIBER_SERVICE_VOICE); - telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_list_current_calls_req(void *telephony_device) -{ - GSList *l; - int i; - - DBG("telephony-ofono: list current calls request"); - - for (l = calls, i = 1; l != NULL; l = l->next, i++) { - struct voice_call *vc = l->data; - int direction, multiparty; - - direction = vc->originating ? - CALL_DIR_OUTGOING : CALL_DIR_INCOMING; - - multiparty = vc->conference ? - CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO; - - DBG("call %s direction %d multiparty %d", vc->number, - direction, multiparty); - - telephony_list_current_call_ind(i, direction, vc->status, - CALL_MODE_VOICE, multiparty, - vc->number, number_type(vc->number)); - } - - telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_operator_selection_req(void *telephony_device) -{ - DBG("telephony-ofono: operator selection request"); - - telephony_operator_selection_ind(OPERATOR_MODE_AUTO, - net.operator_name ? net.operator_name : ""); - telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE); -} - -static void foreach_vc_with_status(int status, - int (*func)(struct voice_call *vc)) -{ - GSList *l; - - for (l = calls; l != NULL; l = l->next) { - struct voice_call *call = l->data; - - if (call->status == status) - func(call); - } -} - -void telephony_call_hold_req(void *telephony_device, const char *cmd) -{ - const char *idx; - struct voice_call *call; - int err = 0; - - DBG("telephony-ofono: got call hold request %s", cmd); - - if (strlen(cmd) > 1) - idx = &cmd[1]; - else - idx = NULL; - - if (idx) - call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1); - else - call = NULL; - - switch (cmd[0]) { - case '0': - if (find_vc_with_status(CALL_STATUS_WAITING)) - foreach_vc_with_status(CALL_STATUS_WAITING, - release_call); - else - foreach_vc_with_status(CALL_STATUS_HELD, release_call); - break; - case '1': - if (idx) { - if (call) - err = release_call(call); - break; - } - err = release_answer_calls(); - break; - case '2': - if (idx) { - if (call) - err = split_call(call); - } else { - call = find_vc_with_status(CALL_STATUS_WAITING); - - if (call) - err = answer_call(call); - else - err = swap_calls(); - } - break; - case '3': - if (find_vc_with_status(CALL_STATUS_HELD) || - find_vc_with_status(CALL_STATUS_WAITING)) - err = create_conference(); - break; - case '4': - err = call_transfer(); - break; - default: - DBG("Unknown call hold request"); - break; - } - - if (err) - telephony_call_hold_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_nr_and_ec_req(void *telephony_device, gboolean enable) -{ - DBG("telephony-ofono: got %s NR and EC request", - enable ? "enable" : "disable"); - - telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_key_press_req(void *telephony_device, const char *keys) -{ - struct voice_call *active, *incoming; - int err; - - DBG("telephony-ofono: got key press request for %s", keys); - - incoming = find_vc_with_status(CALL_STATUS_INCOMING); - - active = find_vc_with_status(CALL_STATUS_ACTIVE); - - if (incoming) - err = answer_call(incoming); - else if (active) - err = release_call(active); - else - err = 0; - - if (err < 0) - telephony_key_press_rsp(telephony_device, - CME_ERROR_AG_FAILURE); - else - telephony_key_press_rsp(telephony_device, CME_ERROR_NONE); -} - -void telephony_voice_dial_req(void *telephony_device, gboolean enable) -{ - DBG("telephony-ofono: got %s voice dial request", - enable ? "enable" : "disable"); - - telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED); -} - -static gboolean iter_get_basic_args(DBusMessageIter *iter, - int first_arg_type, ...) -{ - int type; - va_list ap; - - va_start(ap, first_arg_type); - - for (type = first_arg_type; type != DBUS_TYPE_INVALID; - type = va_arg(ap, int)) { - void *value = va_arg(ap, void *); - int real_type = dbus_message_iter_get_arg_type(iter); - - if (real_type != type) { - error("iter_get_basic_args: expected %c but got %c", - (char) type, (char) real_type); - break; - } - - dbus_message_iter_get_basic(iter, value); - dbus_message_iter_next(iter); - } - - va_end(ap); - - return type == DBUS_TYPE_INVALID ? TRUE : FALSE; -} - -static void call_free(void *data) -{ - struct voice_call *vc = data; - - DBG("%s", vc->obj_path); - - if (vc->status == CALL_STATUS_ACTIVE) - telephony_update_indicator(ofono_indicators, "call", - EV_CALL_INACTIVE); - else - telephony_update_indicator(ofono_indicators, "callsetup", - EV_CALLSETUP_INACTIVE); - - if (vc->status == CALL_STATUS_INCOMING) - telephony_calling_stopped_ind(); - - g_dbus_remove_watch(connection, vc->watch); - g_free(vc->obj_path); - g_free(vc->number); - g_free(vc); -} - -static gboolean handle_vc_property_changed(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - struct voice_call *vc = data; - const char *obj_path = dbus_message_get_path(msg); - DBusMessageIter iter, sub; - const char *property, *state; - - DBG("path %s", obj_path); - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { - error("Unexpected signature in vc PropertyChanged signal"); - return TRUE; - } - - dbus_message_iter_get_basic(&iter, &property); - DBG("property %s", property); - - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &sub); - if (g_str_equal(property, "State")) { - dbus_message_iter_get_basic(&sub, &state); - DBG("State %s", state); - if (g_str_equal(state, "disconnected")) { - calls = g_slist_remove(calls, vc); - call_free(vc); - } else if (g_str_equal(state, "active")) { - telephony_update_indicator(ofono_indicators, - "call", EV_CALL_ACTIVE); - telephony_update_indicator(ofono_indicators, - "callsetup", - EV_CALLSETUP_INACTIVE); - if (vc->status == CALL_STATUS_INCOMING) - telephony_calling_stopped_ind(); - vc->status = CALL_STATUS_ACTIVE; - } else if (g_str_equal(state, "alerting")) { - telephony_update_indicator(ofono_indicators, - "callsetup", EV_CALLSETUP_ALERTING); - vc->status = CALL_STATUS_ALERTING; - vc->originating = TRUE; - } else if (g_str_equal(state, "incoming")) { - /* state change from waiting to incoming */ - telephony_update_indicator(ofono_indicators, - "callsetup", EV_CALLSETUP_INCOMING); - telephony_incoming_call_ind(vc->number, - NUMBER_TYPE_TELEPHONY); - vc->status = CALL_STATUS_INCOMING; - vc->originating = FALSE; - } else if (g_str_equal(state, "held")) { - vc->status = CALL_STATUS_HELD; - if (find_vc_without_status(CALL_STATUS_HELD)) - telephony_update_indicator(ofono_indicators, - "callheld", - EV_CALLHELD_MULTIPLE); - else - telephony_update_indicator(ofono_indicators, - "callheld", - EV_CALLHELD_ON_HOLD); - } - } else if (g_str_equal(property, "Multiparty")) { - dbus_bool_t multiparty; - - dbus_message_iter_get_basic(&sub, &multiparty); - DBG("Multiparty %s", multiparty ? "True" : "False"); - vc->conference = multiparty; - } - - return TRUE; -} - -static struct voice_call *call_new(const char *path, DBusMessageIter *properties) -{ - struct voice_call *vc; - - DBG("%s", path); - - vc = g_new0(struct voice_call, 1); - vc->obj_path = g_strdup(path); - vc->watch = g_dbus_add_signal_watch(connection, NULL, path, - OFONO_VC_INTERFACE, "PropertyChanged", - handle_vc_property_changed, vc, NULL); - - while (dbus_message_iter_get_arg_type(properties) - == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry, value; - const char *property, *cli, *state; - dbus_bool_t multiparty; - - dbus_message_iter_recurse(properties, &entry); - dbus_message_iter_get_basic(&entry, &property); - - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); - - if (g_str_equal(property, "LineIdentification")) { - dbus_message_iter_get_basic(&value, &cli); - DBG("cli %s", cli); - vc->number = g_strdup(cli); - } else if (g_str_equal(property, "State")) { - dbus_message_iter_get_basic(&value, &state); - DBG("state %s", state); - if (g_str_equal(state, "incoming")) - vc->status = CALL_STATUS_INCOMING; - else if (g_str_equal(state, "dialing")) - vc->status = CALL_STATUS_DIALING; - else if (g_str_equal(state, "alerting")) - vc->status = CALL_STATUS_ALERTING; - else if (g_str_equal(state, "waiting")) - vc->status = CALL_STATUS_WAITING; - else if (g_str_equal(state, "held")) - vc->status = CALL_STATUS_HELD; - } else if (g_str_equal(property, "Multiparty")) { - dbus_message_iter_get_basic(&value, &multiparty); - DBG("Multipary %s", multiparty ? "True" : "False"); - vc->conference = multiparty; - } - - dbus_message_iter_next(properties); - } - - switch (vc->status) { - case CALL_STATUS_INCOMING: - DBG("CALL_STATUS_INCOMING"); - vc->originating = FALSE; - telephony_update_indicator(ofono_indicators, "callsetup", - EV_CALLSETUP_INCOMING); - telephony_incoming_call_ind(vc->number, NUMBER_TYPE_TELEPHONY); - break; - case CALL_STATUS_DIALING: - DBG("CALL_STATUS_DIALING"); - vc->originating = TRUE; - g_free(last_dialed_number); - last_dialed_number = g_strdup(vc->number); - telephony_update_indicator(ofono_indicators, "callsetup", - EV_CALLSETUP_OUTGOING); - break; - case CALL_STATUS_ALERTING: - DBG("CALL_STATUS_ALERTING"); - vc->originating = TRUE; - g_free(last_dialed_number); - last_dialed_number = g_strdup(vc->number); - telephony_update_indicator(ofono_indicators, "callsetup", - EV_CALLSETUP_ALERTING); - break; - case CALL_STATUS_WAITING: - DBG("CALL_STATUS_WAITING"); - vc->originating = FALSE; - telephony_update_indicator(ofono_indicators, "callsetup", - EV_CALLSETUP_INCOMING); - telephony_call_waiting_ind(vc->number, NUMBER_TYPE_TELEPHONY); - break; - } - - return vc; -} - -static void remove_pending(DBusPendingCall *call) -{ - pending = g_slist_remove(pending, call); - dbus_pending_call_unref(call); -} - -static void call_added(const char *path, DBusMessageIter *properties) -{ - struct voice_call *vc; - - DBG("%s", path); - - vc = find_vc(path); - if (vc) - return; - - vc = call_new(path, properties); - calls = g_slist_prepend(calls, vc); -} - -static void get_calls_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - DBusMessageIter iter, entry; - - DBG(""); - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("ofono replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature"); - goto done; - } - - dbus_message_iter_recurse(&iter, &entry); - - while (dbus_message_iter_get_arg_type(&entry) - == DBUS_TYPE_STRUCT) { - const char *path; - DBusMessageIter value, properties; - - dbus_message_iter_recurse(&entry, &value); - dbus_message_iter_get_basic(&value, &path); - - dbus_message_iter_next(&value); - dbus_message_iter_recurse(&value, &properties); - - call_added(path, &properties); - - dbus_message_iter_next(&entry); - } - -done: - dbus_message_unref(reply); - remove_pending(call); -} - -static void handle_network_property(const char *property, DBusMessageIter *variant) -{ - const char *status, *operator; - unsigned int signals_bar; - - if (g_str_equal(property, "Status")) { - dbus_message_iter_get_basic(variant, &status); - DBG("Status is %s", status); - if (g_str_equal(status, "registered")) { - net.status = NETWORK_REG_STATUS_HOME; - telephony_update_indicator(ofono_indicators, - "roam", EV_ROAM_INACTIVE); - telephony_update_indicator(ofono_indicators, - "service", EV_SERVICE_PRESENT); - } else if (g_str_equal(status, "roaming")) { - net.status = NETWORK_REG_STATUS_ROAM; - telephony_update_indicator(ofono_indicators, - "roam", EV_ROAM_ACTIVE); - telephony_update_indicator(ofono_indicators, - "service", EV_SERVICE_PRESENT); - } else { - net.status = NETWORK_REG_STATUS_NOSERV; - telephony_update_indicator(ofono_indicators, - "roam", EV_ROAM_INACTIVE); - telephony_update_indicator(ofono_indicators, - "service", EV_SERVICE_NONE); - } - } else if (g_str_equal(property, "Name")) { - dbus_message_iter_get_basic(variant, &operator); - DBG("Operator is %s", operator); - g_free(net.operator_name); - net.operator_name = g_strdup(operator); - } else if (g_str_equal(property, "SignalStrength")) { - dbus_message_iter_get_basic(variant, &signals_bar); - DBG("SignalStrength is %d", signals_bar); - net.signals_bar = signals_bar; - telephony_update_indicator(ofono_indicators, "signal", - (signals_bar + 20) / 21); - } -} - -static int parse_network_properties(DBusMessageIter *properties) -{ - int i; - - /* Reset indicators */ - for (i = 0; ofono_indicators[i].desc != NULL; i++) { - if (g_str_equal(ofono_indicators[i].desc, "battchg")) - ofono_indicators[i].val = 5; - else - ofono_indicators[i].val = 0; - } - - while (dbus_message_iter_get_arg_type(properties) - == DBUS_TYPE_DICT_ENTRY) { - const char *key; - DBusMessageIter value, entry; - - dbus_message_iter_recurse(properties, &entry); - dbus_message_iter_get_basic(&entry, &key); - - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); - - handle_network_property(key, &value); - - dbus_message_iter_next(properties); - } - - return 0; -} - -static void get_properties_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - DBusMessageIter iter, properties; - int ret = 0; - - DBG(""); - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("ofono replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature"); - goto done; - } - - dbus_message_iter_recurse(&iter, &properties); - - ret = parse_network_properties(&properties); - if (ret < 0) { - error("Unable to parse %s.GetProperty reply", - OFONO_NETWORKREG_INTERFACE); - goto done; - } - - ret = send_method_call(OFONO_BUS_NAME, modem_obj_path, - OFONO_VCMANAGER_INTERFACE, "GetCalls", - get_calls_reply, NULL, DBUS_TYPE_INVALID); - if (ret < 0) - error("Unable to send %s.GetCalls", - OFONO_VCMANAGER_INTERFACE); - -done: - dbus_message_unref(reply); - remove_pending(call); -} - -static void network_found(const char *path) -{ - int ret; - - DBG("%s", path); - - modem_obj_path = g_strdup(path); - - ret = send_method_call(OFONO_BUS_NAME, path, - OFONO_NETWORKREG_INTERFACE, "GetProperties", - get_properties_reply, NULL, DBUS_TYPE_INVALID); - if (ret < 0) - error("Unable to send %s.GetProperties", - OFONO_NETWORKREG_INTERFACE); -} - -static void modem_removed(const char *path) -{ - if (g_strcmp0(modem_obj_path, path) != 0) - return; - - DBG("%s", path); - - g_slist_free_full(calls, call_free); - calls = NULL; - - g_free(net.operator_name); - net.operator_name = NULL; - net.status = NETWORK_REG_STATUS_NOSERV; - net.signals_bar = 0; - - g_free(modem_obj_path); - modem_obj_path = NULL; -} - -static void parse_modem_interfaces(const char *path, DBusMessageIter *ifaces) -{ - DBG("%s", path); - - while (dbus_message_iter_get_arg_type(ifaces) == DBUS_TYPE_STRING) { - const char *iface; - - dbus_message_iter_get_basic(ifaces, &iface); - - if (g_str_equal(iface, OFONO_NETWORKREG_INTERFACE)) { - network_found(path); - return; - } - - dbus_message_iter_next(ifaces); - } - - modem_removed(path); -} - -static void modem_added(const char *path, DBusMessageIter *properties) -{ - if (modem_obj_path != NULL) { - DBG("Ignoring, modem already exist"); - return; - } - - DBG("%s", path); - - while (dbus_message_iter_get_arg_type(properties) - == DBUS_TYPE_DICT_ENTRY) { - const char *key; - DBusMessageIter interfaces, value, entry; - - dbus_message_iter_recurse(properties, &entry); - dbus_message_iter_get_basic(&entry, &key); - - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); - - if (strcasecmp(key, "Interfaces") != 0) - goto next; - - if (dbus_message_iter_get_arg_type(&value) - != DBUS_TYPE_ARRAY) { - error("Invalid Signature"); - return; - } - - dbus_message_iter_recurse(&value, &interfaces); - - parse_modem_interfaces(path, &interfaces); - - if (modem_obj_path != NULL) - return; - - next: - dbus_message_iter_next(properties); - } -} - -static void get_modems_reply(DBusPendingCall *call, void *user_data) -{ - DBusError err; - DBusMessage *reply; - DBusMessageIter iter, entry; - - DBG(""); - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("ofono replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - /* Skip modem selection if a modem already exist */ - if (modem_obj_path != NULL) - goto done; - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature"); - goto done; - } - - dbus_message_iter_recurse(&iter, &entry); - - while (dbus_message_iter_get_arg_type(&entry) - == DBUS_TYPE_STRUCT) { - const char *path; - DBusMessageIter item, properties; - - dbus_message_iter_recurse(&entry, &item); - dbus_message_iter_get_basic(&item, &path); - - dbus_message_iter_next(&item); - dbus_message_iter_recurse(&item, &properties); - - modem_added(path, &properties); - if (modem_obj_path != NULL) - break; - - dbus_message_iter_next(&entry); - } - -done: - dbus_message_unref(reply); - remove_pending(call); -} - -static gboolean handle_network_property_changed(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessageIter iter, variant; - const char *property; - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { - error("Unexpected signature in networkregistration" - " PropertyChanged signal"); - return TRUE; - } - dbus_message_iter_get_basic(&iter, &property); - DBG("in handle_registration_property_changed()," - " the property is %s", property); - - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &variant); - - handle_network_property(property, &variant); - - return TRUE; -} - -static void handle_modem_property(const char *path, const char *property, - DBusMessageIter *variant) -{ - DBG("%s", property); - - if (g_str_equal(property, "Interfaces")) { - DBusMessageIter interfaces; - - if (dbus_message_iter_get_arg_type(variant) - != DBUS_TYPE_ARRAY) { - error("Invalid signature"); - return; - } - - dbus_message_iter_recurse(variant, &interfaces); - parse_modem_interfaces(path, &interfaces); - } -} - -static gboolean handle_modem_property_changed(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessageIter iter, variant; - const char *property, *path; - - path = dbus_message_get_path(msg); - - /* Ignore if modem already exist and paths doesn't match */ - if (modem_obj_path != NULL && - g_str_equal(path, modem_obj_path) == FALSE) - return TRUE; - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { - error("Unexpected signature in %s.%s PropertyChanged signal", - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - return TRUE; - } - - dbus_message_iter_get_basic(&iter, &property); - - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &variant); - - handle_modem_property(path, property, &variant); - - return TRUE; -} - -static gboolean handle_vcmanager_call_added(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessageIter iter, properties; - const char *path = dbus_message_get_path(msg); - - /* Ignore call if modem path doesn't math */ - if (g_strcmp0(modem_obj_path, path) != 0) - return TRUE; - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) - != DBUS_TYPE_OBJECT_PATH) { - error("Unexpected signature in %s.%s signal", - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - return TRUE; - } - - dbus_message_iter_get_basic(&iter, &path); - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &properties); - - call_added(path, &properties); - - return TRUE; -} - -static void call_removed(const char *path) -{ - struct voice_call *vc; - - DBG("%s", path); - - vc = find_vc(path); - if (vc == NULL) - return; - - calls = g_slist_remove(calls, vc); - call_free(vc); -} - -static gboolean handle_vcmanager_call_removed(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *path = dbus_message_get_path(msg); - - /* Ignore call if modem path doesn't math */ - if (g_strcmp0(modem_obj_path, path) != 0) - return TRUE; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - error("Unexpected signature in %s.%s signal", - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - return TRUE; - } - - call_removed(path); - - return TRUE; -} - -static gboolean handle_manager_modem_added(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - DBusMessageIter iter, properties; - const char *path; - - if (modem_obj_path != NULL) - return TRUE; - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) - != DBUS_TYPE_OBJECT_PATH) { - error("Unexpected signature in %s.%s signal", - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - return TRUE; - } - - dbus_message_iter_get_basic(&iter, &path); - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &properties); - - modem_added(path, &properties); - - return TRUE; -} - -static gboolean handle_manager_modem_removed(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *path; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID)) { - error("Unexpected signature in %s.%s signal", - dbus_message_get_interface(msg), - dbus_message_get_member(msg)); - return TRUE; - } - - modem_removed(path); - - return TRUE; -} - -static void hal_battery_level_reply(DBusPendingCall *call, void *user_data) -{ - DBusMessage *reply; - DBusError err; - dbus_int32_t level; - int *value = user_data; - - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - if (dbus_set_error_from_message(&err, reply)) { - error("hald replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_error_init(&err); - if (dbus_message_get_args(reply, &err, - DBUS_TYPE_INT32, &level, - DBUS_TYPE_INVALID) == FALSE) { - error("Unable to parse GetPropertyInteger reply: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - *value = (int) level; - - if (value == &battchg_last) - DBG("telephony-ofono: battery.charge_level.last_full" - " is %d", *value); - else if (value == &battchg_design) - DBG("telephony-ofono: battery.charge_level.design" - " is %d", *value); - else - DBG("telephony-ofono: battery.charge_level.current" - " is %d", *value); - - if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) { - int new, max; - - if (battchg_last > 0) - max = battchg_last; - else - max = battchg_design; - - new = battchg_cur * 5 / max; - - telephony_update_indicator(ofono_indicators, "battchg", new); - } -done: - dbus_message_unref(reply); - remove_pending(call); -} - -static void hal_get_integer(const char *path, const char *key, void *user_data) -{ - send_method_call("org.freedesktop.Hal", path, - "org.freedesktop.Hal.Device", - "GetPropertyInteger", - hal_battery_level_reply, user_data, - DBUS_TYPE_STRING, &key, - DBUS_TYPE_INVALID); -} - -static gboolean handle_hal_property_modified(DBusConnection *conn, - DBusMessage *msg, void *data) -{ - const char *path; - DBusMessageIter iter, array; - dbus_int32_t num_changes; - - path = dbus_message_get_path(msg); - - dbus_message_iter_init(msg, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) { - error("Unexpected signature in hal PropertyModified signal"); - return TRUE; - } - - dbus_message_iter_get_basic(&iter, &num_changes); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature in hal PropertyModified signal"); - return TRUE; - } - - dbus_message_iter_recurse(&iter, &array); - - while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) { - DBusMessageIter prop; - const char *name; - dbus_bool_t added, removed; - - dbus_message_iter_recurse(&array, &prop); - - if (!iter_get_basic_args(&prop, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_BOOLEAN, &added, - DBUS_TYPE_BOOLEAN, &removed, - DBUS_TYPE_INVALID)) { - error("Invalid hal PropertyModified parameters"); - break; - } - - if (g_str_equal(name, "battery.charge_level.last_full")) - hal_get_integer(path, name, &battchg_last); - else if (g_str_equal(name, "battery.charge_level.current")) - hal_get_integer(path, name, &battchg_cur); - else if (g_str_equal(name, "battery.charge_level.design")) - hal_get_integer(path, name, &battchg_design); - - dbus_message_iter_next(&array); - } - - return TRUE; -} - -static void add_watch(const char *sender, const char *path, - const char *interface, const char *member, - GDBusSignalFunction function) -{ - guint watch; - - watch = g_dbus_add_signal_watch(connection, sender, path, interface, - member, function, NULL, NULL); - - watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch)); -} - -static void hal_find_device_reply(DBusPendingCall *call, void *user_data) -{ - DBusMessage *reply; - DBusError err; - DBusMessageIter iter, sub; - int type; - const char *path; - - DBG("begin of hal_find_device_reply()"); - reply = dbus_pending_call_steal_reply(call); - - dbus_error_init(&err); - - if (dbus_set_error_from_message(&err, reply)) { - error("hald replied with an error: %s, %s", - err.name, err.message); - dbus_error_free(&err); - goto done; - } - - dbus_message_iter_init(reply, &iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { - error("Unexpected signature in hal_find_device_reply()"); - goto done; - } - - dbus_message_iter_recurse(&iter, &sub); - - type = dbus_message_iter_get_arg_type(&sub); - - if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) { - error("No hal device with battery capability found"); - goto done; - } - - dbus_message_iter_get_basic(&sub, &path); - - DBG("telephony-ofono: found battery device at %s", path); - - add_watch(NULL, path, "org.freedesktop.Hal.Device", - "PropertyModified", handle_hal_property_modified); - - hal_get_integer(path, "battery.charge_level.last_full", &battchg_last); - hal_get_integer(path, "battery.charge_level.current", &battchg_cur); - hal_get_integer(path, "battery.charge_level.design", &battchg_design); -done: - dbus_message_unref(reply); - remove_pending(call); -} - -static void handle_service_connect(DBusConnection *conn, void *user_data) -{ - DBG("telephony-ofono: %s found", OFONO_BUS_NAME); - - send_method_call(OFONO_BUS_NAME, OFONO_PATH, - OFONO_MANAGER_INTERFACE, "GetModems", - get_modems_reply, NULL, DBUS_TYPE_INVALID); -} - -static void handle_service_disconnect(DBusConnection *conn, void *user_data) -{ - DBG("telephony-ofono: %s exitted", OFONO_BUS_NAME); - - if (modem_obj_path) - modem_removed(modem_obj_path); -} - -int telephony_init(void) -{ - uint32_t features = AG_FEATURE_EC_ANDOR_NR | - AG_FEATURE_INBAND_RINGTONE | - AG_FEATURE_REJECT_A_CALL | - AG_FEATURE_ENHANCED_CALL_STATUS | - AG_FEATURE_ENHANCED_CALL_CONTROL | - AG_FEATURE_EXTENDED_ERROR_RESULT_CODES | - AG_FEATURE_THREE_WAY_CALLING; - const char *battery_cap = "battery"; - int ret; - guint watch; - - connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); - - add_watch(OFONO_BUS_NAME, NULL, OFONO_MODEM_INTERFACE, - "PropertyChanged", handle_modem_property_changed); - add_watch(OFONO_BUS_NAME, NULL, OFONO_NETWORKREG_INTERFACE, - "PropertyChanged", handle_network_property_changed); - add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE, - "ModemAdded", handle_manager_modem_added); - add_watch(OFONO_BUS_NAME, NULL, OFONO_MANAGER_INTERFACE, - "ModemRemoved", handle_manager_modem_removed); - add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE, - "CallAdded", handle_vcmanager_call_added); - add_watch(OFONO_BUS_NAME, NULL, OFONO_VCMANAGER_INTERFACE, - "CallRemoved", handle_vcmanager_call_removed); - - watch = g_dbus_add_service_watch(connection, OFONO_BUS_NAME, - handle_service_connect, - handle_service_disconnect, - NULL, NULL); - if (watch == 0) - return -ENOMEM; - - watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch)); - - ret = send_method_call("org.freedesktop.Hal", - "/org/freedesktop/Hal/Manager", - "org.freedesktop.Hal.Manager", - "FindDeviceByCapability", - hal_find_device_reply, NULL, - DBUS_TYPE_STRING, &battery_cap, - DBUS_TYPE_INVALID); - if (ret < 0) - return ret; - - DBG("telephony_init() successfully"); - - telephony_ready_ind(features, ofono_indicators, BTRH_NOT_SUPPORTED, - chld_str); - - return ret; -} - -static void remove_watch(gpointer data) -{ - g_dbus_remove_watch(connection, GPOINTER_TO_UINT(data)); -} - -static void pending_free(void *data) -{ - DBusPendingCall *call = data; - - if (!dbus_pending_call_get_completed(call)) - dbus_pending_call_cancel(call); - - dbus_pending_call_unref(call); -} - -void telephony_exit(void) -{ - DBG(""); - - g_free(last_dialed_number); - last_dialed_number = NULL; - - if (modem_obj_path) - modem_removed(modem_obj_path); - - g_slist_free_full(watches, remove_watch); - watches = NULL; - - g_slist_free_full(pending, pending_free); - pending = NULL; - - dbus_connection_unref(connection); - connection = NULL; - - telephony_deinit(); -} diff -Nru bluez-4.101/audio/transport.c bluez-5.23/audio/transport.c --- bluez-4.101/audio/transport.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/transport.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1147 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2007 Nokia Corporation - * Copyright (C) 2004-2009 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include - -#include -#include - -#include "../src/adapter.h" -#include "../src/dbus-common.h" - -#include "log.h" -#include "error.h" -#include "device.h" -#include "avdtp.h" -#include "media.h" -#include "transport.h" -#include "a2dp.h" -#include "headset.h" -#include "gateway.h" -#include "avrcp.h" - -#define MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport" - -struct media_request { - DBusMessage *msg; - guint id; -}; - -struct media_owner { - struct media_transport *transport; - struct media_request *pending; - char *name; - char *accesstype; - guint watch; -}; - -struct a2dp_transport { - struct avdtp *session; - uint16_t delay; - uint16_t volume; -}; - -struct headset_transport { - struct audio_device *device; - unsigned int nrec_id; -}; - -struct media_transport { - DBusConnection *conn; - char *path; /* Transport object path */ - struct audio_device *device; /* Transport device */ - struct media_endpoint *endpoint; /* Transport endpoint */ - GSList *owners; /* Transport owners */ - uint8_t *configuration; /* Transport configuration */ - int size; /* Transport configuration size */ - int fd; /* Transport file descriptor */ - uint16_t imtu; /* Transport input mtu */ - uint16_t omtu; /* Transport output mtu */ - gboolean read_lock; - gboolean write_lock; - gboolean in_use; - guint (*resume) (struct media_transport *transport, - struct media_owner *owner); - guint (*suspend) (struct media_transport *transport, - struct media_owner *owner); - void (*cancel) (struct media_transport *transport, - guint id); - void (*get_properties) ( - struct media_transport *transport, - DBusMessageIter *dict); - int (*set_property) ( - struct media_transport *transport, - const char *property, - DBusMessageIter *value); - GDestroyNotify destroy; - void *data; -}; - -void media_transport_destroy(struct media_transport *transport) -{ - char *path; - - path = g_strdup(transport->path); - g_dbus_unregister_interface(transport->conn, path, - MEDIA_TRANSPORT_INTERFACE); - - g_free(path); -} - -static struct media_request *media_request_create(DBusMessage *msg, guint id) -{ - struct media_request *req; - - req = g_new0(struct media_request, 1); - req->msg = dbus_message_ref(msg); - req->id = id; - - DBG("Request created: method=%s id=%u", dbus_message_get_member(msg), - id); - - return req; -} - -static void media_request_reply(struct media_request *req, - DBusConnection *conn, int err) -{ - DBusMessage *reply; - - DBG("Request %s Reply %s", dbus_message_get_member(req->msg), - strerror(err)); - - if (!err) - reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID); - else - reply = g_dbus_create_error(req->msg, - ERROR_INTERFACE ".Failed", - "%s", strerror(err)); - - g_dbus_send_message(conn, reply); -} - -static gboolean media_transport_release(struct media_transport *transport, - const char *accesstype) -{ - if (g_strstr_len(accesstype, -1, "r") != NULL) { - transport->read_lock = FALSE; - DBG("Transport %s: read lock released", transport->path); - } - - if (g_strstr_len(accesstype, -1, "w") != NULL) { - transport->write_lock = FALSE; - DBG("Transport %s: write lock released", transport->path); - } - - return TRUE; -} - -static void media_owner_remove(struct media_owner *owner) -{ - struct media_transport *transport = owner->transport; - struct media_request *req = owner->pending; - - if (!req) - return; - - DBG("Owner %s Request %s", owner->name, - dbus_message_get_member(req->msg)); - - if (req->id) - transport->cancel(transport, req->id); - - owner->pending = NULL; - if (req->msg) - dbus_message_unref(req->msg); - - g_free(req); -} - -static void media_owner_free(struct media_owner *owner) -{ - DBG("Owner %s", owner->name); - - media_owner_remove(owner); - - g_free(owner->name); - g_free(owner->accesstype); - g_free(owner); -} - -static void media_transport_remove(struct media_transport *transport, - struct media_owner *owner) -{ - DBG("Transport %s Owner %s", transport->path, owner->name); - - media_transport_release(transport, owner->accesstype); - - /* Reply if owner has a pending request */ - if (owner->pending) - media_request_reply(owner->pending, transport->conn, EIO); - - transport->owners = g_slist_remove(transport->owners, owner); - - if (owner->watch) - g_dbus_remove_watch(transport->conn, owner->watch); - - media_owner_free(owner); - - /* Suspend if there is no longer any owner */ - if (transport->owners == NULL && transport->in_use) - transport->suspend(transport, NULL); -} - -static gboolean media_transport_set_fd(struct media_transport *transport, - int fd, uint16_t imtu, uint16_t omtu) -{ - if (transport->fd == fd) - return TRUE; - - transport->fd = fd; - transport->imtu = imtu; - transport->omtu = omtu; - - info("%s: fd(%d) ready", transport->path, fd); - - return TRUE; -} - -static void a2dp_resume_complete(struct avdtp *session, - struct avdtp_error *err, void *user_data) -{ - struct media_owner *owner = user_data; - struct media_request *req = owner->pending; - struct media_transport *transport = owner->transport; - struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint); - struct avdtp_stream *stream; - int fd; - uint16_t imtu, omtu; - gboolean ret; - - req->id = 0; - - if (err) - goto fail; - - stream = a2dp_sep_get_stream(sep); - if (stream == NULL) - goto fail; - - ret = avdtp_stream_get_transport(stream, &fd, &imtu, &omtu, NULL); - if (ret == FALSE) - goto fail; - - media_transport_set_fd(transport, fd, imtu, omtu); - - if (g_strstr_len(owner->accesstype, -1, "r") == NULL) - imtu = 0; - - if (g_strstr_len(owner->accesstype, -1, "w") == NULL) - omtu = 0; - - ret = g_dbus_send_reply(transport->conn, req->msg, - DBUS_TYPE_UNIX_FD, &fd, - DBUS_TYPE_UINT16, &imtu, - DBUS_TYPE_UINT16, &omtu, - DBUS_TYPE_INVALID); - if (ret == FALSE) - goto fail; - - media_owner_remove(owner); - - return; - -fail: - media_transport_remove(transport, owner); -} - -static guint resume_a2dp(struct media_transport *transport, - struct media_owner *owner) -{ - struct a2dp_transport *a2dp = transport->data; - struct media_endpoint *endpoint = transport->endpoint; - struct audio_device *device = transport->device; - struct a2dp_sep *sep = media_endpoint_get_sep(endpoint); - - if (a2dp->session == NULL) { - a2dp->session = avdtp_get(&device->src, &device->dst); - if (a2dp->session == NULL) - return 0; - } - - if (transport->in_use == TRUE) - goto done; - - transport->in_use = a2dp_sep_lock(sep, a2dp->session); - if (transport->in_use == FALSE) - return 0; - -done: - return a2dp_resume(a2dp->session, sep, a2dp_resume_complete, owner); -} - -static void a2dp_suspend_complete(struct avdtp *session, - struct avdtp_error *err, void *user_data) -{ - struct media_owner *owner = user_data; - struct media_transport *transport = owner->transport; - struct a2dp_transport *a2dp = transport->data; - struct a2dp_sep *sep = media_endpoint_get_sep(transport->endpoint); - - /* Release always succeeds */ - if (owner->pending) { - owner->pending->id = 0; - media_request_reply(owner->pending, transport->conn, 0); - media_owner_remove(owner); - } - - a2dp_sep_unlock(sep, a2dp->session); - transport->in_use = FALSE; - media_transport_remove(transport, owner); -} - -static guint suspend_a2dp(struct media_transport *transport, - struct media_owner *owner) -{ - struct a2dp_transport *a2dp = transport->data; - struct media_endpoint *endpoint = transport->endpoint; - struct a2dp_sep *sep = media_endpoint_get_sep(endpoint); - - if (!owner) { - a2dp_sep_unlock(sep, a2dp->session); - transport->in_use = FALSE; - return 0; - } - - return a2dp_suspend(a2dp->session, sep, a2dp_suspend_complete, owner); -} - -static void cancel_a2dp(struct media_transport *transport, guint id) -{ - a2dp_cancel(transport->device, id); -} - -static void headset_resume_complete(struct audio_device *dev, void *user_data) -{ - struct media_owner *owner = user_data; - struct media_request *req = owner->pending; - struct media_transport *transport = owner->transport; - int fd; - uint16_t imtu, omtu; - gboolean ret; - - req->id = 0; - - if (dev == NULL) - goto fail; - - fd = headset_get_sco_fd(dev); - if (fd < 0) - goto fail; - - imtu = 48; - omtu = 48; - - media_transport_set_fd(transport, fd, imtu, omtu); - - if (g_strstr_len(owner->accesstype, -1, "r") == NULL) - imtu = 0; - - if (g_strstr_len(owner->accesstype, -1, "w") == NULL) - omtu = 0; - - ret = g_dbus_send_reply(transport->conn, req->msg, - DBUS_TYPE_UNIX_FD, &fd, - DBUS_TYPE_UINT16, &imtu, - DBUS_TYPE_UINT16, &omtu, - DBUS_TYPE_INVALID); - if (ret == FALSE) - goto fail; - - media_owner_remove(owner); - - return; - -fail: - media_transport_remove(transport, owner); -} - -static guint resume_headset(struct media_transport *transport, - struct media_owner *owner) -{ - struct audio_device *device = transport->device; - - if (transport->in_use == TRUE) - goto done; - - transport->in_use = headset_lock(device, HEADSET_LOCK_READ | - HEADSET_LOCK_WRITE); - if (transport->in_use == FALSE) - return 0; - -done: - return headset_request_stream(device, headset_resume_complete, - owner); -} - -static void headset_suspend_complete(struct audio_device *dev, void *user_data) -{ - struct media_owner *owner = user_data; - struct media_transport *transport = owner->transport; - - /* Release always succeeds */ - if (owner->pending) { - owner->pending->id = 0; - media_request_reply(owner->pending, transport->conn, 0); - media_owner_remove(owner); - } - - headset_unlock(dev, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE); - transport->in_use = FALSE; - media_transport_remove(transport, owner); -} - -static guint suspend_headset(struct media_transport *transport, - struct media_owner *owner) -{ - struct audio_device *device = transport->device; - - if (!owner) { - headset_unlock(device, HEADSET_LOCK_READ | HEADSET_LOCK_WRITE); - transport->in_use = FALSE; - return 0; - } - - return headset_suspend_stream(device, headset_suspend_complete, owner); -} - -static void cancel_headset(struct media_transport *transport, guint id) -{ - headset_cancel_stream(transport->device, id); -} - -static void gateway_resume_complete(struct audio_device *dev, GError *err, - void *user_data) -{ - struct media_owner *owner = user_data; - struct media_request *req = owner->pending; - struct media_transport *transport = owner->transport; - int fd; - uint16_t imtu, omtu; - gboolean ret; - - req->id = 0; - - if (dev == NULL) - goto fail; - - if (err) { - error("Failed to resume gateway: error %s", err->message); - goto fail; - } - - fd = gateway_get_sco_fd(dev); - if (fd < 0) - goto fail; - - imtu = 48; - omtu = 48; - - media_transport_set_fd(transport, fd, imtu, omtu); - - if (g_strstr_len(owner->accesstype, -1, "r") == NULL) - imtu = 0; - - if (g_strstr_len(owner->accesstype, -1, "w") == NULL) - omtu = 0; - - ret = g_dbus_send_reply(transport->conn, req->msg, - DBUS_TYPE_UNIX_FD, &fd, - DBUS_TYPE_UINT16, &imtu, - DBUS_TYPE_UINT16, &omtu, - DBUS_TYPE_INVALID); - if (ret == FALSE) - goto fail; - - media_owner_remove(owner); - - return; - -fail: - media_transport_remove(transport, owner); -} - -static guint resume_gateway(struct media_transport *transport, - struct media_owner *owner) -{ - struct audio_device *device = transport->device; - - if (transport->in_use == TRUE) - goto done; - - transport->in_use = gateway_lock(device, GATEWAY_LOCK_READ | - GATEWAY_LOCK_WRITE); - if (transport->in_use == FALSE) - return 0; - -done: - return gateway_request_stream(device, gateway_resume_complete, - owner); -} - -static gboolean gateway_suspend_complete(gpointer user_data) -{ - struct media_owner *owner = user_data; - struct media_transport *transport = owner->transport; - struct audio_device *device = transport->device; - - /* Release always succeeds */ - if (owner->pending) { - owner->pending->id = 0; - media_request_reply(owner->pending, transport->conn, 0); - media_owner_remove(owner); - } - - gateway_unlock(device, GATEWAY_LOCK_READ | GATEWAY_LOCK_WRITE); - transport->in_use = FALSE; - media_transport_remove(transport, owner); - return FALSE; -} - -static guint suspend_gateway(struct media_transport *transport, - struct media_owner *owner) -{ - struct audio_device *device = transport->device; - static int id = 1; - - if (!owner) { - gateway_unlock(device, GATEWAY_LOCK_READ | GATEWAY_LOCK_WRITE); - transport->in_use = FALSE; - return 0; - } - - gateway_suspend_stream(device); - g_idle_add(gateway_suspend_complete, owner); - return id++; -} - -static void cancel_gateway(struct media_transport *transport, guint id) -{ - gateway_cancel_stream(transport->device, id); -} - -static void media_owner_exit(DBusConnection *connection, void *user_data) -{ - struct media_owner *owner = user_data; - - owner->watch = 0; - - media_owner_remove(owner); - - media_transport_remove(owner->transport, owner); -} - -static gboolean media_transport_acquire(struct media_transport *transport, - const char *accesstype) -{ - gboolean read_lock = FALSE, write_lock = FALSE; - - if (g_strstr_len(accesstype, -1, "r") != NULL) { - if (transport->read_lock == TRUE) - return FALSE; - read_lock = TRUE; - } - - if (g_strstr_len(accesstype, -1, "w") != NULL) { - if (transport->write_lock == TRUE) - return FALSE; - write_lock = TRUE; - } - - /* Check invalid accesstype */ - if (read_lock == FALSE && write_lock == FALSE) - return FALSE; - - if (read_lock) { - transport->read_lock = read_lock; - DBG("Transport %s: read lock acquired", transport->path); - } - - if (write_lock) { - transport->write_lock = write_lock; - DBG("Transport %s: write lock acquired", transport->path); - } - - - return TRUE; -} - -static void media_transport_add(struct media_transport *transport, - struct media_owner *owner) -{ - DBG("Transport %s Owner %s", transport->path, owner->name); - transport->owners = g_slist_append(transport->owners, owner); - owner->transport = transport; - owner->watch = g_dbus_add_disconnect_watch(transport->conn, owner->name, - media_owner_exit, - owner, NULL); -} - -static struct media_owner *media_owner_create(DBusConnection *conn, - DBusMessage *msg, - const char *accesstype) -{ - struct media_owner *owner; - - owner = g_new0(struct media_owner, 1); - owner->name = g_strdup(dbus_message_get_sender(msg)); - owner->accesstype = g_strdup(accesstype); - - DBG("Owner created: sender=%s accesstype=%s", owner->name, - accesstype); - - return owner; -} - -static void media_owner_add(struct media_owner *owner, - struct media_request *req) -{ - DBG("Owner %s Request %s", owner->name, - dbus_message_get_member(req->msg)); - - owner->pending = req; -} - -static struct media_owner *media_transport_find_owner( - struct media_transport *transport, - const char *name) -{ - GSList *l; - - for (l = transport->owners; l; l = l->next) { - struct media_owner *owner = l->data; - - if (g_strcmp0(owner->name, name) == 0) - return owner; - } - - return NULL; -} - -static DBusMessage *acquire(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct media_transport *transport = data; - struct media_owner *owner; - struct media_request *req; - const char *accesstype, *sender; - guint id; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &accesstype, - DBUS_TYPE_INVALID)) - return NULL; - - sender = dbus_message_get_sender(msg); - - owner = media_transport_find_owner(transport, sender); - if (owner != NULL) - return btd_error_not_authorized(msg); - - if (media_transport_acquire(transport, accesstype) == FALSE) - return btd_error_not_authorized(msg); - - owner = media_owner_create(conn, msg, accesstype); - id = transport->resume(transport, owner); - if (id == 0) { - media_transport_release(transport, accesstype); - media_owner_free(owner); - return btd_error_not_authorized(msg); - } - - req = media_request_create(msg, id); - media_owner_add(owner, req); - media_transport_add(transport, owner); - - return NULL; -} - -static DBusMessage *release(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct media_transport *transport = data; - struct media_owner *owner; - const char *accesstype, *sender; - struct media_request *req; - - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &accesstype, - DBUS_TYPE_INVALID)) - return NULL; - - sender = dbus_message_get_sender(msg); - - owner = media_transport_find_owner(transport, sender); - if (owner == NULL) - return btd_error_not_authorized(msg); - - if (g_strcmp0(owner->accesstype, accesstype) == 0) { - guint id; - - /* Not the last owner, no need to suspend */ - if (g_slist_length(transport->owners) != 1) { - media_transport_remove(transport, owner); - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - } - - if (owner->pending) { - const char *member; - - member = dbus_message_get_member(owner->pending->msg); - /* Cancel Acquire request if that exist */ - if (g_str_equal(member, "Acquire")) - media_owner_remove(owner); - else - return btd_error_in_progress(msg); - } - - id = transport->suspend(transport, owner); - if (id == 0) { - media_transport_remove(transport, owner); - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - } - - req = media_request_create(msg, id); - media_owner_add(owner, req); - - return NULL; - } else if (g_strstr_len(owner->accesstype, -1, accesstype) != NULL) { - media_transport_release(transport, accesstype); - g_strdelimit(owner->accesstype, accesstype, ' '); - } else - return btd_error_not_authorized(msg); - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static int set_property_a2dp(struct media_transport *transport, - const char *property, - DBusMessageIter *value) -{ - struct a2dp_transport *a2dp = transport->data; - - if (g_strcmp0(property, "Delay") == 0) { - if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) - return -EINVAL; - dbus_message_iter_get_basic(value, &a2dp->delay); - - /* FIXME: send new delay */ - return 0; - } else if (g_strcmp0(property, "Volume") == 0) { - uint16_t volume; - - if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) - return -EINVAL; - - dbus_message_iter_get_basic(value, &volume); - - if (volume > 127) - return -EINVAL; - - if (a2dp->volume == volume) - return 0; - - return avrcp_set_volume(transport->device, volume); - } - - return -EINVAL; -} - -static int set_property_headset(struct media_transport *transport, - const char *property, - DBusMessageIter *value) -{ - if (g_strcmp0(property, "NREC") == 0) { - gboolean nrec; - - if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) - return -EINVAL; - dbus_message_iter_get_basic(value, &nrec); - - /* FIXME: set new nrec */ - return 0; - } else if (g_strcmp0(property, "InbandRingtone") == 0) { - gboolean inband; - - if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) - return -EINVAL; - dbus_message_iter_get_basic(value, &inband); - - /* FIXME: set new inband */ - return 0; - } - - return -EINVAL; -} - -static int set_property_gateway(struct media_transport *transport, - const char *property, - DBusMessageIter *value) -{ - return -EINVAL; -} - -static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct media_transport *transport = data; - DBusMessageIter iter; - DBusMessageIter value; - const char *property, *sender; - GSList *l; - int err; - - if (!dbus_message_iter_init(msg, &iter)) - return btd_error_invalid_args(msg); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - return btd_error_invalid_args(msg); - - dbus_message_iter_get_basic(&iter, &property); - dbus_message_iter_next(&iter); - - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) - return btd_error_invalid_args(msg); - dbus_message_iter_recurse(&iter, &value); - - sender = dbus_message_get_sender(msg); - err = -EINVAL; - - /* Check if sender has acquired the transport */ - for (l = transport->owners; l; l = l->next) { - struct media_owner *owner = l->data; - - if (g_strcmp0(owner->name, sender) == 0) { - err = transport->set_property(transport, property, - &value); - break; - } - } - - if (err < 0) { - if (err == -EINVAL) - return btd_error_invalid_args(msg); - return btd_error_failed(msg, strerror(-err)); - } - - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static void get_properties_a2dp(struct media_transport *transport, - DBusMessageIter *dict) -{ - struct a2dp_transport *a2dp = transport->data; - - dict_append_entry(dict, "Delay", DBUS_TYPE_UINT16, &a2dp->delay); - - if (a2dp->volume <= 127) - dict_append_entry(dict, "Volume", DBUS_TYPE_UINT16, - &a2dp->volume); -} - -static void get_properties_headset(struct media_transport *transport, - DBusMessageIter *dict) -{ - gboolean nrec, inband; - const char *routing; - - nrec = headset_get_nrec(transport->device); - dict_append_entry(dict, "NREC", DBUS_TYPE_BOOLEAN, &nrec); - - inband = headset_get_inband(transport->device); - dict_append_entry(dict, "InbandRingtone", DBUS_TYPE_BOOLEAN, &inband); - - routing = headset_get_sco_hci(transport->device) ? "HCI" : "PCM"; - dict_append_entry(dict, "Routing", DBUS_TYPE_STRING, &routing); -} - -static void get_properties_gateway(struct media_transport *transport, - DBusMessageIter *dict) -{ - /* None */ -} - -void transport_get_properties(struct media_transport *transport, - DBusMessageIter *iter) -{ - DBusMessageIter dict; - const char *uuid; - uint8_t codec; - - dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, - DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING - DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING - DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - - /* Device */ - dict_append_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH, - &transport->device->path); - - uuid = media_endpoint_get_uuid(transport->endpoint); - dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid); - - codec = media_endpoint_get_codec(transport->endpoint); - dict_append_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec); - - dict_append_array(&dict, "Configuration", DBUS_TYPE_BYTE, - &transport->configuration, transport->size); - - if (transport->get_properties) - transport->get_properties(transport, &dict); - - dbus_message_iter_close_container(iter, &dict); -} - -static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg, - void *data) -{ - struct media_transport *transport = data; - DBusMessage *reply; - DBusMessageIter iter; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return NULL; - - dbus_message_iter_init_append(reply, &iter); - - transport_get_properties(transport, &iter); - - return reply; -} - -static const GDBusMethodTable transport_methods[] = { - { GDBUS_METHOD("GetProperties", - NULL, GDBUS_ARGS({ "properties", "a{sv}" }), - get_properties) }, - { GDBUS_ASYNC_METHOD("Acquire", - GDBUS_ARGS({ "access_type", "s" }), - GDBUS_ARGS({ "fd", "h" }, { "mtu_r", "q" }, - { "mtu_w", "q" } ), - acquire) }, - { GDBUS_ASYNC_METHOD("Release", - GDBUS_ARGS({ "access_type", "s" }), NULL, - release ) }, - { GDBUS_ASYNC_METHOD("SetProperty", - GDBUS_ARGS({ "name", "s" }, { "value", "v" }), - NULL, set_property) }, - { }, -}; - -static const GDBusSignalTable transport_signals[] = { - { GDBUS_SIGNAL("PropertyChanged", - GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { } -}; - -static void destroy_a2dp(void *data) -{ - struct a2dp_transport *a2dp = data; - - if (a2dp->session) - avdtp_unref(a2dp->session); - - g_free(a2dp); -} - -static void destroy_headset(void *data) -{ - struct headset_transport *headset = data; - - if (headset->nrec_id > 0) - headset_remove_nrec_cb(headset->device, headset->nrec_id); - - g_free(headset); -} - -static void media_transport_free(void *data) -{ - struct media_transport *transport = data; - GSList *l = transport->owners; - - while (l) { - struct media_owner *owner = l->data; - l = l->next; - media_transport_remove(transport, owner); - } - - g_slist_free(transport->owners); - - if (transport->destroy != NULL) - transport->destroy(transport->data); - - if (transport->conn) - dbus_connection_unref(transport->conn); - - g_free(transport->configuration); - g_free(transport->path); - g_free(transport); -} - -static void headset_nrec_changed(struct audio_device *dev, gboolean nrec, - void *user_data) -{ - struct media_transport *transport = user_data; - - DBG(""); - - emit_property_changed(transport->conn, transport->path, - MEDIA_TRANSPORT_INTERFACE, "NREC", - DBUS_TYPE_BOOLEAN, &nrec); -} - -struct media_transport *media_transport_create(DBusConnection *conn, - struct media_endpoint *endpoint, - struct audio_device *device, - uint8_t *configuration, - size_t size) -{ - struct media_transport *transport; - const char *uuid; - static int fd = 0; - - transport = g_new0(struct media_transport, 1); - transport->conn = dbus_connection_ref(conn); - transport->device = device; - transport->endpoint = endpoint; - transport->configuration = g_new(uint8_t, size); - memcpy(transport->configuration, configuration, size); - transport->size = size; - transport->path = g_strdup_printf("%s/fd%d", device->path, fd++); - transport->fd = -1; - - uuid = media_endpoint_get_uuid(endpoint); - if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0 || - strcasecmp(uuid, A2DP_SINK_UUID) == 0) { - struct a2dp_transport *a2dp; - - a2dp = g_new0(struct a2dp_transport, 1); - a2dp->volume = -1; - - transport->resume = resume_a2dp; - transport->suspend = suspend_a2dp; - transport->cancel = cancel_a2dp; - transport->get_properties = get_properties_a2dp; - transport->set_property = set_property_a2dp; - transport->data = a2dp; - transport->destroy = destroy_a2dp; - } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 || - strcasecmp(uuid, HSP_AG_UUID) == 0) { - struct headset_transport *headset; - - headset = g_new0(struct headset_transport, 1); - headset->device = device; - headset->nrec_id = headset_add_nrec_cb(device, - headset_nrec_changed, - transport); - - transport->resume = resume_headset; - transport->suspend = suspend_headset; - transport->cancel = cancel_headset; - transport->get_properties = get_properties_headset; - transport->set_property = set_property_headset; - transport->data = headset; - transport->destroy = destroy_headset; - } else if (strcasecmp(uuid, HFP_HS_UUID) == 0 || - strcasecmp(uuid, HSP_HS_UUID) == 0) { - transport->resume = resume_gateway; - transport->suspend = suspend_gateway; - transport->cancel = cancel_gateway; - transport->get_properties = get_properties_gateway; - transport->set_property = set_property_gateway; - } else - goto fail; - - if (g_dbus_register_interface(transport->conn, transport->path, - MEDIA_TRANSPORT_INTERFACE, - transport_methods, transport_signals, NULL, - transport, media_transport_free) == FALSE) { - error("Could not register transport %s", transport->path); - goto fail; - } - - return transport; - -fail: - media_transport_free(transport); - return NULL; -} - -const char *media_transport_get_path(struct media_transport *transport) -{ - return transport->path; -} - -void media_transport_update_delay(struct media_transport *transport, - uint16_t delay) -{ - struct a2dp_transport *a2dp = transport->data; - - /* Check if delay really changed */ - if (a2dp->delay == delay) - return; - - a2dp->delay = delay; - - emit_property_changed(transport->conn, transport->path, - MEDIA_TRANSPORT_INTERFACE, "Delay", - DBUS_TYPE_UINT16, &a2dp->delay); -} - -struct audio_device *media_transport_get_dev(struct media_transport *transport) -{ - return transport->device; -} - -void media_transport_update_volume(struct media_transport *transport, - uint8_t volume) -{ - struct a2dp_transport *a2dp = transport->data; - - /* Check if volume really changed */ - if (a2dp->volume == volume) - return; - - a2dp->volume = volume; - - emit_property_changed(transport->conn, transport->path, - MEDIA_TRANSPORT_INTERFACE, "Volume", - DBUS_TYPE_UINT16, &a2dp->volume); -} diff -Nru bluez-4.101/audio/transport.h bluez-5.23/audio/transport.h --- bluez-4.101/audio/transport.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/transport.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2007 Nokia Corporation - * Copyright (C) 2004-2009 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -struct media_transport; - -struct media_transport *media_transport_create(DBusConnection *conn, - struct media_endpoint *endpoint, - struct audio_device *device, - uint8_t *configuration, - size_t size); - -void media_transport_destroy(struct media_transport *transport); -const char *media_transport_get_path(struct media_transport *transport); -struct audio_device *media_transport_get_dev(struct media_transport *transport); -void media_transport_update_delay(struct media_transport *transport, - uint16_t delay); -void media_transport_update_volume(struct media_transport *transport, - uint8_t volume); -void transport_get_properties(struct media_transport *transport, - DBusMessageIter *iter); diff -Nru bluez-4.101/audio/unix.c bluez-5.23/audio/unix.c --- bluez-4.101/audio/unix.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/audio/unix.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1909 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "log.h" -#include "ipc.h" -#include "device.h" -#include "manager.h" -#include "avdtp.h" -#include "media.h" -#include "a2dp.h" -#include "headset.h" -#include "sink.h" -#include "source.h" -#include "gateway.h" -#include "unix.h" - -#define check_nul(str) (str[sizeof(str) - 1] == '\0') - -typedef enum { - TYPE_NONE, - TYPE_HEADSET, - TYPE_GATEWAY, - TYPE_SINK, - TYPE_SOURCE -} service_type_t; - -typedef void (*notify_cb_t) (struct audio_device *dev, void *data); - -struct a2dp_data { - struct avdtp *session; - struct avdtp_stream *stream; - struct a2dp_sep *sep; -}; - -struct headset_data { - gboolean locked; -}; - -struct unix_client { - struct audio_device *dev; - GSList *caps; - service_type_t type; - char *interface; - uint8_t seid; - union { - struct a2dp_data a2dp; - struct headset_data hs; - } d; - int sock; - int lock; - int data_fd; /* To be deleted once two phase configuration is fully implemented */ - unsigned int req_id; - unsigned int cb_id; - gboolean (*cancel) (struct audio_device *dev, unsigned int id); -}; - -static GSList *clients = NULL; - -static int unix_sock = -1; - -static void client_free(void *data) -{ - struct unix_client *client = data; - - DBG("client_free(%p)", client); - - if (client->cancel && client->dev && client->req_id > 0) - client->cancel(client->dev, client->req_id); - - if (client->sock >= 0) - close(client->sock); - - g_slist_free_full(client->caps, g_free); - - g_free(client->interface); - g_free(client); -} - -static int set_nonblocking(int fd) -{ - long arg; - - arg = fcntl(fd, F_GETFL); - if (arg < 0) - return -errno; - - /* Return if already nonblocking */ - if (arg & O_NONBLOCK) - return 0; - - arg |= O_NONBLOCK; - if (fcntl(fd, F_SETFL, arg) < 0) - return -errno; - - return 0; -} - -/* Pass file descriptor through local domain sockets (AF_LOCAL, formerly - * AF_UNIX) and the sendmsg() system call with the cmsg_type field of a "struct - * cmsghdr" set to SCM_RIGHTS and the data being an integer value equal to the - * handle of the file descriptor to be passed. */ -static int unix_sendmsg_fd(int sock, int fd) -{ - char cmsg_b[CMSG_SPACE(sizeof(int))], m = 'm'; - struct cmsghdr *cmsg; - struct iovec iov = { &m, sizeof(m) }; - struct msghdr msgh; - - memset(&msgh, 0, sizeof(msgh)); - msgh.msg_iov = &iov; - msgh.msg_iovlen = 1; - msgh.msg_control = &cmsg_b; - msgh.msg_controllen = CMSG_LEN(sizeof(int)); - - cmsg = CMSG_FIRSTHDR(&msgh); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - /* Initialize the payload */ - memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - return sendmsg(sock, &msgh, MSG_NOSIGNAL); -} - -static void unix_ipc_sendmsg(struct unix_client *client, - const bt_audio_msg_header_t *msg) -{ - const char *type = bt_audio_strtype(msg->type); - const char *name = bt_audio_strname(msg->name); - - DBG("Audio API: %s -> %s", type, name); - - if (send(client->sock, msg, msg->length, 0) < 0) - error("Error %s(%d)", strerror(errno), errno); -} - -static void unix_ipc_error(struct unix_client *client, uint8_t name, int err) -{ - char buf[BT_SUGGESTED_BUFFER_SIZE]; - bt_audio_error_t *rsp = (void *) buf; - - if (!g_slist_find(clients, client)) - return; - - memset(buf, 0, sizeof(buf)); - rsp->h.type = BT_ERROR; - rsp->h.name = name; - rsp->h.length = sizeof(*rsp); - - rsp->posix_errno = err; - - DBG("sending error %s(%d)", strerror(err), err); - unix_ipc_sendmsg(client, &rsp->h); -} - -static service_type_t select_service(struct audio_device *dev, const char *interface) -{ - if (!interface) { - if (dev->sink && avdtp_is_connected(&dev->src, &dev->dst)) - return TYPE_SINK; - else if (dev->source && avdtp_is_connected(&dev->src, - &dev->dst)) - return TYPE_SOURCE; - else if (dev->headset && headset_is_active(dev)) - return TYPE_HEADSET; - else if (dev->sink) - return TYPE_SINK; - else if (dev->source) - return TYPE_SOURCE; - else if (dev->headset) - return TYPE_HEADSET; - } else if (!strcmp(interface, AUDIO_SOURCE_INTERFACE) && dev->source) - return TYPE_SOURCE; - else if (!strcmp(interface, AUDIO_SINK_INTERFACE) && dev->sink) - return TYPE_SINK; - else if (!strcmp(interface, AUDIO_HEADSET_INTERFACE) && dev->headset) - return TYPE_HEADSET; - else if (!strcmp(interface, AUDIO_GATEWAY_INTERFACE) && dev->gateway) - return TYPE_GATEWAY; - - return TYPE_NONE; -} - -static void stream_state_changed(struct avdtp_stream *stream, - avdtp_state_t old_state, - avdtp_state_t new_state, - struct avdtp_error *err, - void *user_data) -{ - struct unix_client *client = user_data; - struct a2dp_data *a2dp = &client->d.a2dp; - - switch (new_state) { - case AVDTP_STATE_IDLE: - if (a2dp->sep) { - a2dp_sep_unlock(a2dp->sep, a2dp->session); - a2dp->sep = NULL; - } - if (a2dp->session) { - avdtp_unref(a2dp->session); - a2dp->session = NULL; - } - a2dp->stream = NULL; - client->cb_id = 0; - break; - default: - break; - } -} - -static uint8_t headset_generate_capability(struct audio_device *dev, - codec_capabilities_t *codec) -{ - pcm_capabilities_t *pcm; - - codec->seid = BT_A2DP_SEID_RANGE + 1; - codec->transport = BT_CAPABILITIES_TRANSPORT_SCO; - codec->type = BT_HFP_CODEC_PCM; - codec->length = sizeof(*pcm); - - pcm = (void *) codec; - pcm->sampling_rate = 8000; - if (dev->headset) { - if (headset_get_nrec(dev)) - pcm->flags |= BT_PCM_FLAG_NREC; - if (!headset_get_sco_hci(dev)) - pcm->flags |= BT_PCM_FLAG_PCM_ROUTING; - codec->configured = headset_is_active(dev); - codec->lock = headset_get_lock(dev); - } else { - pcm->flags |= BT_PCM_FLAG_NREC; - codec->configured = TRUE; - codec->lock = 0; - } - - return codec->length; -} - -static void headset_discovery_complete(struct audio_device *dev, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_get_capabilities_rsp *rsp = (void *) buf; - uint8_t length; - - client->req_id = 0; - - if (!dev) - goto failed; - - memset(buf, 0, sizeof(buf)); - - length = headset_generate_capability(dev, (void *) rsp->data); - - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_GET_CAPABILITIES; - rsp->h.length = sizeof(*rsp) + length; - - ba2str(&dev->src, rsp->source); - ba2str(&dev->dst, rsp->destination); - strncpy(rsp->object, dev->path, sizeof(rsp->object)); - - unix_ipc_sendmsg(client, &rsp->h); - - return; - -failed: - error("discovery failed"); - unix_ipc_error(client, BT_SET_CONFIGURATION, EIO); -} - -static void headset_setup_complete(struct audio_device *dev, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_set_configuration_rsp *rsp = (void *) buf; - - client->req_id = 0; - - if (!dev) - goto failed; - - memset(buf, 0, sizeof(buf)); - - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_SET_CONFIGURATION; - rsp->h.length = sizeof(*rsp); - - rsp->link_mtu = 48; - - client->data_fd = headset_get_sco_fd(dev); - - unix_ipc_sendmsg(client, &rsp->h); - - return; - -failed: - error("config failed"); - unix_ipc_error(client, BT_SET_CONFIGURATION, EIO); -} - -static void gateway_setup_complete(struct audio_device *dev, GError *err, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_set_configuration_rsp *rsp = (void *) buf; - - if (err) { - unix_ipc_error(client, BT_SET_CONFIGURATION, err->code); - return; - } - - client->req_id = 0; - - memset(buf, 0, sizeof(buf)); - - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_SET_CONFIGURATION; - rsp->h.length = sizeof(*rsp); - - rsp->link_mtu = 48; - - client->data_fd = gateway_get_sco_fd(dev); - - unix_ipc_sendmsg(client, &rsp->h); -} - -static void headset_resume_complete(struct audio_device *dev, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_start_stream_rsp *rsp = (void *) buf; - struct bt_new_stream_ind *ind = (void *) buf; - - client->req_id = 0; - - if (!dev) - goto failed; - - client->data_fd = headset_get_sco_fd(dev); - if (client->data_fd < 0) { - error("Unable to get a SCO fd"); - goto failed; - } - - memset(buf, 0, sizeof(buf)); - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_START_STREAM; - rsp->h.length = sizeof(*rsp); - - unix_ipc_sendmsg(client, &rsp->h); - - memset(buf, 0, sizeof(buf)); - ind->h.type = BT_INDICATION; - ind->h.name = BT_NEW_STREAM; - ind->h.length = sizeof(*ind); - - unix_ipc_sendmsg(client, &ind->h); - - if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) { - error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); - goto failed; - } - - return; - -failed: - error("headset_resume_complete: resume failed"); - unix_ipc_error(client, BT_START_STREAM, EIO); -} - -static void gateway_resume_complete(struct audio_device *dev, GError *err, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_start_stream_rsp *rsp = (void *) buf; - struct bt_new_stream_ind *ind = (void *) buf; - - if (err) { - unix_ipc_error(client, BT_START_STREAM, err->code); - return; - } - - memset(buf, 0, sizeof(buf)); - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_START_STREAM; - rsp->h.length = sizeof(*rsp); - - unix_ipc_sendmsg(client, &rsp->h); - - memset(buf, 0, sizeof(buf)); - ind->h.type = BT_INDICATION; - ind->h.name = BT_NEW_STREAM; - ind->h.length = sizeof(*ind); - - unix_ipc_sendmsg(client, &ind->h); - - client->data_fd = gateway_get_sco_fd(dev); - if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) { - error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); - unix_ipc_error(client, BT_START_STREAM, EIO); - } - - client->req_id = 0; -} - -static void headset_suspend_complete(struct audio_device *dev, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_stop_stream_rsp *rsp = (void *) buf; - - if (!dev) - goto failed; - - memset(buf, 0, sizeof(buf)); - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_STOP_STREAM; - rsp->h.length = sizeof(*rsp); - - unix_ipc_sendmsg(client, &rsp->h); - - return; - -failed: - error("suspend failed"); - unix_ipc_error(client, BT_STOP_STREAM, EIO); -} - -static void print_mpeg12(struct mpeg_codec_cap *mpeg) -{ - DBG("Media Codec: MPEG12" - " Channel Modes: %s%s%s%s" - " Frequencies: %s%s%s%s%s%s" - " Layers: %s%s%s" - " CRC: %s", - mpeg->channel_mode & MPEG_CHANNEL_MODE_MONO ? "Mono " : "", - mpeg->channel_mode & MPEG_CHANNEL_MODE_DUAL_CHANNEL ? - "DualChannel " : "", - mpeg->channel_mode & MPEG_CHANNEL_MODE_STEREO ? "Stereo " : "", - mpeg->channel_mode & MPEG_CHANNEL_MODE_JOINT_STEREO ? - "JointStereo " : "", - mpeg->frequency & MPEG_SAMPLING_FREQ_16000 ? "16Khz " : "", - mpeg->frequency & MPEG_SAMPLING_FREQ_22050 ? "22.05Khz " : "", - mpeg->frequency & MPEG_SAMPLING_FREQ_24000 ? "24Khz " : "", - mpeg->frequency & MPEG_SAMPLING_FREQ_32000 ? "32Khz " : "", - mpeg->frequency & MPEG_SAMPLING_FREQ_44100 ? "44.1Khz " : "", - mpeg->frequency & MPEG_SAMPLING_FREQ_48000 ? "48Khz " : "", - mpeg->layer & MPEG_LAYER_MP1 ? "1 " : "", - mpeg->layer & MPEG_LAYER_MP2 ? "2 " : "", - mpeg->layer & MPEG_LAYER_MP3 ? "3 " : "", - mpeg->crc ? "Yes" : "No"); -} - -static void print_sbc(struct sbc_codec_cap *sbc) -{ - DBG("Media Codec: SBC" - " Channel Modes: %s%s%s%s" - " Frequencies: %s%s%s%s" - " Subbands: %s%s" - " Blocks: %s%s%s%s" - " Bitpool: %d-%d", - sbc->channel_mode & SBC_CHANNEL_MODE_MONO ? "Mono " : "", - sbc->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL ? - "DualChannel " : "", - sbc->channel_mode & SBC_CHANNEL_MODE_STEREO ? "Stereo " : "", - sbc->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO ? "JointStereo" : "", - sbc->frequency & SBC_SAMPLING_FREQ_16000 ? "16Khz " : "", - sbc->frequency & SBC_SAMPLING_FREQ_32000 ? "32Khz " : "", - sbc->frequency & SBC_SAMPLING_FREQ_44100 ? "44.1Khz " : "", - sbc->frequency & SBC_SAMPLING_FREQ_48000 ? "48Khz " : "", - sbc->subbands & SBC_SUBBANDS_4 ? "4 " : "", - sbc->subbands & SBC_SUBBANDS_8 ? "8 " : "", - sbc->block_length & SBC_BLOCK_LENGTH_4 ? "4 " : "", - sbc->block_length & SBC_BLOCK_LENGTH_8 ? "8 " : "", - sbc->block_length & SBC_BLOCK_LENGTH_12 ? "12 " : "", - sbc->block_length & SBC_BLOCK_LENGTH_16 ? "16 " : "", - sbc->min_bitpool, sbc->max_bitpool); -} - -static int a2dp_append_codec(struct bt_get_capabilities_rsp *rsp, - struct avdtp_service_capability *cap, - uint8_t seid, - uint8_t type, - uint8_t configured, - uint8_t lock) -{ - struct avdtp_media_codec_capability *codec_cap = (void *) cap->data; - codec_capabilities_t *codec = (void *) rsp + rsp->h.length; - size_t space_left; - - if (rsp->h.length > BT_SUGGESTED_BUFFER_SIZE) - return -ENOMEM; - - space_left = BT_SUGGESTED_BUFFER_SIZE - rsp->h.length; - - /* endianness prevents direct cast */ - if (codec_cap->media_codec_type == A2DP_CODEC_SBC) { - struct sbc_codec_cap *sbc_cap = (void *) codec_cap; - sbc_capabilities_t *sbc = (void *) codec; - - if (space_left < sizeof(sbc_capabilities_t)) - return -ENOMEM; - - if (type == AVDTP_SEP_TYPE_SINK) - codec->type = BT_A2DP_SBC_SINK; - else if (type == AVDTP_SEP_TYPE_SOURCE) - codec->type = BT_A2DP_SBC_SOURCE; - else - return -EINVAL; - - codec->length = sizeof(sbc_capabilities_t); - - sbc->channel_mode = sbc_cap->channel_mode; - sbc->frequency = sbc_cap->frequency; - sbc->allocation_method = sbc_cap->allocation_method; - sbc->subbands = sbc_cap->subbands; - sbc->block_length = sbc_cap->block_length; - sbc->min_bitpool = sbc_cap->min_bitpool; - sbc->max_bitpool = sbc_cap->max_bitpool; - - print_sbc(sbc_cap); - } else if (codec_cap->media_codec_type == A2DP_CODEC_MPEG12) { - struct mpeg_codec_cap *mpeg_cap = (void *) codec_cap; - mpeg_capabilities_t *mpeg = (void *) codec; - - if (space_left < sizeof(mpeg_capabilities_t)) - return -ENOMEM; - - if (type == AVDTP_SEP_TYPE_SINK) - codec->type = BT_A2DP_MPEG12_SINK; - else if (type == AVDTP_SEP_TYPE_SOURCE) - codec->type = BT_A2DP_MPEG12_SOURCE; - else - return -EINVAL; - - codec->length = sizeof(mpeg_capabilities_t); - - mpeg->channel_mode = mpeg_cap->channel_mode; - mpeg->crc = mpeg_cap->crc; - mpeg->layer = mpeg_cap->layer; - mpeg->frequency = mpeg_cap->frequency; - mpeg->mpf = mpeg_cap->mpf; - mpeg->bitrate = mpeg_cap->bitrate; - - print_mpeg12(mpeg_cap); - } else { - size_t codec_length, type_length, total_length; - - codec_length = cap->length - (sizeof(struct avdtp_service_capability) - + sizeof(struct avdtp_media_codec_capability)); - type_length = sizeof(codec_cap->media_codec_type); - total_length = type_length + codec_length + - sizeof(codec_capabilities_t); - - if (space_left < total_length) - return -ENOMEM; - - if (type == AVDTP_SEP_TYPE_SINK) - codec->type = BT_A2DP_UNKNOWN_SINK; - else if (type == AVDTP_SEP_TYPE_SOURCE) - codec->type = BT_A2DP_UNKNOWN_SOURCE; - else - return -EINVAL; - - codec->length = total_length; - memcpy(codec->data, &codec_cap->media_codec_type, type_length); - memcpy(codec->data + type_length, codec_cap->data, - codec_length); - } - - codec->seid = seid; - codec->configured = configured; - codec->lock = lock; - rsp->h.length += codec->length; - - DBG("Append %s seid %d - length %d - total %d", - configured ? "configured" : "", seid, codec->length, - rsp->h.length); - - return 0; -} - -static void a2dp_discovery_complete(struct avdtp *session, GSList *seps, - struct avdtp_error *err, - void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_get_capabilities_rsp *rsp = (void *) buf; - struct a2dp_data *a2dp = &client->d.a2dp; - - if (!g_slist_find(clients, client)) { - DBG("Client disconnected during discovery"); - return; - } - - if (err) - goto failed; - - memset(buf, 0, sizeof(buf)); - client->req_id = 0; - - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_GET_CAPABILITIES; - rsp->h.length = sizeof(*rsp); - ba2str(&client->dev->src, rsp->source); - ba2str(&client->dev->dst, rsp->destination); - strncpy(rsp->object, client->dev->path, sizeof(rsp->object)); - - for (; seps; seps = g_slist_next(seps)) { - struct avdtp_remote_sep *rsep = seps->data; - struct a2dp_sep *sep; - struct avdtp_service_capability *cap; - struct avdtp_stream *stream; - uint8_t type, seid, configured = 0, lock = 0; - GSList *cl; - - type = avdtp_get_type(rsep); - - if (type != AVDTP_SEP_TYPE_SINK && - type != AVDTP_SEP_TYPE_SOURCE) - continue; - - cap = avdtp_get_codec(rsep); - - if (cap->category != AVDTP_MEDIA_CODEC) - continue; - - seid = avdtp_get_seid(rsep); - - if (client->seid != 0 && client->seid != seid) - continue; - - stream = avdtp_get_stream(rsep); - if (stream) { - configured = 1; - if (client->seid == seid) - cap = avdtp_stream_get_codec(stream); - } - - for (cl = clients; cl; cl = cl->next) { - struct unix_client *c = cl->data; - struct a2dp_data *ca2dp = &c->d.a2dp; - - if (ca2dp->session == session && c->seid == seid) { - lock = c->lock; - break; - } - } - - sep = a2dp_get_sep(session, stream); - if (sep && a2dp_sep_get_lock(sep)) - lock = BT_WRITE_LOCK; - - a2dp_append_codec(rsp, cap, seid, type, configured, lock); - } - - unix_ipc_sendmsg(client, &rsp->h); - - return; - -failed: - error("discovery failed"); - unix_ipc_error(client, BT_GET_CAPABILITIES, EIO); - - if (a2dp->sep) { - a2dp_sep_unlock(a2dp->sep, a2dp->session); - a2dp->sep = NULL; - } - - avdtp_unref(a2dp->session); - a2dp->session = NULL; - a2dp->stream = NULL; -} - -static void a2dp_config_complete(struct avdtp *session, struct a2dp_sep *sep, - struct avdtp_stream *stream, - struct avdtp_error *err, - void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_set_configuration_rsp *rsp = (void *) buf; - struct a2dp_data *a2dp = &client->d.a2dp; - uint16_t imtu, omtu; - GSList *caps; - - client->req_id = 0; - - if (err) - goto failed; - - memset(buf, 0, sizeof(buf)); - - if (!stream) - goto failed; - - if (client->cb_id > 0) - avdtp_stream_remove_cb(a2dp->session, a2dp->stream, - client->cb_id); - - a2dp->sep = sep; - a2dp->stream = stream; - - if (!avdtp_stream_get_transport(stream, &client->data_fd, &imtu, &omtu, - &caps)) { - error("Unable to get stream transport"); - goto failed; - } - - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_SET_CONFIGURATION; - rsp->h.length = sizeof(*rsp); - - /* FIXME: Use imtu when fd_opt is CFG_FD_OPT_READ */ - rsp->link_mtu = omtu; - - unix_ipc_sendmsg(client, &rsp->h); - - client->cb_id = avdtp_stream_add_cb(session, stream, - stream_state_changed, client); - - return; - -failed: - error("config failed"); - - unix_ipc_error(client, BT_SET_CONFIGURATION, EIO); - - avdtp_unref(a2dp->session); - - a2dp->session = NULL; - a2dp->stream = NULL; - a2dp->sep = NULL; -} - -static void a2dp_resume_complete(struct avdtp *session, - struct avdtp_error *err, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_start_stream_rsp *rsp = (void *) buf; - struct bt_new_stream_ind *ind = (void *) buf; - struct a2dp_data *a2dp = &client->d.a2dp; - - if (err) - goto failed; - - memset(buf, 0, sizeof(buf)); - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_START_STREAM; - rsp->h.length = sizeof(*rsp); - - unix_ipc_sendmsg(client, &rsp->h); - - memset(buf, 0, sizeof(buf)); - ind->h.type = BT_RESPONSE; - ind->h.name = BT_NEW_STREAM; - rsp->h.length = sizeof(*ind); - - unix_ipc_sendmsg(client, &ind->h); - - if (unix_sendmsg_fd(client->sock, client->data_fd) < 0) { - error("unix_sendmsg_fd: %s(%d)", strerror(errno), errno); - goto failed; - } - - return; - -failed: - error("resume failed"); - - unix_ipc_error(client, BT_START_STREAM, EIO); - - if (client->cb_id > 0) { - avdtp_stream_remove_cb(a2dp->session, a2dp->stream, - client->cb_id); - client->cb_id = 0; - } - - if (a2dp->sep) { - a2dp_sep_unlock(a2dp->sep, a2dp->session); - a2dp->sep = NULL; - } - - avdtp_unref(a2dp->session); - a2dp->session = NULL; - a2dp->stream = NULL; -} - -static void a2dp_suspend_complete(struct avdtp *session, - struct avdtp_error *err, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_stop_stream_rsp *rsp = (void *) buf; - - if (err) - goto failed; - - memset(buf, 0, sizeof(buf)); - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_STOP_STREAM; - rsp->h.length = sizeof(*rsp); - - unix_ipc_sendmsg(client, &rsp->h); - - return; - -failed: - error("suspend failed"); - - unix_ipc_error(client, BT_STOP_STREAM, EIO); -} - -static void start_discovery(struct audio_device *dev, struct unix_client *client) -{ - struct a2dp_data *a2dp; - int err = 0; - - switch (client->type) { - case TYPE_SINK: - case TYPE_SOURCE: - a2dp = &client->d.a2dp; - - if (!a2dp->session) - a2dp->session = avdtp_get(&dev->src, &dev->dst); - - if (!a2dp->session) { - error("Unable to get a session"); - goto failed; - } - - err = avdtp_discover(a2dp->session, a2dp_discovery_complete, - client); - if (err) { - if (a2dp->session) { - avdtp_unref(a2dp->session); - a2dp->session = NULL; - } - goto failed; - } - break; - - case TYPE_HEADSET: - case TYPE_GATEWAY: - headset_discovery_complete(dev, client); - break; - - default: - error("No known services for device"); - goto failed; - } - - client->dev = dev; - - return; - -failed: - unix_ipc_error(client, BT_GET_CAPABILITIES, err ? : EIO); -} - -static void open_complete(struct audio_device *dev, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_open_rsp *rsp = (void *) buf; - - memset(buf, 0, sizeof(buf)); - - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_OPEN; - rsp->h.length = sizeof(*rsp); - - ba2str(&dev->src, rsp->source); - ba2str(&dev->dst, rsp->destination); - strncpy(rsp->object, dev->path, sizeof(rsp->object)); - - unix_ipc_sendmsg(client, &rsp->h); -} - -static void start_open(struct audio_device *dev, struct unix_client *client) -{ - struct a2dp_data *a2dp; - struct headset_data *hs; - struct avdtp_remote_sep *rsep; - gboolean unref_avdtp_on_fail = FALSE; - - switch (client->type) { - case TYPE_SINK: - case TYPE_SOURCE: - a2dp = &client->d.a2dp; - - if (!a2dp->session) { - a2dp->session = avdtp_get(&dev->src, &dev->dst); - unref_avdtp_on_fail = TRUE; - } - - if (!a2dp->session) { - error("Unable to get a session"); - goto failed; - } - - if (a2dp->sep) { - error("Client already has an opened session"); - goto failed; - } - - rsep = avdtp_get_remote_sep(a2dp->session, client->seid); - if (!rsep) { - error("Invalid seid %d", client->seid); - goto failed; - } - - a2dp->sep = a2dp_get(a2dp->session, rsep); - if (!a2dp->sep) { - error("seid %d not available or locked", client->seid); - goto failed; - } - - if (!a2dp_sep_lock(a2dp->sep, a2dp->session)) { - error("Unable to open seid %d", client->seid); - a2dp->sep = NULL; - goto failed; - } - - break; - - case TYPE_HEADSET: - hs = &client->d.hs; - - if (hs->locked) { - error("Client already has an opened session"); - goto failed; - } - - hs->locked = headset_lock(dev, client->lock); - if (!hs->locked) { - error("Unable to open seid %d", client->seid); - goto failed; - } - break; - - case TYPE_GATEWAY: - break; - default: - error("No known services for device"); - goto failed; - } - - client->dev = dev; - - open_complete(dev, client); - - return; - -failed: - if (unref_avdtp_on_fail && a2dp->session) { - avdtp_unref(a2dp->session); - a2dp->session = NULL; - } - unix_ipc_error(client, BT_OPEN, EINVAL); -} - -static void start_config(struct audio_device *dev, struct unix_client *client) -{ - struct a2dp_data *a2dp; - struct headset_data *hs; - unsigned int id; - - switch (client->type) { - case TYPE_SINK: - case TYPE_SOURCE: - a2dp = &client->d.a2dp; - - if (!a2dp->session) - a2dp->session = avdtp_get(&dev->src, &dev->dst); - - if (!a2dp->session) { - error("Unable to get a session"); - goto failed; - } - - if (!a2dp->sep) { - error("seid %d not opened", client->seid); - goto failed; - } - - id = a2dp_config(a2dp->session, a2dp->sep, a2dp_config_complete, - client->caps, client); - client->cancel = a2dp_cancel; - break; - - case TYPE_HEADSET: - hs = &client->d.hs; - - if (!hs->locked) { - error("seid %d not opened", client->seid); - goto failed; - } - - id = headset_config_stream(dev, TRUE, headset_setup_complete, - client); - client->cancel = headset_cancel_stream; - break; - case TYPE_GATEWAY: - id = gateway_config_stream(dev, gateway_setup_complete, client); - client->cancel = gateway_cancel_stream; - break; - - default: - error("No known services for device"); - goto failed; - } - - if (id == 0) { - error("config failed"); - goto failed; - } - - client->req_id = id; - g_slist_free(client->caps); - client->caps = NULL; - - return; - -failed: - unix_ipc_error(client, BT_SET_CONFIGURATION, EIO); -} - -static void start_resume(struct audio_device *dev, struct unix_client *client) -{ - struct a2dp_data *a2dp = NULL; - struct headset_data *hs; - unsigned int id; - struct avdtp *session = NULL; - - switch (client->type) { - case TYPE_SINK: - case TYPE_SOURCE: - a2dp = &client->d.a2dp; - - if (!a2dp->sep) { - error("seid not opened"); - goto failed; - } - - if (!a2dp->session) { - session = avdtp_get(&dev->src, &dev->dst); - if (!session) { - error("Unable to get a session"); - goto failed; - } - a2dp->session = session; - } - - id = a2dp_resume(a2dp->session, a2dp->sep, a2dp_resume_complete, - client); - client->cancel = a2dp_cancel; - - break; - - case TYPE_HEADSET: - hs = &client->d.hs; - - if (!hs->locked) { - error("seid not opened"); - goto failed; - } - - id = headset_request_stream(dev, headset_resume_complete, - client); - client->cancel = headset_cancel_stream; - break; - - case TYPE_GATEWAY: - id = gateway_request_stream(dev, gateway_resume_complete, - client); - client->cancel = gateway_cancel_stream; - break; - - default: - error("No known services for device"); - goto failed; - } - - if (id == 0) { - error("start_resume: resume failed"); - goto failed; - } - - client->req_id = id; - - return; - -failed: - if (session) { - avdtp_unref(session); - a2dp->session = NULL; - } - - unix_ipc_error(client, BT_START_STREAM, EIO); -} - -static void start_suspend(struct audio_device *dev, struct unix_client *client) -{ - struct a2dp_data *a2dp = NULL; - struct headset_data *hs; - unsigned int id; - struct avdtp *session = NULL; - - switch (client->type) { - case TYPE_SINK: - case TYPE_SOURCE: - a2dp = &client->d.a2dp; - - if (!a2dp->sep) { - error("seid not opened"); - goto failed; - } - - if (!a2dp->session) { - session = avdtp_get(&dev->src, &dev->dst); - if (!session) { - error("Unable to get a session"); - goto failed; - } - a2dp->session = session; - } - - if (!a2dp->sep) { - error("Unable to get a sep"); - goto failed; - } - - id = a2dp_suspend(a2dp->session, a2dp->sep, - a2dp_suspend_complete, client); - client->cancel = a2dp_cancel; - break; - - case TYPE_HEADSET: - hs = &client->d.hs; - - if (!hs->locked) { - error("seid not opened"); - goto failed; - } - - id = headset_suspend_stream(dev, headset_suspend_complete, - client); - client->cancel = headset_cancel_stream; - break; - - case TYPE_GATEWAY: - gateway_suspend_stream(dev); - client->cancel = gateway_cancel_stream; - headset_suspend_complete(dev, client); - id = 1; - break; - - default: - error("No known services for device"); - goto failed; - } - - if (id == 0) { - error("suspend failed"); - goto failed; - } - - return; - -failed: - if (session) { - avdtp_unref(session); - a2dp->session = NULL; - } - - unix_ipc_error(client, BT_STOP_STREAM, EIO); -} - -static void close_complete(struct audio_device *dev, void *user_data) -{ - struct unix_client *client = user_data; - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_close_rsp *rsp = (void *) buf; - - memset(buf, 0, sizeof(buf)); - - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_CLOSE; - rsp->h.length = sizeof(*rsp); - - unix_ipc_sendmsg(client, &rsp->h); - - return; -} - -static void start_close(struct audio_device *dev, struct unix_client *client, - gboolean reply) -{ - struct a2dp_data *a2dp; - struct headset_data *hs; - - if (!client->dev) - goto failed; - - switch (client->type) { - case TYPE_HEADSET: - hs = &client->d.hs; - - if (client->dev && hs->locked) { - headset_unlock(client->dev, client->lock); - hs->locked = FALSE; - } - break; - case TYPE_GATEWAY: - break; - case TYPE_SOURCE: - case TYPE_SINK: - a2dp = &client->d.a2dp; - - if (client->cb_id > 0) { - avdtp_stream_remove_cb(a2dp->session, a2dp->stream, - client->cb_id); - client->cb_id = 0; - } - if (a2dp->sep) { - a2dp_sep_unlock(a2dp->sep, a2dp->session); - a2dp->sep = NULL; - } - if (a2dp->session) { - avdtp_unref(a2dp->session); - a2dp->session = NULL; - } - a2dp->stream = NULL; - break; - default: - error("No known services for device"); - goto failed; - } - - if (!reply) - return; - - close_complete(dev, client); - client->dev = NULL; - - return; - -failed: - if (reply) - unix_ipc_error(client, BT_STOP_STREAM, EINVAL); -} - -static void handle_getcapabilities_req(struct unix_client *client, - struct bt_get_capabilities_req *req) -{ - struct audio_device *dev; - bdaddr_t src, dst; - int err = EIO; - const char *interface; - - if (!check_nul(req->source) || !check_nul(req->destination) || - !check_nul(req->object)) { - err = EINVAL; - goto failed; - } - - str2ba(req->source, &src); - str2ba(req->destination, &dst); - - if (!manager_find_device(req->object, &src, &dst, NULL, FALSE)) - goto failed; - - if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO) - interface = AUDIO_HEADSET_INTERFACE; - else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) - interface = AUDIO_SINK_INTERFACE; - else - interface = client->interface; - - dev = manager_find_device(req->object, &src, &dst, interface, TRUE); - if (!dev && (req->flags & BT_FLAG_AUTOCONNECT)) - dev = manager_find_device(req->object, &src, &dst, - interface, FALSE); - - if (!dev) { - if (req->transport == BT_CAPABILITIES_TRANSPORT_SCO) - interface = AUDIO_GATEWAY_INTERFACE; - else if (req->transport == BT_CAPABILITIES_TRANSPORT_A2DP) - interface = AUDIO_SOURCE_INTERFACE; - else - interface = NULL; - dev = manager_find_device(req->object, &src, &dst, - interface, TRUE); - if (!dev && (req->flags & BT_FLAG_AUTOCONNECT)) - dev = manager_find_device(req->object, &src, &dst, - interface, FALSE); - } - - if (!dev) { - error("Unable to find a matching device"); - goto failed; - } - - client->type = select_service(dev, interface); - if (client->type == TYPE_NONE) { - error("No matching service found"); - goto failed; - } - - if (g_strcmp0(interface, client->interface) != 0) { - g_free(client->interface); - client->interface = g_strdup(interface); - } - - client->seid = req->seid; - - start_discovery(dev, client); - - return; - -failed: - unix_ipc_error(client, BT_GET_CAPABILITIES, err); -} - -static int handle_sco_open(struct unix_client *client, struct bt_open_req *req) -{ - if (!client->interface) - client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); - else if (!g_str_equal(client->interface, AUDIO_HEADSET_INTERFACE) && - !g_str_equal(client->interface, AUDIO_GATEWAY_INTERFACE)) - return -EIO; - - DBG("open sco - object=%s source=%s destination=%s lock=%s%s", - strcmp(req->object, "") ? req->object : "ANY", - strcmp(req->source, "") ? req->source : "ANY", - strcmp(req->destination, "") ? req->destination : "ANY", - req->lock & BT_READ_LOCK ? "read" : "", - req->lock & BT_WRITE_LOCK ? "write" : ""); - - return 0; -} - -static int handle_a2dp_open(struct unix_client *client, struct bt_open_req *req) -{ - if (!client->interface) - /* FIXME: are we treating a sink or a source? */ - client->interface = g_strdup(AUDIO_SINK_INTERFACE); - else if (!g_str_equal(client->interface, AUDIO_SINK_INTERFACE) && - !g_str_equal(client->interface, AUDIO_SOURCE_INTERFACE)) - return -EIO; - - DBG("open a2dp - object=%s source=%s destination=%s lock=%s%s", - strcmp(req->object, "") ? req->object : "ANY", - strcmp(req->source, "") ? req->source : "ANY", - strcmp(req->destination, "") ? req->destination : "ANY", - req->lock & BT_READ_LOCK ? "read" : "", - req->lock & BT_WRITE_LOCK ? "write" : ""); - - return 0; -} - -static void handle_open_req(struct unix_client *client, struct bt_open_req *req) -{ - struct audio_device *dev; - bdaddr_t src, dst; - int err = 0; - - if (!check_nul(req->source) || !check_nul(req->destination) || - !check_nul(req->object)) { - err = EINVAL; - goto failed; - } - - str2ba(req->source, &src); - str2ba(req->destination, &dst); - - if (req->seid > BT_A2DP_SEID_RANGE) { - err = handle_sco_open(client, req); - if (err < 0) { - err = -err; - goto failed; - } - } else { - err = handle_a2dp_open(client, req); - if (err < 0) { - err = -err; - goto failed; - } - } - - if (!manager_find_device(req->object, &src, &dst, NULL, FALSE)) - goto failed; - - dev = manager_find_device(req->object, &src, &dst, client->interface, - TRUE); - if (!dev) - dev = manager_find_device(req->object, &src, &dst, - client->interface, FALSE); - - if (!dev) - goto failed; - - client->seid = req->seid; - client->lock = req->lock; - - start_open(dev, client); - - return; - -failed: - unix_ipc_error(client, BT_OPEN, err ? : EIO); -} - -static int handle_sco_transport(struct unix_client *client, - struct bt_set_configuration_req *req) -{ - struct audio_device *dev = client->dev; - - if (!client->interface) { - if (dev->headset) - client->interface = g_strdup(AUDIO_HEADSET_INTERFACE); - else if (dev->gateway) - client->interface = g_strdup(AUDIO_GATEWAY_INTERFACE); - else - return -EIO; - } else if (!g_str_equal(client->interface, AUDIO_HEADSET_INTERFACE) && - !g_str_equal(client->interface, AUDIO_GATEWAY_INTERFACE)) - return -EIO; - - return 0; -} - -static int handle_a2dp_transport(struct unix_client *client, - struct bt_set_configuration_req *req) -{ - struct avdtp_service_capability *media_transport, *media_codec; - struct sbc_codec_cap sbc_cap; - struct mpeg_codec_cap mpeg_cap; - - if (!client->interface) - /* FIXME: are we treating a sink or a source? */ - client->interface = g_strdup(AUDIO_SINK_INTERFACE); - else if (!g_str_equal(client->interface, AUDIO_SINK_INTERFACE) && - !g_str_equal(client->interface, AUDIO_SOURCE_INTERFACE)) - return -EIO; - - g_slist_free_full(client->caps, g_free); - client->caps = NULL; - - media_transport = avdtp_service_cap_new(AVDTP_MEDIA_TRANSPORT, - NULL, 0); - - client->caps = g_slist_append(client->caps, media_transport); - - if (req->codec.type == BT_A2DP_MPEG12_SINK || - req->codec.type == BT_A2DP_MPEG12_SOURCE) { - mpeg_capabilities_t *mpeg = (void *) &req->codec; - - memset(&mpeg_cap, 0, sizeof(mpeg_cap)); - - mpeg_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - mpeg_cap.cap.media_codec_type = A2DP_CODEC_MPEG12; - mpeg_cap.channel_mode = mpeg->channel_mode; - mpeg_cap.crc = mpeg->crc; - mpeg_cap.layer = mpeg->layer; - mpeg_cap.frequency = mpeg->frequency; - mpeg_cap.mpf = mpeg->mpf; - mpeg_cap.bitrate = mpeg->bitrate; - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &mpeg_cap, - sizeof(mpeg_cap)); - - print_mpeg12(&mpeg_cap); - } else if (req->codec.type == BT_A2DP_SBC_SINK || - req->codec.type == BT_A2DP_SBC_SOURCE) { - sbc_capabilities_t *sbc = (void *) &req->codec; - - memset(&sbc_cap, 0, sizeof(sbc_cap)); - - sbc_cap.cap.media_type = AVDTP_MEDIA_TYPE_AUDIO; - sbc_cap.cap.media_codec_type = A2DP_CODEC_SBC; - sbc_cap.channel_mode = sbc->channel_mode; - sbc_cap.frequency = sbc->frequency; - sbc_cap.allocation_method = sbc->allocation_method; - sbc_cap.subbands = sbc->subbands; - sbc_cap.block_length = sbc->block_length; - sbc_cap.min_bitpool = sbc->min_bitpool; - sbc_cap.max_bitpool = sbc->max_bitpool; - - media_codec = avdtp_service_cap_new(AVDTP_MEDIA_CODEC, &sbc_cap, - sizeof(sbc_cap)); - - print_sbc(&sbc_cap); - } else - return -EINVAL; - - client->caps = g_slist_append(client->caps, media_codec); - - return 0; -} - -static void handle_setconfiguration_req(struct unix_client *client, - struct bt_set_configuration_req *req) -{ - int err = 0; - - if (req->codec.seid != client->seid) { - error("Unable to set configuration: seid %d not opened", - req->codec.seid); - goto failed; - } - - if (!client->dev) - goto failed; - - if (req->codec.transport == BT_CAPABILITIES_TRANSPORT_SCO) { - err = handle_sco_transport(client, req); - if (err < 0) { - err = -err; - goto failed; - } - } else if (req->codec.transport == BT_CAPABILITIES_TRANSPORT_A2DP) { - err = handle_a2dp_transport(client, req); - if (err < 0) { - err = -err; - goto failed; - } - } - - start_config(client->dev, client); - - return; - -failed: - unix_ipc_error(client, BT_SET_CONFIGURATION, err ? : EIO); -} - -static void handle_streamstart_req(struct unix_client *client, - struct bt_start_stream_req *req) -{ - if (!client->dev) - goto failed; - - start_resume(client->dev, client); - - return; - -failed: - unix_ipc_error(client, BT_START_STREAM, EIO); -} - -static void handle_streamstop_req(struct unix_client *client, - struct bt_stop_stream_req *req) -{ - if (!client->dev) - goto failed; - - start_suspend(client->dev, client); - - return; - -failed: - unix_ipc_error(client, BT_STOP_STREAM, EIO); -} - -static void handle_close_req(struct unix_client *client, - struct bt_close_req *req) -{ - if (!client->dev) - goto failed; - - start_close(client->dev, client, TRUE); - - return; - -failed: - unix_ipc_error(client, BT_CLOSE, EIO); -} - -static void handle_control_req(struct unix_client *client, - struct bt_control_req *req) -{ - /* FIXME: really implement that */ - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_set_configuration_rsp *rsp = (void *) buf; - - memset(buf, 0, sizeof(buf)); - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_CONTROL; - rsp->h.length = sizeof(*rsp); - - unix_ipc_sendmsg(client, &rsp->h); -} - -static void handle_delay_report_req(struct unix_client *client, - struct bt_delay_report_req *req) -{ - char buf[BT_SUGGESTED_BUFFER_SIZE]; - struct bt_set_configuration_rsp *rsp = (void *) buf; - struct a2dp_data *a2dp; - int err; - - if (!client->dev) { - err = -ENODEV; - goto failed; - } - - switch (client->type) { - case TYPE_HEADSET: - case TYPE_GATEWAY: - err = -EINVAL; - goto failed; - case TYPE_SOURCE: - case TYPE_SINK: - a2dp = &client->d.a2dp; - if (a2dp->session && a2dp->stream) { - err = avdtp_delay_report(a2dp->session, a2dp->stream, - req->delay); - if (err < 0) - goto failed; - } else { - err = -EINVAL; - goto failed; - } - break; - default: - error("No known services for device"); - err = -EINVAL; - goto failed; - } - - memset(buf, 0, sizeof(buf)); - rsp->h.type = BT_RESPONSE; - rsp->h.name = BT_DELAY_REPORT; - rsp->h.length = sizeof(*rsp); - - unix_ipc_sendmsg(client, &rsp->h); - - return; - -failed: - unix_ipc_error(client, BT_DELAY_REPORT, -err); -} - -static gboolean client_cb(GIOChannel *chan, GIOCondition cond, gpointer data) -{ - char buf[BT_SUGGESTED_BUFFER_SIZE]; - bt_audio_msg_header_t *msghdr = (void *) buf; - struct unix_client *client = data; - int len; - const char *type, *name; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - DBG("Unix client disconnected (fd=%d)", client->sock); - - goto failed; - } - - memset(buf, 0, sizeof(buf)); - - len = recv(client->sock, buf, sizeof(buf), 0); - if (len < 0) { - error("recv: %s (%d)", strerror(errno), errno); - goto failed; - } - - type = bt_audio_strtype(msghdr->type); - name = bt_audio_strname(msghdr->name); - - DBG("Audio API: %s <- %s", type, name); - - if (msghdr->length != len) { - error("Invalid message: length mismatch"); - goto failed; - } - - switch (msghdr->name) { - case BT_GET_CAPABILITIES: - handle_getcapabilities_req(client, - (struct bt_get_capabilities_req *) msghdr); - break; - case BT_OPEN: - handle_open_req(client, - (struct bt_open_req *) msghdr); - break; - case BT_SET_CONFIGURATION: - handle_setconfiguration_req(client, - (struct bt_set_configuration_req *) msghdr); - break; - case BT_START_STREAM: - handle_streamstart_req(client, - (struct bt_start_stream_req *) msghdr); - break; - case BT_STOP_STREAM: - handle_streamstop_req(client, - (struct bt_stop_stream_req *) msghdr); - break; - case BT_CLOSE: - handle_close_req(client, - (struct bt_close_req *) msghdr); - break; - case BT_CONTROL: - handle_control_req(client, - (struct bt_control_req *) msghdr); - break; - case BT_DELAY_REPORT: - handle_delay_report_req(client, - (struct bt_delay_report_req *) msghdr); - break; - default: - error("Audio API: received unexpected message name %d", - msghdr->name); - } - - return TRUE; - -failed: - clients = g_slist_remove(clients, client); - start_close(client->dev, client, FALSE); - client_free(client); - return FALSE; -} - -static gboolean server_cb(GIOChannel *chan, GIOCondition cond, gpointer data) -{ - struct sockaddr_un addr; - socklen_t addrlen; - int sk, cli_sk; - struct unix_client *client; - GIOChannel *io; - - if (cond & G_IO_NVAL) - return FALSE; - - if (cond & (G_IO_HUP | G_IO_ERR)) { - g_io_channel_shutdown(chan, TRUE, NULL); - return FALSE; - } - - sk = g_io_channel_unix_get_fd(chan); - - memset(&addr, 0, sizeof(addr)); - addrlen = sizeof(addr); - - cli_sk = accept(sk, (struct sockaddr *) &addr, &addrlen); - if (cli_sk < 0) { - error("accept: %s (%d)", strerror(errno), errno); - return TRUE; - } - - DBG("Accepted new client connection on unix socket (fd=%d)", cli_sk); - set_nonblocking(cli_sk); - - client = g_new0(struct unix_client, 1); - client->sock = cli_sk; - clients = g_slist_append(clients, client); - - io = g_io_channel_unix_new(cli_sk); - g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - client_cb, client); - g_io_channel_unref(io); - - return TRUE; -} - -void unix_device_removed(struct audio_device *dev) -{ - GSList *l; - - DBG("unix_device_removed(%p)", dev); - - l = clients; - while (l) { - struct unix_client *client = l->data; - - l = l->next; - - if (client->dev == dev) { - clients = g_slist_remove(clients, client); - start_close(client->dev, client, FALSE); - client_free(client); - } - } -} - -void unix_delay_report(struct audio_device *dev, uint8_t seid, uint16_t delay) -{ - GSList *l; - struct bt_delay_report_ind ind; - - DBG("unix_delay_report(%p): %u.%ums", dev, delay / 10, delay % 10); - - memset(&ind, 0, sizeof(ind)); - ind.h.type = BT_INDICATION; - ind.h.name = BT_DELAY_REPORT; - ind.h.length = sizeof(ind); - ind.delay = delay; - - for (l = clients; l != NULL; l = g_slist_next(l)) { - struct unix_client *client = l->data; - - if (client->dev != dev || client->seid != seid) - continue; - - unix_ipc_sendmsg(client, (void *) &ind); - } -} - -int unix_init(void) -{ - GIOChannel *io; - struct sockaddr_un addr = { - AF_UNIX, BT_IPC_SOCKET_NAME - }; - - int sk, err; - - sk = socket(PF_LOCAL, SOCK_STREAM, 0); - if (sk < 0) { - err = -errno; - error("Can't create unix socket: %s (%d)", strerror(-err), - -err); - return err; - } - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - err = -errno; - error("Can't bind unix socket: %s (%d)", strerror(-err), - -err); - close(sk); - return err; - } - - set_nonblocking(sk); - - if (listen(sk, 1) < 0) { - err = -errno; - error("Can't listen on unix socket: %s (%d)", strerror(-err), - -err); - close(sk); - return err; - } - - unix_sock = sk; - - io = g_io_channel_unix_new(sk); - g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - server_cb, NULL); - g_io_channel_unref(io); - - DBG("Unix socket created: %d", sk); - - return 0; -} - -void unix_exit(void) -{ - g_slist_free_full(clients, client_free); - if (unix_sock >= 0) { - close(unix_sock); - unix_sock = -1; - } -} diff -Nru bluez-4.101/audio/unix.h bluez-5.23/audio/unix.h --- bluez-4.101/audio/unix.h 2010-05-23 12:47:33.000000000 +0000 +++ bluez-5.23/audio/unix.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2006-2010 Nokia Corporation - * Copyright (C) 2004-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -void unix_device_removed(struct audio_device *dev); - -void unix_delay_report(struct audio_device *dev, uint8_t seid, uint16_t delay); - -int unix_init(void); -void unix_exit(void); diff -Nru bluez-4.101/AUTHORS bluez-5.23/AUTHORS --- bluez-4.101/AUTHORS 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/AUTHORS 2014-08-06 17:25:36.000000000 +0000 @@ -59,3 +59,35 @@ Lucas De Marchi Szymon Janc Syam Sidhardhan +Paulo Alcantara +Jefferson Delfes +Andrzej Kaczmarek +Eder Ruiz Maria +Mikel Astiz +Chan-yeol Park +João Paulo Rechi Vita +Larry Junior +Raymond Liu +Radoslaw Jablonski +Rafal Michalski +Dmitriy Paliy +Bartosz Szatkowski +Lukasz Pawlik +Slawomir Bochenski +Wayne Lee +Ricky Yuen +Takashi Sasai +Andre Dieb Martins +Cristian Rodríguez +Alex Deymo +Petri Gynther +Scott James Remnant +Jakub Tyszkowski +Grzegorz Kołodziejczyk +Marcin Krąglak +Łukasz Rymanowski +Jerzy Kasenberg +Arman Uguray +Artem Rakhov +Mike Ryan +David Herrmann diff -Nru bluez-4.101/bluez.pc.in bluez-5.23/bluez.pc.in --- bluez-4.101/bluez.pc.in 2008-10-04 13:08:56.000000000 +0000 +++ bluez-5.23/bluez.pc.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: BlueZ -Description: Bluetooth protocol stack for Linux -Version: @VERSION@ -Libs: -L${libdir} -lbluetooth -Cflags: -I${includedir} diff -Nru bluez-4.101/btio/btio.c bluez-5.23/btio/btio.c --- bluez-4.101/btio/btio.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/btio/btio.c 2014-06-20 18:33:13.000000000 +0000 @@ -21,6 +21,11 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include @@ -43,14 +48,23 @@ #endif #define ERROR_FAILED(gerr, str, err) \ - g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_FAILED, \ + g_set_error(gerr, BT_IO_ERROR, err, \ str ": %s (%d)", strerror(err), err) #define DEFAULT_DEFER_TIMEOUT 30 +typedef enum { + BT_IO_L2CAP, + BT_IO_RFCOMM, + BT_IO_SCO, + BT_IO_INVALID, +} BtIOType; + struct set_opts { bdaddr_t src; bdaddr_t dst; + BtIOType type; + uint8_t src_type; uint8_t dst_type; int defer; int sec_level; @@ -64,6 +78,7 @@ uint8_t mode; int flushable; uint32_t priority; + uint16_t voice; }; struct connect { @@ -85,6 +100,48 @@ GDestroyNotify destroy; }; +static BtIOType bt_io_get_type(GIOChannel *io, GError **gerr) +{ + int sk = g_io_channel_unix_get_fd(io); + int domain, proto, err; + socklen_t len; + + domain = 0; + len = sizeof(domain); + err = getsockopt(sk, SOL_SOCKET, SO_DOMAIN, &domain, &len); + if (err < 0) { + ERROR_FAILED(gerr, "getsockopt(SO_DOMAIN)", errno); + return BT_IO_INVALID; + } + + if (domain != AF_BLUETOOTH) { + g_set_error(gerr, BT_IO_ERROR, EINVAL, + "BtIO socket domain not AF_BLUETOOTH"); + return BT_IO_INVALID; + } + + proto = 0; + len = sizeof(proto); + err = getsockopt(sk, SOL_SOCKET, SO_PROTOCOL, &proto, &len); + if (err < 0) { + ERROR_FAILED(gerr, "getsockopt(SO_PROTOCOL)", errno); + return BT_IO_INVALID; + } + + switch (proto) { + case BTPROTO_RFCOMM: + return BT_IO_RFCOMM; + case BTPROTO_SCO: + return BT_IO_SCO; + case BTPROTO_L2CAP: + return BT_IO_L2CAP; + default: + g_set_error(gerr, BT_IO_ERROR, EINVAL, + "Unknown BtIO socket type"); + return BT_IO_INVALID; + } +} + static void server_remove(struct server *server) { if (server->destroy) @@ -124,19 +181,28 @@ gpointer user_data) { struct accept *accept = user_data; - GError *err = NULL; + GError *gerr = NULL; /* If the user aborted this accept attempt */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; - if (cond & (G_IO_HUP | G_IO_ERR)) - g_set_error(&err, BT_IO_ERROR, BT_IO_ERROR_DISCONNECTED, - "HUP or ERR on socket"); + if (cond & (G_IO_HUP | G_IO_ERR)) { + int err, sk_err, sock = g_io_channel_unix_get_fd(io); + socklen_t len = sizeof(sk_err); + + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) + err = -errno; + else + err = -sk_err; - accept->connect(io, err, accept->user_data); + if (err < 0) + ERROR_FAILED(&gerr, "HUP or ERR on socket", -err); + } - g_clear_error(&err); + accept->connect(io, gerr, accept->user_data); + + g_clear_error(&gerr); return FALSE; } @@ -146,32 +212,26 @@ { struct connect *conn = user_data; GError *gerr = NULL; + int err, sk_err, sock; + socklen_t len = sizeof(sk_err); /* If the user aborted this connect attempt */ if ((cond & G_IO_NVAL) || check_nval(io)) return FALSE; - if (cond & G_IO_OUT) { - int err, sk_err = 0, sock = g_io_channel_unix_get_fd(io); - socklen_t len = sizeof(sk_err); + sock = g_io_channel_unix_get_fd(io); - if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) - err = -errno; - else - err = -sk_err; + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &sk_err, &len) < 0) + err = -errno; + else + err = -sk_err; - if (err < 0) - g_set_error(&gerr, BT_IO_ERROR, - BT_IO_ERROR_CONNECT_FAILED, "%s (%d)", - strerror(-err), -err); - } else if (cond & (G_IO_HUP | G_IO_ERR)) - g_set_error(&gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED, - "HUP or ERR on socket"); + if (err < 0) + ERROR_FAILED(&gerr, "connect error", -err); conn->connect(io, gerr, conn->user_data); - if (gerr) - g_error_free(gerr); + g_clear_error(&gerr); return FALSE; } @@ -258,8 +318,8 @@ (GDestroyNotify) accept_remove); } -static int l2cap_bind(int sock, const bdaddr_t *src, uint16_t psm, - uint16_t cid, GError **err) +static int l2cap_bind(int sock, const bdaddr_t *src, uint8_t src_type, + uint16_t psm, uint16_t cid, GError **err) { struct sockaddr_l2 addr; @@ -272,6 +332,8 @@ else addr.l2_psm = htobs(psm); + addr.l2_bdaddr_type = src_type; + if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { int error = -errno; ERROR_FAILED(err, "l2cap_bind", errno); @@ -390,7 +452,7 @@ int ret; if (level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH) { - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Valid security level range is %d-%d", BT_SECURITY_LOW, BT_SECURITY_HIGH); return FALSE; @@ -529,34 +591,60 @@ return FALSE; } -static gboolean l2cap_set(int sock, int sec_level, uint16_t imtu, - uint16_t omtu, uint8_t mode, int master, - int flushable, uint32_t priority, GError **err) +static gboolean set_l2opts(int sock, uint16_t imtu, uint16_t omtu, + uint8_t mode, GError **err) +{ + struct l2cap_options l2o; + socklen_t len; + + memset(&l2o, 0, sizeof(l2o)); + len = sizeof(l2o); + if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { + ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno); + return FALSE; + } + + if (imtu) + l2o.imtu = imtu; + if (omtu) + l2o.omtu = omtu; + if (mode) + l2o.mode = mode; + + if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, sizeof(l2o)) < 0) { + ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno); + return FALSE; + } + + return TRUE; +} + +static gboolean set_le_imtu(int sock, uint16_t imtu, GError **err) +{ + if (setsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, &imtu, + sizeof(imtu)) < 0) { + ERROR_FAILED(err, "setsockopt(BT_RCVMTU)", errno); + return FALSE; + } + + return TRUE; +} + +static gboolean l2cap_set(int sock, uint8_t src_type, int sec_level, + uint16_t imtu, uint16_t omtu, uint8_t mode, + int master, int flushable, uint32_t priority, + GError **err) { if (imtu || omtu || mode) { - struct l2cap_options l2o; - socklen_t len; + gboolean ret; - memset(&l2o, 0, sizeof(l2o)); - len = sizeof(l2o); - if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, - &len) < 0) { - ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno); - return FALSE; - } + if (src_type == BDADDR_BREDR) + ret = set_l2opts(sock, imtu, omtu, mode, err); + else + ret = set_le_imtu(sock, imtu, err); - if (imtu) - l2o.imtu = imtu; - if (omtu) - l2o.omtu = omtu; - if (mode) - l2o.mode = mode; - - if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, - sizeof(l2o)) < 0) { - ERROR_FAILED(err, "setsockopt(L2CAP_OPTIONS)", errno); - return FALSE; - } + if (!ret) + return ret; } if (master >= 0 && l2cap_set_master(sock, master) < 0) { @@ -662,13 +750,14 @@ return 0; } -static gboolean sco_set(int sock, uint16_t mtu, GError **err) +static gboolean sco_set(int sock, uint16_t mtu, uint16_t voice, GError **err) { struct sco_options sco_opt; + struct bt_voice bt_voice; socklen_t len; if (!mtu) - return TRUE; + goto voice; len = sizeof(sco_opt); memset(&sco_opt, 0, len); @@ -684,6 +773,17 @@ return FALSE; } +voice: + if (!voice) + return TRUE; + + bt_voice.setting = voice; + if (setsockopt(sock, SOL_BLUETOOTH, BT_VOICE, &bt_voice, + sizeof(bt_voice)) < 0) { + ERROR_FAILED(err, "setsockopt(BT_VOICE)", errno); + return FALSE; + } + return TRUE; } @@ -696,11 +796,13 @@ memset(opts, 0, sizeof(*opts)); /* Set defaults */ + opts->type = BT_IO_SCO; opts->defer = DEFAULT_DEFER_TIMEOUT; opts->master = -1; opts->mode = L2CAP_MODE_BASIC; opts->flushable = -1; opts->priority = 0; + opts->src_type = BDADDR_BREDR; opts->dst_type = BDADDR_BREDR; while (opt != BT_IO_OPT_INVALID) { @@ -712,6 +814,9 @@ case BT_IO_OPT_SOURCE_BDADDR: bacpy(&opts->src, va_arg(args, const bdaddr_t *)); break; + case BT_IO_OPT_SOURCE_TYPE: + opts->src_type = va_arg(args, int); + break; case BT_IO_OPT_DEST: str2ba(va_arg(args, const char *), &opts->dst); break; @@ -728,12 +833,15 @@ opts->sec_level = va_arg(args, int); break; case BT_IO_OPT_CHANNEL: + opts->type = BT_IO_RFCOMM; opts->channel = va_arg(args, int); break; case BT_IO_OPT_PSM: + opts->type = BT_IO_L2CAP; opts->psm = va_arg(args, int); break; case BT_IO_OPT_CID: + opts->type = BT_IO_L2CAP; opts->cid = va_arg(args, int); break; case BT_IO_OPT_MTU: @@ -763,8 +871,11 @@ case BT_IO_OPT_PRIORITY: opts->priority = va_arg(args, int); break; + case BT_IO_OPT_VOICE: + opts->voice = va_arg(args, int); + break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } @@ -775,8 +886,7 @@ return TRUE; } -static gboolean get_peers(int sock, struct sockaddr *src, struct sockaddr *dst, - socklen_t len, GError **err) +static gboolean get_src(int sock, void *src, socklen_t len, GError **err) { socklen_t olen; @@ -787,6 +897,13 @@ return FALSE; } + return TRUE; +} + +static gboolean get_dst(int sock, void *dst, socklen_t len, GError **err) +{ + socklen_t olen; + memset(dst, 0, len); olen = len; if (getpeername(sock, dst, &olen) < 0) { @@ -854,20 +971,36 @@ uint8_t dev_class[3]; uint16_t handle; socklen_t len; - gboolean flushable = FALSE; + gboolean flushable = FALSE, have_dst = FALSE; uint32_t priority; + if (!get_src(sock, &src, sizeof(src), err)) + return FALSE; + + memset(&l2o, 0, sizeof(l2o)); + + if (src.l2_bdaddr_type != BDADDR_BREDR) { + len = sizeof(l2o.imtu); + if (getsockopt(sock, SOL_BLUETOOTH, BT_RCVMTU, + &l2o.imtu, &len) == 0) + goto parse_opts; + + /* Non-LE CoC enabled kernels will return one of these + * in which case we need to fall back to L2CAP_OPTIONS. + */ + if (errno != EPROTONOSUPPORT && errno != ENOPROTOOPT) { + ERROR_FAILED(err, "getsockopt(BT_RCVMTU)", errno); + return FALSE; + } + } + len = sizeof(l2o); - memset(&l2o, 0, len); if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &l2o, &len) < 0) { ERROR_FAILED(err, "getsockopt(L2CAP_OPTIONS)", errno); return FALSE; } - if (!get_peers(sock, (struct sockaddr *) &src, - (struct sockaddr *) &dst, sizeof(src), err)) - return FALSE; - +parse_opts: while (opt != BT_IO_OPT_INVALID) { switch (opt) { case BT_IO_OPT_SOURCE: @@ -877,15 +1010,29 @@ bacpy(va_arg(args, bdaddr_t *), &src.l2_bdaddr); break; case BT_IO_OPT_DEST: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; ba2str(&dst.l2_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; bacpy(va_arg(args, bdaddr_t *), &dst.l2_bdaddr); break; case BT_IO_OPT_DEST_TYPE: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, - "Not implemented"); - return FALSE; + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + *(va_arg(args, uint8_t *)) = dst.l2_bdaddr_type; + break; case BT_IO_OPT_DEFER_TIMEOUT: len = sizeof(int); if (getsockopt(sock, SOL_BLUETOOTH, BT_DEFER_SETUP, @@ -905,14 +1052,47 @@ return FALSE; break; case BT_IO_OPT_PSM: - *(va_arg(args, uint16_t *)) = src.l2_psm ? - btohs(src.l2_psm) : btohs(dst.l2_psm); + if (src.l2_psm) { + *(va_arg(args, uint16_t *)) = btohs(src.l2_psm); + break; + } + + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + + *(va_arg(args, uint16_t *)) = btohs(dst.l2_psm); break; case BT_IO_OPT_CID: - *(va_arg(args, uint16_t *)) = src.l2_cid ? - btohs(src.l2_cid) : btohs(dst.l2_cid); + if (src.l2_cid) { + *(va_arg(args, uint16_t *)) = btohs(src.l2_cid); + break; + } + + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + + *(va_arg(args, uint16_t *)) = btohs(dst.l2_cid); break; case BT_IO_OPT_OMTU: + if (src.l2_bdaddr_type == BDADDR_BREDR) { + *(va_arg(args, uint16_t *)) = l2o.omtu; + break; + } + + len = sizeof(l2o.omtu); + if (getsockopt(sock, SOL_BLUETOOTH, BT_SNDMTU, + &l2o.omtu, &len) < 0) { + ERROR_FAILED(err, "getsockopt(BT_SNDMTU)", + errno); + return FALSE; + } + *(va_arg(args, uint16_t *)) = l2o.omtu; break; case BT_IO_OPT_IMTU: @@ -961,7 +1141,7 @@ *(va_arg(args, uint32_t *)) = priority; break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } @@ -995,13 +1175,13 @@ { BtIOOption opt = opt1; struct sockaddr_rc src, dst; + gboolean have_dst = FALSE; int flags; socklen_t len; uint8_t dev_class[3]; uint16_t handle; - if (!get_peers(sock, (struct sockaddr *) &src, - (struct sockaddr *) &dst, sizeof(src), err)) + if (!get_src(sock, &src, sizeof(src), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { @@ -1013,9 +1193,19 @@ bacpy(va_arg(args, bdaddr_t *), &src.rc_bdaddr); break; case BT_IO_OPT_DEST: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; ba2str(&dst.rc_bdaddr, va_arg(args, char *)); break; case BT_IO_OPT_DEST_BDADDR: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; bacpy(va_arg(args, bdaddr_t *), &dst.rc_bdaddr); break; case BT_IO_OPT_DEFER_TIMEOUT: @@ -1033,13 +1223,29 @@ return FALSE; break; case BT_IO_OPT_CHANNEL: - *(va_arg(args, uint8_t *)) = src.rc_channel ? - src.rc_channel : dst.rc_channel; + if (src.rc_channel) { + *(va_arg(args, uint8_t *)) = src.rc_channel; + break; + } + + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + + *(va_arg(args, uint8_t *)) = dst.rc_channel; break; case BT_IO_OPT_SOURCE_CHANNEL: *(va_arg(args, uint8_t *)) = src.rc_channel; break; case BT_IO_OPT_DEST_CHANNEL: + if (!have_dst) + have_dst = get_dst(sock, &dst, sizeof(dst), + err); + if (!have_dst) + return FALSE; + *(va_arg(args, uint8_t *)) = dst.rc_channel; break; case BT_IO_OPT_MASTER: @@ -1068,7 +1274,7 @@ memcpy(va_arg(args, uint8_t *), dev_class, 3); break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } @@ -1113,8 +1319,10 @@ return FALSE; } - if (!get_peers(sock, (struct sockaddr *) &src, - (struct sockaddr *) &dst, sizeof(src), err)) + if (!get_src(sock, &src, sizeof(src), err)) + return FALSE; + + if (!get_dst(sock, &dst, sizeof(dst), err)) return FALSE; while (opt != BT_IO_OPT_INVALID) { @@ -1151,7 +1359,7 @@ memcpy(va_arg(args, uint8_t *), dev_class, 3); break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, + g_set_error(err, BT_IO_ERROR, EINVAL, "Unknown option %d", opt); return FALSE; } @@ -1170,19 +1378,17 @@ sock = g_io_channel_unix_get_fd(io); switch (type) { - case BT_IO_L2RAW: case BT_IO_L2CAP: - case BT_IO_L2ERTM: return l2cap_get(sock, err, opt1, args); case BT_IO_RFCOMM: return rfcomm_get(sock, err, opt1, args); case BT_IO_SCO: return sco_get(sock, err, opt1, args); + default: + g_set_error(err, BT_IO_ERROR, EINVAL, + "Unknown BtIO type %d", type); + return FALSE; } - - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, - "Unknown BtIO type %d", type); - return FALSE; } gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, @@ -1215,13 +1421,13 @@ return TRUE; } -gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err, - BtIOOption opt1, ...) +gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...) { va_list args; gboolean ret; struct set_opts opts; int sock; + BtIOType type; va_start(args, opt1); ret = parse_set_opts(&opts, err, opt1, args); @@ -1230,31 +1436,38 @@ if (!ret) return ret; + type = bt_io_get_type(io, err); + if (type == BT_IO_INVALID) + return FALSE; + sock = g_io_channel_unix_get_fd(io); switch (type) { - case BT_IO_L2RAW: case BT_IO_L2CAP: - case BT_IO_L2ERTM: - return l2cap_set(sock, opts.sec_level, opts.imtu, opts.omtu, - opts.mode, opts.master, opts.flushable, - opts.priority, err); + return l2cap_set(sock, opts.src_type, opts.sec_level, opts.imtu, + opts.omtu, opts.mode, opts.master, + opts.flushable, opts.priority, err); case BT_IO_RFCOMM: return rfcomm_set(sock, opts.sec_level, opts.master, err); case BT_IO_SCO: - return sco_set(sock, opts.mtu, err); + return sco_set(sock, opts.mtu, opts.voice, err); + default: + g_set_error(err, BT_IO_ERROR, EINVAL, + "Unknown BtIO type %d", type); + return FALSE; } - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, - "Unknown BtIO type %d", type); - return FALSE; } -gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err, - BtIOOption opt1, ...) +gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...) { va_list args; gboolean ret; + BtIOType type; + + type = bt_io_get_type(io, err); + if (type == BT_IO_INVALID) + return FALSE; va_start(args, opt1); ret = get_valist(io, type, err, opt1, args); @@ -1263,51 +1476,26 @@ return ret; } -static GIOChannel *create_io(BtIOType type, gboolean server, - struct set_opts *opts, GError **err) +static GIOChannel *create_io(gboolean server, struct set_opts *opts, + GError **err) { int sock; GIOChannel *io; - switch (type) { - case BT_IO_L2RAW: - sock = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP); - if (sock < 0) { - ERROR_FAILED(err, "socket(RAW, L2CAP)", errno); - return NULL; - } - if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0, - opts->cid, err) < 0) - goto failed; - if (!l2cap_set(sock, opts->sec_level, 0, 0, 0, -1, -1, 0, err)) - goto failed; - break; + switch (opts->type) { case BT_IO_L2CAP: sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (sock < 0) { ERROR_FAILED(err, "socket(SEQPACKET, L2CAP)", errno); return NULL; } - if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0, - opts->cid, err) < 0) - goto failed; - if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu, - opts->mode, opts->master, opts->flushable, - opts->priority, err)) - goto failed; - break; - case BT_IO_L2ERTM: - sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_L2CAP); - if (sock < 0) { - ERROR_FAILED(err, "socket(STREAM, L2CAP)", errno); - return NULL; - } - if (l2cap_bind(sock, &opts->src, server ? opts->psm : 0, - opts->cid, err) < 0) + if (l2cap_bind(sock, &opts->src, opts->src_type, + server ? opts->psm : 0, opts->cid, err) < 0) goto failed; - if (!l2cap_set(sock, opts->sec_level, opts->imtu, opts->omtu, - opts->mode, opts->master, opts->flushable, - opts->priority, err)) + if (!l2cap_set(sock, opts->src_type, opts->sec_level, + opts->imtu, opts->omtu, opts->mode, + opts->master, opts->flushable, opts->priority, + err)) goto failed; break; case BT_IO_RFCOMM: @@ -1330,12 +1518,12 @@ } if (sco_bind(sock, &opts->src, err) < 0) goto failed; - if (!sco_set(sock, opts->mtu, err)) + if (!sco_set(sock, opts->mtu, opts->voice, err)) goto failed; break; default: - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, - "Unknown BtIO type %d", type); + g_set_error(err, BT_IO_ERROR, EINVAL, + "Unknown BtIO type %d", opts->type); return NULL; } @@ -1352,9 +1540,9 @@ return NULL; } -GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, - gpointer user_data, GDestroyNotify destroy, - GError **gerr, BtIOOption opt1, ...) +GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data, + GDestroyNotify destroy, GError **gerr, + BtIOOption opt1, ...) { GIOChannel *io; va_list args; @@ -1369,19 +1557,14 @@ if (ret == FALSE) return NULL; - io = create_io(type, FALSE, &opts, gerr); + io = create_io(FALSE, &opts, gerr); if (io == NULL) return NULL; sock = g_io_channel_unix_get_fd(io); - switch (type) { - case BT_IO_L2RAW: - err = l2cap_connect(sock, &opts.dst, opts.dst_type, 0, - opts.cid); - break; + switch (opts.type) { case BT_IO_L2CAP: - case BT_IO_L2ERTM: err = l2cap_connect(sock, &opts.dst, opts.dst_type, opts.psm, opts.cid); break; @@ -1392,14 +1575,13 @@ err = sco_connect(sock, &opts.dst); break; default: - g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, - "Unknown BtIO type %d", type); + g_set_error(gerr, BT_IO_ERROR, EINVAL, + "Unknown BtIO type %d", opts.type); return NULL; } if (err < 0) { - g_set_error(gerr, BT_IO_ERROR, BT_IO_ERROR_CONNECT_FAILED, - "connect: %s (%d)", strerror(-err), -err); + ERROR_FAILED(gerr, "connect", -err); g_io_channel_unref(io); return NULL; } @@ -1409,10 +1591,9 @@ return io; } -GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect, - BtIOConfirm confirm, gpointer user_data, - GDestroyNotify destroy, GError **err, - BtIOOption opt1, ...) +GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm, + gpointer user_data, GDestroyNotify destroy, + GError **err, BtIOOption opt1, ...) { GIOChannel *io; va_list args; @@ -1420,12 +1601,6 @@ int sock; gboolean ret; - if (type == BT_IO_L2RAW) { - g_set_error(err, BT_IO_ERROR, BT_IO_ERROR_INVALID_ARGS, - "Server L2CAP RAW sockets not supported"); - return NULL; - } - va_start(args, opt1); ret = parse_set_opts(&opts, err, opt1, args); va_end(args); @@ -1433,7 +1608,7 @@ if (ret == FALSE) return NULL; - io = create_io(type, TRUE, &opts, err); + io = create_io(TRUE, &opts, err); if (io == NULL) return NULL; diff -Nru bluez-4.101/btio/btio.h bluez-5.23/btio/btio.h --- bluez-4.101/btio/btio.h 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/btio/btio.h 2013-08-08 01:43:24.000000000 +0000 @@ -26,29 +26,15 @@ #include -typedef enum { - BT_IO_ERROR_DISCONNECTED, - BT_IO_ERROR_CONNECT_FAILED, - BT_IO_ERROR_FAILED, - BT_IO_ERROR_INVALID_ARGS, -} BtIOError; - #define BT_IO_ERROR bt_io_error_quark() GQuark bt_io_error_quark(void); typedef enum { - BT_IO_L2RAW, - BT_IO_L2CAP, - BT_IO_L2ERTM, - BT_IO_RFCOMM, - BT_IO_SCO, -} BtIOType; - -typedef enum { BT_IO_OPT_INVALID = 0, BT_IO_OPT_SOURCE, BT_IO_OPT_SOURCE_BDADDR, + BT_IO_OPT_SOURCE_TYPE, BT_IO_OPT_DEST, BT_IO_OPT_DEST_BDADDR, BT_IO_OPT_DEST_TYPE, @@ -69,6 +55,7 @@ BT_IO_OPT_MODE, BT_IO_OPT_FLUSHABLE, BT_IO_OPT_PRIORITY, + BT_IO_OPT_VOICE, } BtIOOption; typedef enum { @@ -93,19 +80,16 @@ gboolean bt_io_accept(GIOChannel *io, BtIOConnect connect, gpointer user_data, GDestroyNotify destroy, GError **err); -gboolean bt_io_set(GIOChannel *io, BtIOType type, GError **err, - BtIOOption opt1, ...); +gboolean bt_io_set(GIOChannel *io, GError **err, BtIOOption opt1, ...); -gboolean bt_io_get(GIOChannel *io, BtIOType type, GError **err, - BtIOOption opt1, ...); +gboolean bt_io_get(GIOChannel *io, GError **err, BtIOOption opt1, ...); -GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect, +GIOChannel *bt_io_connect(BtIOConnect connect, gpointer user_data, + GDestroyNotify destroy, GError **gerr, + BtIOOption opt1, ...); + +GIOChannel *bt_io_listen(BtIOConnect connect, BtIOConfirm confirm, gpointer user_data, GDestroyNotify destroy, GError **err, BtIOOption opt1, ...); -GIOChannel *bt_io_listen(BtIOType type, BtIOConnect connect, - BtIOConfirm confirm, gpointer user_data, - GDestroyNotify destroy, GError **err, - BtIOOption opt1, ...); - #endif diff -Nru bluez-4.101/ChangeLog bluez-5.23/ChangeLog --- bluez-4.101/ChangeLog 2012-06-22 16:36:49.000000000 +0000 +++ bluez-5.23/ChangeLog 2014-09-07 23:23:46.000000000 +0000 @@ -1,3 +1,242 @@ +ver 5.23: + Fix issue with concurrent authorization requests. + Fix issue with HID report identifier mismatch. + Fix issue with crash when receiving uHID events. + Fix issue with crash and OBEX disconnect handling. + Fix issue with OBEX client transfers and suspend. + Fix issue with parsing of MAP application parameters. + Fix issue with devices rejecting AVRCP GetCapabilities. + Add support for kernel whitelist and Android Bluetooth. + +ver 5.22: + Fix issue with UHID_OUTPUT events mapping. + Fix issue with UHID_FEATURE events handling. + Fix issue with UINT32_MAX overflow and AVRCP. + Fix issue when dirent type DT_UNKNOWN is returned. + Add support for kernel whitelist filtering feature. + Add support for Android Bluetooth GATT over BR/EDR. + +ver 5.21: + Fix issue with SDP requests and wrong PDU size. + Fix issue with handling passive scanning triggers. + Add support for storing and loading connection parameters. + Add support for kernel background auto-connection feature. + Add support for Android Bluetooth Scan Parameters feature. + Add support for Android Bluetooth Device Information feature. + Add support for Android Bluetooth Health Device interface. + +ver 5.20: + Fix issue with LED handling of PS3 controllers. + Add support for Android Bluetooth GATT server interface. + Add support for Android Bluetooth HID over GATT feature. + Add support for Android Bluetooth multi-profile feature. + Add support for Android Bluetooth aptX audio integration. + + Note: aptX codec not included + +ver 5.19: + Fix issue with OBEX Put-Delete and Create-Empty methods. + Fix issue with AVRCP browsable/searchable player properties. + Fix issue with handling multiple default agents. + Fix issue with handling unpair event per bearer. + Fix issue with HID over GATT report ID presence. + Add support for HID protocol handling in userspace. + Add support for Bluetooth reconnection policy framework. + Add support for Android Bluetooth SCO over HCI transport. + Add support for Android Bluetooth audio quality control. + Add support for Android Bluetooth Low Energy only mode. + +ver 5.18: + Fix issue with identifying LE single mode devices. + Fix issue with L2CAP and RFCOMM peer address lookup. + Add support for handling OBEX authentication procedure. + Add support for Android Bluetooth GATT client interface. + +ver 5.17: + Fix issue with not resetting OBEX SRM setup. + Fix issue with BR/EDR devices and auto-connect list. + Fix issue with bonding complete detection as peripheral. + Fix issue with not updating bearer timestamp of connections. + Fix issue with paired property for multiple bearers. + Add support for Android Bluetooth Handsfree interface. + Add support for Android Bluetooth Wideband speech. + +ver 5.16: + Fix issue with HID over GATT physical location. + Fix issue with HID over GATT unique identifier. + Fix issue with missing paired property notification. + Fix issue with endianess of long term key storage. + Add support for storing signature resolving keys. + Add support for Android Bluetooth AVRCP interface. + +ver 5.15: + Fix issue with LE enabling and background scanning. + Fix issue with HID over GATT input device name. + Fix issue with storage of slave long term keys. + Add support for handling identity resolving keys. + Add support for Android Bluetooth A2DP interface. + Add support for Android Bluetooth audio interface. + +ver 5.14: + Fix issue with marking PS3 controllers as trusted. + Fix issue with authorization of PS3 controllers. + Add support for DualShock 4 controller detection. + Add support for legacy pairing emulation. + Add support for secure simple pairing emulation. + Add support for automated pairing testing. + Add support for RFCOMM protocol testing. + Add support for HCI controller testing. + +ver 5.13: + Fix issue with PS3 controller detection. + Add support for data transfers to L2CAP testing tool. + Add support for delay reporting to AVDTP testing tool. + Add support for Android Bluetooth Core interface. + Add support for Android Bluetooth Socket interface. + Add support for Android Bluetooth HID Host interface. + Add support for Android Bluetooth PAN interface. + +ver 5.12: + Fix issue with missing reply to DisconnectProfile. + Fix issue with icon property and class of device changes. + Fix issue with HID devices when SDP record is not available. + Fix issue with handling auto-pairing of printers. + Fix issue with agent authorization handling. + Add support for PS3 controller setup and pairing. + Add support for LE L2CAP CoC test capabilities. + Add support for AVDTP qualification test cases. + Add support for SMP cryptographic test cases. + +ver 5.11: + Fix issue with connection attempt when not powered. + Fix issue with assigning player to AVRCP target role. + Fix issue with OBEX default cache directory. + Fix issue with SDP search error handling. + Fix issue with processing of SDP records. + Fix issue with HID to HCI switching utility. + Fix issue with mgmt end-to-end testing tool. + Fix issue with L2CAP end-to-end testing tool. + Add support for SMP end-to-end testing tool. + Add support for more Wii controllers. + +ver 5.10: + Fix issue with discoverable timeout handling. + Fix issue with MAP messages and record version. + Fix issue with MAP messages and status events. + Fix issue with MAP messages and relative folders. + Fix issue with MAP messages and type property signals. + Fix issue with transfer size for OBEX GET operations. + Fix issue with AVRCP service class identifier. + Fix issue with AVRCP tracking seeked signal. + Add support for OBEX command line client. + +ver 5.9: + Fix issue with network service and adapter removal. + Fix issue with misleading OBEX error messages. + Fix issue with OBEX transport reference handling. + Fix issue with memory leak with MAP event handler. + Fix issue with missing MAP property changed signal. + Fix issue with message type property values. + Fix issue with empty UUID list for devices. + Fix issue with profile agent cancel method. + Remove dependency on USB library. + +ver 5.8: + Fix issue with missing OBEX session properties. + Fix issue with missing SDP service refresh. + Fix issue with SDP attribute range check. + Fix issue with priority for SDP transactions. + Fix issue with service discovery after pairing. + Fix issue with race condition in service list. + Fix issue with input service state transition. + Fix issue with default authorization for profiles. + Fix issue with AVRCP browsing channel connections. + Add support for AVRCP role agnostic sessions. + +ver 5.7: + Fix issue with missing UUID discovery during pairing. + Fix issue with broken patch for SDP range check handling. + Fix issue with AVRCP usage of UID=0 for paused/stopped. + Add support MAP notification dispatching. + +ver 5.6: + Fix issue with incoming connections without SDP record. + Fix issue with canceling ongoing device connections. + Fix issue with handling failed connection attempts. + Fix issue with pending resume during A2DP open failures. + Fix issue with registering AVRCP unsupported notification. + Fix issue with listing available AVRCP target settings. + Fix issue with missing error for OBEX SetPath commands. + Fix issue with missing OBEX session command queue. + Fix issue with retrieving multiple MAP event reports. + Add support for command line player utility. + +ver 5.5: + Fix issue with race condition between SDP and properties. + Fix issue with handling storage of private device addresses. + Fix issue with NFC out-of-band pairing and power states. + Fix issue with short name during device update handling. + Fix issue with handling AVRCP without A2DP being present. + Add support for handling AVRCP pass-through operations. + Add support for automatically reconnecting HID devices. + Add support for automatically pairing of devices. + +ver 5.4: + Fix issue with invalid memory access and SDP service search. + Add support for available player changed event for controller. + Add support for UIDs changed event for AVRCP controller. + Add support for mandatory AVRCP pass-through operations. + Add support for Message Notification Service (MNS) server. + Add support for agent methods within command line client. + +ver 5.3: + Fix issue with registering invalid profiles. + Fix issue with inconsistent A2DP transport state. + Fix issue with A2DP resume while in configured state. + Fix issue with buffer overflow when processing SDP response. + Fix issue with missing range check for SDP attribute response. + Fix issue with missing validation of SDP data elements. + Fix issue with missing fallback to static hostname. + Fix issue with default adapter assignment. + +ver 5.2: + Fix issue with connection handling for Low Energy. + Fix issue with broken device discovery handling. + Fix issue with invalid memory access within A2DP. + Fix issue with handling empty path name of SetPath. + Fix issue with handling Message Access Profile filters. + Fix issue with handling network service unregistration. + Fix issue with not handling bogus device pairing results. + Fix issue with initial service discovery and profile manager. + Add support for AVRCP volume notifications. + Add support for AVRCP browsing commands. + +ver 5.1: + Fix issue with crash when removing OBEX session. + Fix issue with HID device disconnected from kernel. + Fix issue with buffer overflow when parsing HID SDP record. + Fix issue with SDP_TEXT_STR16 and SDP_URL_STR16 parsing. + Add support for integration with systemd's hostname daemon. + Add support for separate adapter alias property. + Add support for adapter and device modalias properties. + Add support for official BlueZ device information. + Add support for asynchronous management interface handling. + Add tool for testing management interface compliance. + Add tool for testing SDP qualification requirements. + Add tool for testing various EIR and AD data records. + +ver 5.0: + Introduce D-Bus Properties and ObjectManager interfaces. + Add support for generic profile interface. + Add support for global agent interface. + Add support for integrated OBEX daemon. + Add support for integrated hcidump utility. + Add support for Bluetooth tracing and monitor utility. + Add support for Bluetooth command line client utility. + Remove support for Handsfree gateway handling. + Remove support for GStreamer A2DP and SBC elements. + Disable default installation of Bluetooth library. + ver 4.101: Fix issue with missing BlueZ service file. Fix issue with aborting A2DP setup during AVDTP start. diff -Nru bluez-4.101/client/agent.c bluez-5.23/client/agent.c --- bluez-4.101/client/agent.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/client/agent.c 2014-05-19 08:51:52.000000000 +0000 @@ -0,0 +1,492 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "display.h" +#include "agent.h" + +#define AGENT_PATH "/org/bluez/agent" +#define AGENT_INTERFACE "org.bluez.Agent1" + +#define AGENT_PROMPT COLOR_RED "[agent]" COLOR_OFF " " + +static gboolean agent_registered = FALSE; +static const char *agent_capability = NULL; +static DBusMessage *pending_message = NULL; +static char *agent_saved_prompt = NULL; +static int agent_saved_point = 0; + +static void agent_prompt(const char *msg) +{ + char *prompt; + + /* Normal use should not prompt for user input to the agent a second + * time before it releases the prompt, but we take a safe action. */ + if (agent_saved_prompt) + return; + + agent_saved_point = rl_point; + agent_saved_prompt = g_strdup(rl_prompt); + + rl_set_prompt(""); + rl_redisplay(); + + prompt = g_strdup_printf(AGENT_PROMPT "%s", msg); + rl_set_prompt(prompt); + g_free(prompt); + + rl_replace_line("", 0); + rl_redisplay(); +} + +static void agent_release_prompt(void) +{ + if (!agent_saved_prompt) + return; + + /* This will cause rl_expand_prompt to re-run over the last prompt, but + * our prompt doesn't expand anyway. */ + rl_set_prompt(agent_saved_prompt); + rl_replace_line("", 0); + rl_point = agent_saved_point; + rl_redisplay(); + + g_free(agent_saved_prompt); + agent_saved_prompt = NULL; +} + +dbus_bool_t agent_completion(void) +{ + if (!pending_message) + return FALSE; + + return TRUE; +} + +static void pincode_response(DBusConnection *conn, const char *input) +{ + g_dbus_send_reply(conn, pending_message, DBUS_TYPE_STRING, &input, + DBUS_TYPE_INVALID); +} + +static void passkey_response(DBusConnection *conn, const char *input) +{ + dbus_uint32_t passkey; + if (sscanf(input, "%u", &passkey) == 1) + g_dbus_send_reply(conn, pending_message, DBUS_TYPE_UINT32, + &passkey, DBUS_TYPE_INVALID); + else if (!strcmp(input, "no")) + g_dbus_send_error(conn, pending_message, + "org.bluez.Error.Rejected", NULL); + else + g_dbus_send_error(conn, pending_message, + "org.bluez.Error.Canceled", NULL); +} + +static void confirm_response(DBusConnection *conn, const char *input) +{ + if (!strcmp(input, "yes")) + g_dbus_send_reply(conn, pending_message, DBUS_TYPE_INVALID); + else if (!strcmp(input, "no")) + g_dbus_send_error(conn, pending_message, + "org.bluez.Error.Rejected", NULL); + else + g_dbus_send_error(conn, pending_message, + "org.bluez.Error.Canceled", NULL); +} + +dbus_bool_t agent_input(DBusConnection *conn, const char *input) +{ + const char *member; + + if (!pending_message) + return FALSE; + + agent_release_prompt(); + + member = dbus_message_get_member(pending_message); + + if (!strcmp(member, "RequestPinCode")) + pincode_response(conn, input); + else if (!strcmp(member, "RequestPasskey")) + passkey_response(conn, input); + else if (!strcmp(member, "RequestConfirmation")) + confirm_response(conn, input); + else if (!strcmp(member, "RequestAuthorization")) + confirm_response(conn, input); + else if (!strcmp(member, "AuthorizeService")) + confirm_response(conn, input); + else + g_dbus_send_error(conn, pending_message, + "org.bluez.Error.Canceled", NULL); + + dbus_message_unref(pending_message); + pending_message = NULL; + + return TRUE; +} + +static void agent_release(DBusConnection *conn) +{ + agent_registered = FALSE; + agent_capability = NULL; + + if (pending_message) { + dbus_message_unref(pending_message); + pending_message = NULL; + } + + agent_release_prompt(); + + g_dbus_unregister_interface(conn, AGENT_PATH, AGENT_INTERFACE); +} + +static DBusMessage *release_agent(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + rl_printf("Agent released\n"); + + agent_release(conn); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *request_pincode(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + const char *device; + + rl_printf("Request PIN code\n"); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, + DBUS_TYPE_INVALID); + + agent_prompt("Enter PIN code: "); + + pending_message = dbus_message_ref(msg); + + return NULL; +} + +static DBusMessage *display_pincode(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + const char *device; + const char *pincode; + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, + DBUS_TYPE_STRING, &pincode, DBUS_TYPE_INVALID); + + rl_printf(AGENT_PROMPT "PIN code: %s\n", pincode); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *request_passkey(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + const char *device; + + rl_printf("Request passkey\n"); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, + DBUS_TYPE_INVALID); + + agent_prompt("Enter passkey (number in 0-999999): "); + + pending_message = dbus_message_ref(msg); + + return NULL; +} + +static DBusMessage *display_passkey(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + const char *device; + dbus_uint32_t passkey; + dbus_uint16_t entered; + char passkey_full[7]; + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, + DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_UINT16, &entered, + DBUS_TYPE_INVALID); + + snprintf(passkey_full, sizeof(passkey_full), "%.6u", passkey); + passkey_full[6] = '\0'; + + if (entered > strlen(passkey_full)) + entered = strlen(passkey_full); + + rl_printf(AGENT_PROMPT "Passkey: " + COLOR_BOLDGRAY "%.*s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, + entered, passkey_full, passkey_full + entered); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *request_confirmation(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + const char *device; + dbus_uint32_t passkey; + char *str; + + rl_printf("Request confirmation\n"); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, + DBUS_TYPE_UINT32, &passkey, DBUS_TYPE_INVALID); + + str = g_strdup_printf("Confirm passkey %06u (yes/no): ", passkey); + agent_prompt(str); + g_free(str); + + pending_message = dbus_message_ref(msg); + + return NULL; +} + +static DBusMessage *request_authorization(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + const char *device; + + rl_printf("Request authorization\n"); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, + DBUS_TYPE_INVALID); + + agent_prompt("Accept pairing (yes/no): "); + + pending_message = dbus_message_ref(msg); + + return NULL; +} + +static DBusMessage *authorize_service(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + const char *device, *uuid; + char *str; + + rl_printf("Authorize service\n"); + + dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device, + DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID); + + str = g_strdup_printf("Authorize service %s (yes/no): ", uuid); + agent_prompt(str); + g_free(str); + + pending_message = dbus_message_ref(msg); + + return NULL; +} + +static DBusMessage *cancel_request(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + rl_printf("Request canceled\n"); + + agent_release_prompt(); + dbus_message_unref(pending_message); + pending_message = NULL; + + return dbus_message_new_method_return(msg); +} + +static const GDBusMethodTable methods[] = { + { GDBUS_METHOD("Release", NULL, NULL, release_agent) }, + { GDBUS_ASYNC_METHOD("RequestPinCode", + GDBUS_ARGS({ "device", "o" }), + GDBUS_ARGS({ "pincode", "s" }), request_pincode) }, + { GDBUS_METHOD("DisplayPinCode", + GDBUS_ARGS({ "device", "o" }, { "pincode", "s" }), + NULL, display_pincode) }, + { GDBUS_ASYNC_METHOD("RequestPasskey", + GDBUS_ARGS({ "device", "o" }), + GDBUS_ARGS({ "passkey", "u" }), request_passkey) }, + { GDBUS_METHOD("DisplayPasskey", + GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }, + { "entered", "q" }), + NULL, display_passkey) }, + { GDBUS_ASYNC_METHOD("RequestConfirmation", + GDBUS_ARGS({ "device", "o" }, { "passkey", "u" }), + NULL, request_confirmation) }, + { GDBUS_ASYNC_METHOD("RequestAuthorization", + GDBUS_ARGS({ "device", "o" }), + NULL, request_authorization) }, + { GDBUS_ASYNC_METHOD("AuthorizeService", + GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }), + NULL, authorize_service) }, + { GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) }, + { } +}; + +static void register_agent_setup(DBusMessageIter *iter, void *user_data) +{ + const char *path = AGENT_PATH; + const char *capability = agent_capability; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &capability); +} + +static void register_agent_reply(DBusMessage *message, void *user_data) +{ + DBusConnection *conn = user_data; + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == FALSE) { + agent_registered = TRUE; + rl_printf("Agent registered\n"); + } else { + rl_printf("Failed to register agent: %s\n", error.name); + dbus_error_free(&error); + + if (g_dbus_unregister_interface(conn, AGENT_PATH, + AGENT_INTERFACE) == FALSE) + rl_printf("Failed to unregister agent object\n"); + } +} + +void agent_register(DBusConnection *conn, GDBusProxy *manager, + const char *capability) + +{ + if (agent_registered == TRUE) { + rl_printf("Agent is already registered\n"); + return; + } + + agent_capability = capability; + + if (g_dbus_register_interface(conn, AGENT_PATH, + AGENT_INTERFACE, methods, + NULL, NULL, NULL, NULL) == FALSE) { + rl_printf("Failed to register agent object\n"); + return; + } + + if (g_dbus_proxy_method_call(manager, "RegisterAgent", + register_agent_setup, + register_agent_reply, + conn, NULL) == FALSE) { + rl_printf("Failed to call register agent method\n"); + return; + } + + agent_capability = NULL; +} + +static void unregister_agent_setup(DBusMessageIter *iter, void *user_data) +{ + const char *path = AGENT_PATH; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); +} + +static void unregister_agent_reply(DBusMessage *message, void *user_data) +{ + DBusConnection *conn = user_data; + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == FALSE) { + rl_printf("Agent unregistered\n"); + agent_release(conn); + } else { + rl_printf("Failed to unregister agent: %s\n", error.name); + dbus_error_free(&error); + } +} + +void agent_unregister(DBusConnection *conn, GDBusProxy *manager) +{ + if (agent_registered == FALSE) { + rl_printf("No agent is registered\n"); + return; + } + + if (!manager) { + rl_printf("Agent unregistered\n"); + agent_release(conn); + return; + } + + if (g_dbus_proxy_method_call(manager, "UnregisterAgent", + unregister_agent_setup, + unregister_agent_reply, + conn, NULL) == FALSE) { + rl_printf("Failed to call unregister agent method\n"); + return; + } +} + +static void request_default_setup(DBusMessageIter *iter, void *user_data) +{ + const char *path = AGENT_PATH; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); +} + +static void request_default_reply(DBusMessage *message, void *user_data) +{ + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to request default agent: %s\n", error.name); + dbus_error_free(&error); + return; + } + + rl_printf("Default agent request successful\n"); +} + +void agent_default(DBusConnection *conn, GDBusProxy *manager) +{ + if (agent_registered == FALSE) { + rl_printf("No agent is registered\n"); + return; + } + + if (g_dbus_proxy_method_call(manager, "RequestDefaultAgent", + request_default_setup, + request_default_reply, + NULL, NULL) == FALSE) { + rl_printf("Failed to call request default agent method\n"); + return; + } +} diff -Nru bluez-4.101/client/agent.h bluez-5.23/client/agent.h --- bluez-4.101/client/agent.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/client/agent.h 2012-12-24 17:46:54.000000000 +0000 @@ -0,0 +1,30 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +void agent_register(DBusConnection *conn, GDBusProxy *manager, + const char *capability); +void agent_unregister(DBusConnection *conn, GDBusProxy *manager); +void agent_default(DBusConnection *conn, GDBusProxy *manager); + +dbus_bool_t agent_completion(void); +dbus_bool_t agent_input(DBusConnection *conn, const char *input); diff -Nru bluez-4.101/client/display.c bluez-5.23/client/display.c --- bluez-4.101/client/display.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/client/display.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,64 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "display.h" + +void rl_printf(const char *fmt, ...) +{ + va_list args; + bool save_input; + char *saved_line; + int saved_point; + + save_input = !RL_ISSTATE(RL_STATE_DONE); + + if (save_input) { + saved_point = rl_point; + saved_line = rl_copy_text(0, rl_end); + rl_save_prompt(); + rl_replace_line("", 0); + rl_redisplay(); + } + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + if (save_input) { + rl_restore_prompt(); + rl_replace_line(saved_line, 0); + rl_point = saved_point; + rl_forced_update_display(); + free(saved_line); + } +} diff -Nru bluez-4.101/client/display.h bluez-5.23/client/display.h --- bluez-4.101/client/display.h 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/client/display.h 2013-04-11 02:19:33.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#define COLOR_OFF "\x1B[0m" +#define COLOR_RED "\x1B[0;91m" +#define COLOR_GREEN "\x1B[0;92m" +#define COLOR_YELLOW "\x1B[0;93m" +#define COLOR_BLUE "\x1B[0;94m" +#define COLOR_BOLDGRAY "\x1B[1;30m" +#define COLOR_BOLDWHITE "\x1B[1;37m" + +void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); diff -Nru bluez-4.101/client/main.c bluez-5.23/client/main.c --- bluez-4.101/client/main.c 1970-01-01 00:00:00.000000000 +0000 +++ bluez-5.23/client/main.c 2014-09-07 23:23:46.000000000 +0000 @@ -0,0 +1,1554 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Intel Corporation. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "monitor/uuid.h" +#include "agent.h" +#include "display.h" + +/* String display constants */ +#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF +#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF +#define COLORED_DEL COLOR_RED "DEL" COLOR_OFF + +#define PROMPT_ON COLOR_BLUE "[bluetooth]" COLOR_OFF "# " +#define PROMPT_OFF "[bluetooth]# " + +static GMainLoop *main_loop; +static DBusConnection *dbus_conn; + +static GDBusProxy *agent_manager; +static char *auto_register_agent = NULL; + +static GDBusProxy *default_ctrl; +static GList *ctrl_list; +static GList *dev_list; + +static guint input = 0; + +static const char * const agent_arguments[] = { + "on", + "off", + "DisplayOnly", + "DisplayYesNo", + "KeyboardDisplay", + "KeyboardOnly", + "NoInputNoOutput", + NULL +}; + +static void proxy_leak(gpointer data) +{ + printf("Leaking proxy %p\n", data); +} + +static void connect_handler(DBusConnection *connection, void *user_data) +{ + rl_set_prompt(PROMPT_ON); + printf("\r"); + rl_on_new_line(); + rl_redisplay(); +} + +static void disconnect_handler(DBusConnection *connection, void *user_data) +{ + rl_set_prompt(PROMPT_OFF); + printf("\r"); + rl_on_new_line(); + rl_redisplay(); + + g_list_free(ctrl_list); + ctrl_list = NULL; + + default_ctrl = NULL; + + g_list_free(dev_list); + dev_list = NULL; +} + +static void print_adapter(GDBusProxy *proxy, const char *description) +{ + DBusMessageIter iter; + const char *address, *name; + + if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) + return; + + dbus_message_iter_get_basic(&iter, &address); + + if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE) + dbus_message_iter_get_basic(&iter, &name); + else + name = ""; + + rl_printf("%s%s%sController %s %s %s\n", + description ? "[" : "", + description ? : "", + description ? "] " : "", + address, name, + default_ctrl == proxy ? "[default]" : ""); + +} + +static void print_device(GDBusProxy *proxy, const char *description) +{ + DBusMessageIter iter; + const char *address, *name; + + if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) + return; + + dbus_message_iter_get_basic(&iter, &address); + + if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == TRUE) + dbus_message_iter_get_basic(&iter, &name); + else + name = ""; + + rl_printf("%s%s%sDevice %s %s\n", + description ? "[" : "", + description ? : "", + description ? "] " : "", + address, name); +} + +static void print_iter(const char *label, const char *name, + DBusMessageIter *iter) +{ + dbus_bool_t valbool; + dbus_uint32_t valu32; + dbus_uint16_t valu16; + dbus_int16_t vals16; + const char *valstr; + DBusMessageIter subiter; + int type; + + if (iter == NULL) { + rl_printf("%s%s is nil\n", label, name); + return; + } + + switch (dbus_message_iter_get_arg_type(iter)) { + case DBUS_TYPE_INVALID: + rl_printf("%s%s is invalid\n", label, name); + break; + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + dbus_message_iter_get_basic(iter, &valstr); + rl_printf("%s%s: %s\n", label, name, valstr); + break; + case DBUS_TYPE_BOOLEAN: + dbus_message_iter_get_basic(iter, &valbool); + rl_printf("%s%s: %s\n", label, name, + valbool == TRUE ? "yes" : "no"); + break; + case DBUS_TYPE_UINT32: + dbus_message_iter_get_basic(iter, &valu32); + rl_printf("%s%s: 0x%06x\n", label, name, valu32); + break; + case DBUS_TYPE_UINT16: + dbus_message_iter_get_basic(iter, &valu16); + rl_printf("%s%s: 0x%04x\n", label, name, valu16); + break; + case DBUS_TYPE_INT16: + dbus_message_iter_get_basic(iter, &vals16); + rl_printf("%s%s: %d\n", label, name, vals16); + break; + case DBUS_TYPE_ARRAY: + dbus_message_iter_recurse(iter, &subiter); + rl_printf("%s%s:\n", label, name); + + do { + type = dbus_message_iter_get_arg_type(&subiter); + if (type == DBUS_TYPE_INVALID) + break; + + if (type == DBUS_TYPE_STRING) { + dbus_message_iter_get_basic(&subiter, &valstr); + rl_printf("\t%s\n", valstr); + } + + dbus_message_iter_next(&subiter); + } while(true); + + break; + default: + rl_printf("%s%s has unsupported type\n", label, name); + break; + } +} + +static void print_property(GDBusProxy *proxy, const char *name) +{ + DBusMessageIter iter; + + if (g_dbus_proxy_get_property(proxy, name, &iter) == FALSE) + return; + + print_iter("\t", name, &iter); +} + +static void print_uuids(GDBusProxy *proxy) +{ + DBusMessageIter iter, value; + + if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE) + return; + + dbus_message_iter_recurse(&iter, &value); + + while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { + const char *uuid, *text; + + dbus_message_iter_get_basic(&value, &uuid); + + text = uuidstr_to_str(uuid); + if (text) { + char str[26]; + unsigned int n; + + str[sizeof(str) - 1] = '\0'; + + n = snprintf(str, sizeof(str), "%s", text); + if (n > sizeof(str) - 1) { + str[sizeof(str) - 2] = '.'; + str[sizeof(str) - 3] = '.'; + if (str[sizeof(str) - 4] == ' ') + str[sizeof(str) - 4] = '.'; + + n = sizeof(str) - 1; + } + + rl_printf("\tUUID: %s%*c(%s)\n", + str, 26 - n, ' ', uuid); + } else + rl_printf("\tUUID: %*c(%s)\n", 26, ' ', uuid); + + dbus_message_iter_next(&value); + } +} + +static gboolean device_is_child(GDBusProxy *device, GDBusProxy *master) +{ + DBusMessageIter iter; + const char *adapter, *path; + + if (!master) + return FALSE; + + if (g_dbus_proxy_get_property(device, "Adapter", &iter) == FALSE) + return FALSE; + + dbus_message_iter_get_basic(&iter, &adapter); + path = g_dbus_proxy_get_path(master); + + if (!strcmp(path, adapter)) + return TRUE; + + return FALSE; +} + +static void proxy_added(GDBusProxy *proxy, void *user_data) +{ + const char *interface; + + interface = g_dbus_proxy_get_interface(proxy); + + if (!strcmp(interface, "org.bluez.Device1")) { + if (device_is_child(proxy, default_ctrl) == TRUE) { + dev_list = g_list_append(dev_list, proxy); + + print_device(proxy, COLORED_NEW); + } + } else if (!strcmp(interface, "org.bluez.Adapter1")) { + ctrl_list = g_list_append(ctrl_list, proxy); + + if (!default_ctrl) + default_ctrl = proxy; + + print_adapter(proxy, COLORED_NEW); + } else if (!strcmp(interface, "org.bluez.AgentManager1")) { + if (!agent_manager) { + agent_manager = proxy; + + if (auto_register_agent) + agent_register(dbus_conn, agent_manager, + auto_register_agent); + } + } +} + +static void proxy_removed(GDBusProxy *proxy, void *user_data) +{ + const char *interface; + + interface = g_dbus_proxy_get_interface(proxy); + + if (!strcmp(interface, "org.bluez.Device1")) { + if (device_is_child(proxy, default_ctrl) == TRUE) { + dev_list = g_list_remove(dev_list, proxy); + + print_device(proxy, COLORED_DEL); + } + } else if (!strcmp(interface, "org.bluez.Adapter1")) { + ctrl_list = g_list_remove(ctrl_list, proxy); + + print_adapter(proxy, COLORED_DEL); + + if (default_ctrl == proxy) { + default_ctrl = NULL; + + g_list_free(dev_list); + dev_list = NULL; + } + } else if (!strcmp(interface, "org.bluez.AgentManager1")) { + if (agent_manager == proxy) { + agent_manager = NULL; + if (auto_register_agent) + agent_unregister(dbus_conn, NULL); + } + } +} + +static void property_changed(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, void *user_data) +{ + const char *interface; + + interface = g_dbus_proxy_get_interface(proxy); + + if (!strcmp(interface, "org.bluez.Device1")) { + if (device_is_child(proxy, default_ctrl) == TRUE) { + DBusMessageIter addr_iter; + char *str; + + if (g_dbus_proxy_get_property(proxy, "Address", + &addr_iter) == TRUE) { + const char *address; + + dbus_message_iter_get_basic(&addr_iter, + &address); + str = g_strdup_printf("[" COLORED_CHG + "] Device %s ", address); + } else + str = g_strdup(""); + + print_iter(str, name, iter); + g_free(str); + } + } else if (!strcmp(interface, "org.bluez.Adapter1")) { + DBusMessageIter addr_iter; + char *str; + + if (g_dbus_proxy_get_property(proxy, "Address", + &addr_iter) == TRUE) { + const char *address; + + dbus_message_iter_get_basic(&addr_iter, &address); + str = g_strdup_printf("[" COLORED_CHG + "] Controller %s ", address); + } else + str = g_strdup(""); + + print_iter(str, name, iter); + g_free(str); + } +} + +static void message_handler(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + rl_printf("[SIGNAL] %s.%s\n", dbus_message_get_interface(message), + dbus_message_get_member(message)); +} + +static GDBusProxy *find_proxy_by_address(GList *source, const char *address) +{ + GList *list; + + for (list = g_list_first(source); list; list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + DBusMessageIter iter; + const char *str; + + if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) + continue; + + dbus_message_iter_get_basic(&iter, &str); + + if (!strcmp(str, address)) + return proxy; + } + + return NULL; +} + +static gboolean check_default_ctrl(void) +{ + if (!default_ctrl) { + rl_printf("No default controller available\n"); + return FALSE; + } + + return TRUE; +} + +static gboolean parse_argument_on_off(const char *arg, dbus_bool_t *value) +{ + if (!arg || !strlen(arg)) { + rl_printf("Missing on/off argument\n"); + return FALSE; + } + + if (!strcmp(arg, "on") || !strcmp(arg, "yes")) { + *value = TRUE; + return TRUE; + } + + if (!strcmp(arg, "off") || !strcmp(arg, "no")) { + *value = FALSE; + return TRUE; + } + + rl_printf("Invalid argument %s\n", arg); + return FALSE; +} + +static gboolean parse_argument_agent(const char *arg, dbus_bool_t *value, + const char **capability) +{ + const char * const *opt; + + if (arg == NULL || strlen(arg) == 0) { + rl_printf("Missing on/off/capability argument\n"); + return FALSE; + } + + if (strcmp(arg, "on") == 0 || strcmp(arg, "yes") == 0) { + *value = TRUE; + *capability = ""; + return TRUE; + } + + if (strcmp(arg, "off") == 0 || strcmp(arg, "no") == 0) { + *value = FALSE; + return TRUE; + } + + for (opt = agent_arguments; *opt; opt++) { + if (strcmp(arg, *opt) == 0) { + *value = TRUE; + *capability = *opt; + return TRUE; + } + } + + rl_printf("Invalid argument %s\n", arg); + return FALSE; +} + +static void cmd_list(const char *arg) +{ + GList *list; + + for (list = g_list_first(ctrl_list); list; list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + print_adapter(proxy, NULL); + } +} + +static void cmd_show(const char *arg) +{ + GDBusProxy *proxy; + DBusMessageIter iter; + const char *address; + + if (!arg || !strlen(arg)) { + if (check_default_ctrl() == FALSE) + return; + + proxy = default_ctrl; + } else { + proxy = find_proxy_by_address(ctrl_list, arg); + if (!proxy) { + rl_printf("Controller %s not available\n", arg); + return; + } + } + + if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) + return; + + dbus_message_iter_get_basic(&iter, &address); + rl_printf("Controller %s\n", address); + + print_property(proxy, "Name"); + print_property(proxy, "Alias"); + print_property(proxy, "Class"); + print_property(proxy, "Powered"); + print_property(proxy, "Discoverable"); + print_property(proxy, "Pairable"); + print_uuids(proxy); + print_property(proxy, "Modalias"); + print_property(proxy, "Discovering"); +} + +static void cmd_select(const char *arg) +{ + GDBusProxy *proxy; + + if (!arg || !strlen(arg)) { + rl_printf("Missing controller address argument\n"); + return; + } + + proxy = find_proxy_by_address(ctrl_list, arg); + if (!proxy) { + rl_printf("Controller %s not available\n", arg); + return; + } + + if (default_ctrl == proxy) + return; + + default_ctrl = proxy; + print_adapter(proxy, NULL); + + g_list_free(dev_list); + dev_list = NULL; +} + +static void cmd_devices(const char *arg) +{ + GList *list; + + for (list = g_list_first(dev_list); list; list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + print_device(proxy, NULL); + } +} + +static void cmd_paired_devices(const char *arg) +{ + GList *list; + + for (list = g_list_first(dev_list); list; list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + DBusMessageIter iter; + dbus_bool_t paired; + + if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE) + continue; + + dbus_message_iter_get_basic(&iter, &paired); + if (!paired) + continue; + + print_device(proxy, NULL); + } +} + +static void generic_callback(const DBusError *error, void *user_data) +{ + char *str = user_data; + + if (dbus_error_is_set(error)) + rl_printf("Failed to set %s: %s\n", str, error->name); + else + rl_printf("Changing %s succeeded\n", str); +} + +static void cmd_system_alias(const char *arg) +{ + char *name; + + if (!arg || !strlen(arg)) { + rl_printf("Missing name argument\n"); + return; + } + + if (check_default_ctrl() == FALSE) + return; + + name = g_strdup(arg); + + if (g_dbus_proxy_set_property_basic(default_ctrl, "Alias", + DBUS_TYPE_STRING, &name, + generic_callback, name, g_free) == TRUE) + return; + + g_free(name); +} + +static void cmd_reset_alias(const char *arg) +{ + char *name; + + if (check_default_ctrl() == FALSE) + return; + + name = g_strdup(""); + + if (g_dbus_proxy_set_property_basic(default_ctrl, "Alias", + DBUS_TYPE_STRING, &name, + generic_callback, name, g_free) == TRUE) + return; + + g_free(name); +} + +static void cmd_power(const char *arg) +{ + dbus_bool_t powered; + char *str; + + if (parse_argument_on_off(arg, &powered) == FALSE) + return; + + if (check_default_ctrl() == FALSE) + return; + + str = g_strdup_printf("power %s", powered == TRUE ? "on" : "off"); + + if (g_dbus_proxy_set_property_basic(default_ctrl, "Powered", + DBUS_TYPE_BOOLEAN, &powered, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void cmd_pairable(const char *arg) +{ + dbus_bool_t pairable; + char *str; + + if (parse_argument_on_off(arg, &pairable) == FALSE) + return; + + if (check_default_ctrl() == FALSE) + return; + + str = g_strdup_printf("pairable %s", pairable == TRUE ? "on" : "off"); + + if (g_dbus_proxy_set_property_basic(default_ctrl, "Pairable", + DBUS_TYPE_BOOLEAN, &pairable, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void cmd_discoverable(const char *arg) +{ + dbus_bool_t discoverable; + char *str; + + if (parse_argument_on_off(arg, &discoverable) == FALSE) + return; + + if (check_default_ctrl() == FALSE) + return; + + str = g_strdup_printf("discoverable %s", + discoverable == TRUE ? "on" : "off"); + + if (g_dbus_proxy_set_property_basic(default_ctrl, "Discoverable", + DBUS_TYPE_BOOLEAN, &discoverable, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void cmd_agent(const char *arg) +{ + dbus_bool_t enable; + const char *capability; + + if (parse_argument_agent(arg, &enable, &capability) == FALSE) + return; + + if (enable == TRUE) { + g_free(auto_register_agent); + auto_register_agent = g_strdup(capability); + + if (agent_manager) + agent_register(dbus_conn, agent_manager, + auto_register_agent); + else + rl_printf("Agent registration enabled\n"); + } else { + g_free(auto_register_agent); + auto_register_agent = NULL; + + if (agent_manager) + agent_unregister(dbus_conn, agent_manager); + else + rl_printf("Agent registration disabled\n"); + } +} + +static void cmd_default_agent(const char *arg) +{ + agent_default(dbus_conn, agent_manager); +} + +static void start_discovery_reply(DBusMessage *message, void *user_data) +{ + dbus_bool_t enable = GPOINTER_TO_UINT(user_data); + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to %s discovery: %s\n", + enable == TRUE ? "start" : "stop", error.name); + dbus_error_free(&error); + return; + } + + rl_printf("Discovery %s\n", enable == TRUE ? "started" : "stopped"); +} + +static void cmd_scan(const char *arg) +{ + dbus_bool_t enable; + const char *method; + + if (parse_argument_on_off(arg, &enable) == FALSE) + return; + + if (check_default_ctrl() == FALSE) + return; + + if (enable == TRUE) + method = "StartDiscovery"; + else + method = "StopDiscovery"; + + if (g_dbus_proxy_method_call(default_ctrl, method, + NULL, start_discovery_reply, + GUINT_TO_POINTER(enable), NULL) == FALSE) { + rl_printf("Failed to %s discovery\n", + enable == TRUE ? "start" : "stop"); + return; + } +} + +static void cmd_info(const char *arg) +{ + GDBusProxy *proxy; + DBusMessageIter iter; + const char *address; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE) + return; + + dbus_message_iter_get_basic(&iter, &address); + rl_printf("Device %s\n", address); + + print_property(proxy, "Name"); + print_property(proxy, "Alias"); + print_property(proxy, "Class"); + print_property(proxy, "Appearance"); + print_property(proxy, "Icon"); + print_property(proxy, "Paired"); + print_property(proxy, "Trusted"); + print_property(proxy, "Blocked"); + print_property(proxy, "Connected"); + print_property(proxy, "LegacyPairing"); + print_uuids(proxy); + print_property(proxy, "Modalias"); +} + +static void pair_reply(DBusMessage *message, void *user_data) +{ + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to pair: %s\n", error.name); + dbus_error_free(&error); + return; + } + + rl_printf("Pairing successful\n"); +} + +static void cmd_pair(const char *arg) +{ + GDBusProxy *proxy; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply, + NULL, NULL) == FALSE) { + rl_printf("Failed to pair\n"); + return; + } + + rl_printf("Attempting to pair with %s\n", arg); +} + +static void cmd_trust(const char *arg) +{ + GDBusProxy *proxy; + dbus_bool_t trusted; + char *str; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + trusted = TRUE; + + str = g_strdup_printf("%s trust", arg); + + if (g_dbus_proxy_set_property_basic(proxy, "Trusted", + DBUS_TYPE_BOOLEAN, &trusted, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void cmd_untrust(const char *arg) +{ + GDBusProxy *proxy; + dbus_bool_t trusted; + char *str; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + trusted = FALSE; + + str = g_strdup_printf("%s untrust", arg); + + if (g_dbus_proxy_set_property_basic(proxy, "Trusted", + DBUS_TYPE_BOOLEAN, &trusted, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void cmd_block(const char *arg) +{ + GDBusProxy *proxy; + dbus_bool_t blocked; + char *str; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + blocked = TRUE; + + str = g_strdup_printf("%s block", arg); + + if (g_dbus_proxy_set_property_basic(proxy, "Blocked", + DBUS_TYPE_BOOLEAN, &blocked, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void cmd_unblock(const char *arg) +{ + GDBusProxy *proxy; + dbus_bool_t blocked; + char *str; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + blocked = FALSE; + + str = g_strdup_printf("%s unblock", arg); + + if (g_dbus_proxy_set_property_basic(proxy, "Blocked", + DBUS_TYPE_BOOLEAN, &blocked, + generic_callback, str, g_free) == TRUE) + return; + + g_free(str); +} + +static void remove_device_reply(DBusMessage *message, void *user_data) +{ + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to remove device: %s\n", error.name); + dbus_error_free(&error); + return; + } + + rl_printf("Device has been removed\n"); +} + +static void remove_device_setup(DBusMessageIter *iter, void *user_data) +{ + const char *path = user_data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path); +} + +static void cmd_remove(const char *arg) +{ + GDBusProxy *proxy; + char *path; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + if (check_default_ctrl() == FALSE) + return; + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + path = g_strdup(g_dbus_proxy_get_path(proxy)); + + if (g_dbus_proxy_method_call(default_ctrl, "RemoveDevice", + remove_device_setup, + remove_device_reply, + path, g_free) == FALSE) { + rl_printf("Failed to remove device\n"); + g_free(path); + return; + } +} + +static void connect_reply(DBusMessage *message, void *user_data) +{ + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to connect: %s\n", error.name); + dbus_error_free(&error); + return; + } + + rl_printf("Connection successful\n"); +} + +static void cmd_connect(const char *arg) +{ + GDBusProxy *proxy; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply, + NULL, NULL) == FALSE) { + rl_printf("Failed to connect\n"); + return; + } + + rl_printf("Attempting to connect to %s\n", arg); +} + +static void disconn_reply(DBusMessage *message, void *user_data) +{ + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + rl_printf("Failed to disconnect: %s\n", error.name); + dbus_error_free(&error); + return; + } + + rl_printf("Successful disconnected\n"); +} + +static void cmd_disconn(const char *arg) +{ + GDBusProxy *proxy; + + if (!arg || !strlen(arg)) { + rl_printf("Missing device address argument\n"); + return; + } + + proxy = find_proxy_by_address(dev_list, arg); + if (!proxy) { + rl_printf("Device %s not available\n", arg); + return; + } + + if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply, + NULL, NULL) == FALSE) { + rl_printf("Failed to disconnect\n"); + return; + } + + rl_printf("Attempting to disconnect from %s\n", arg); +} + +static void cmd_version(const char *arg) +{ + rl_printf("Version %s\n", VERSION); +} + +static void cmd_quit(const char *arg) +{ + g_main_loop_quit(main_loop); +} + +static char *generic_generator(const char *text, int state, + GList *source, const char *property) +{ + static int index, len; + GList *list; + + if (!state) { + index = 0; + len = strlen(text); + } + + for (list = g_list_nth(source, index); list; + list = g_list_next(list)) { + GDBusProxy *proxy = list->data; + DBusMessageIter iter; + const char *str; + + index++; + + if (g_dbus_proxy_get_property(proxy, property, &iter) == FALSE) + continue; + + dbus_message_iter_get_basic(&iter, &str); + + if (!strncmp(str, text, len)) + return strdup(str); + } + + return NULL; +} + +static char *ctrl_generator(const char *text, int state) +{ + return generic_generator(text, state, ctrl_list, "Address"); +} + +static char *dev_generator(const char *text, int state) +{ + return generic_generator(text, state, dev_list, "Address"); +} + +static char *capability_generator(const char *text, int state) +{ + static int index, len; + const char *arg; + + if (!state) { + index = 0; + len = strlen(text); + } + + while ((arg = agent_arguments[index])) { + index++; + + if (!strncmp(arg, text, len)) + return strdup(arg); + } + + return NULL; +} + +static const struct { + const char *cmd; + const char *arg; + void (*func) (const char *arg); + const char *desc; + char * (*gen) (const char *text, int state); + void (*disp) (char **matches, int num_matches, int max_length); +} cmd_table[] = { + { "list", NULL, cmd_list, "List available controllers" }, + { "show", "[ctrl]", cmd_show, "Controller information", + ctrl_generator }, + { "select", "", cmd_select, "Select default controller", + ctrl_generator }, + { "devices", NULL, cmd_devices, "List available devices" }, + { "paired-devices", NULL, cmd_paired_devices, + "List paired devices"}, + { "system-alias", "", cmd_system_alias }, + { "reset-alias", NULL, cmd_reset_alias }, + { "power", "", cmd_power, "Set controller power" }, + { "pairable", "", cmd_pairable, + "Set controller pairable mode" }, + { "discoverable", "", cmd_discoverable, + "Set controller discoverable mode" }, + { "agent", "", cmd_agent, + "Enable/disable agent with given capability", + capability_generator}, + { "default-agent",NULL, cmd_default_agent, + "Set agent as the default one" }, + { "scan", "", cmd_scan, "Scan for devices" }, + { "info", "", cmd_info, "Device information", + dev_generator }, + { "pair", "", cmd_pair, "Pair with device", + dev_generator }, + { "trust", "", cmd_trust, "Trust device", + dev_generator }, + { "untrust", "", cmd_untrust, "Untrust device", + dev_generator }, + { "block", "", cmd_block, "Block device", + dev_generator }, + { "unblock", "", cmd_unblock, "Unblock device", + dev_generator }, + { "remove", "", cmd_remove, "Remove device", + dev_generator }, + { "connect", "", cmd_connect, "Connect device", + dev_generator }, + { "disconnect", "", cmd_disconn, "Disconnect device", + dev_generator }, + { "version", NULL, cmd_version, "Display version" }, + { "quit", NULL, cmd_quit, "Quit program" }, + { "exit", NULL, cmd_quit }, + { "help" }, + { } +}; + +static char *cmd_generator(const char *text, int state) +{ + static int index, len; + const char *cmd; + + if (!state) { + index = 0; + len = strlen(text); + } + + while ((cmd = cmd_table[index].cmd)) { + index++; + + if (!strncmp(cmd, text, len)) + return strdup(cmd); + } + + return NULL; +} + +static char **cmd_completion(const char *text, int start, int end) +{ + char **matches = NULL; + + if (agent_completion() == TRUE) { + rl_attempted_completion_over = 1; + return NULL; + } + + if (start > 0) { + int i; + + for (i = 0; cmd_table[i].cmd; i++) { + if (strncmp(cmd_table[i].cmd, + rl_line_buffer, start - 1)) + continue; + + if (!cmd_table[i].gen) + continue; + + rl_completion_display_matches_hook = cmd_table[i].disp; + matches = rl_completion_matches(text, cmd_table[i].gen); + break; + } + } else { + rl_completion_display_matches_hook = NULL; + matches = rl_completion_matches(text, cmd_generator); + } + + if (!matches) + rl_attempted_completion_over = 1; + + return matches; +} + +static void rl_handler(char *input) +{ + char *cmd, *arg; + int i; + + if (!input) { + rl_insert_text("quit"); + rl_redisplay(); + rl_crlf(); + g_main_loop_quit(main_loop); + return; + } + + if (!strlen(input)) + goto done; + + if (agent_input(dbus_conn, input) == TRUE) + goto done; + + add_history(input); + + cmd = strtok_r(input, " ", &arg); + if (!cmd) + goto done; + + if (arg) { + int len = strlen(arg); + if (len > 0 && arg[len - 1] == ' ') + arg[len - 1] = '\0'; + } + + for (i = 0; cmd_table[i].cmd; i++) { + if (strcmp(cmd, cmd_table[i].cmd)) + continue; + + if (cmd_table[i].func) { + cmd_table[i].func(arg); + goto done; + } + } + + if (strcmp(cmd, "help")) { + printf("Invalid command\n"); + goto done; + } + + printf("Available commands:\n"); + + for (i = 0; cmd_table[i].cmd; i++) { + if (cmd_table[i].desc) + printf(" %s %-*s %s\n", cmd_table[i].cmd, + (int)(25 - strlen(cmd_table[i].cmd)), + cmd_table[i].arg ? : "", + cmd_table[i].desc ? : ""); + } + +done: + free(input); +} + +static gboolean input_handler(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + if (condition & G_IO_IN) { + rl_callback_read_char(); + return TRUE; + } + + if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { + g_main_loop_quit(main_loop); + return FALSE; + } + + return TRUE; +} + +static guint setup_standard_input(void) +{ + GIOChannel *channel; + guint source; + + channel = g_io_channel_unix_new(fileno(stdin)); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + input_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +static gboolean signal_handler(GIOChannel *channel, GIOCondition condition, + gpointer user_data) +{ + static unsigned int __terminated = 0; + struct signalfd_siginfo si; + ssize_t result; + int fd; + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + g_main_loop_quit(main_loop); + return FALSE; + } + + fd = g_io_channel_unix_get_fd(channel); + + result = read(fd, &si, sizeof(si)); + if (result != sizeof(si)) + return FALSE; + + switch (si.ssi_signo) { + case SIGINT: + if (input) { + rl_replace_line("", 0); + rl_crlf(); + rl_on_new_line(); + rl_redisplay(); + break; + } + + /* + * If input was not yet setup up that means signal was received + * while daemon was not yet running. Since user is not able + * to terminate client by CTRL-D or typing exit treat this as + * exit and fall through. + */ + case SIGTERM: + if (__terminated == 0) { + rl_replace_line("", 0); + rl_crlf(); + g_main_loop_quit(main_loop); + } + + __terminated = 1; + break; + } + + return TRUE; +} + +static guint setup_signalfd(void) +{ + GIOChannel *channel; + guint source; + sigset_t mask; + int fd; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + + if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0) { + perror("Failed to set signal mask"); + return 0; + } + + fd = signalfd(-1, &mask, 0); + if (fd < 0) { + perror("Failed to create signal descriptor"); + return 0; + } + + channel = g_io_channel_unix_new(fd); + + g_io_channel_set_close_on_unref(channel, TRUE); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + source = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + signal_handler, NULL); + + g_io_channel_unref(channel); + + return source; +} + +static gboolean option_version = FALSE; + +static gboolean parse_agent(const char *key, const char *value, + gpointer user_data, GError **error) +{ + if (value) + auto_register_agent = g_strdup(value); + else + auto_register_agent = g_strdup(""); + + return TRUE; +} + +static GOptionEntry options[] = { + { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, + "Show version information and exit" }, + { "agent", 'a', G_OPTION_FLAG_OPTIONAL_ARG, + G_OPTION_ARG_CALLBACK, parse_agent, + "Register agent handler", "CAPABILITY" }, + { NULL }, +}; + +static void client_ready(GDBusClient *client, void *user_data) +{ + guint *input = user_data; + + *input = setup_standard_input(); +} + +int main(int argc, char *argv[]) +{ + GOptionContext *context; + GError *error = NULL; + GDBusClient *client; + guint signal; + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, options, NULL); + + if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { + if (error != NULL) { + g_printerr("%s\n", error->message); + g_error_free(error); + } else + g_printerr("An unknown error occurred\n"); + exit(1); + } + + g_option_context_free(context); + + if (option_version == TRUE) { + printf("%s\n", VERSION); + exit(0); + } + + main_loop = g_main_loop_new(NULL, FALSE); + dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); + + rl_attempted_completion_function = cmd_completion; + + rl_erase_empty_line = 1; + rl_callback_handler_install(NULL, rl_handler); + + rl_set_prompt(PROMPT_OFF); + rl_redisplay(); + + signal = setup_signalfd(); + client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez"); + + g_dbus_client_set_connect_watch(client, connect_handler, NULL); + g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL); + g_dbus_client_set_signal_watch(client, message_handler, NULL); + + g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed, + property_changed, NULL); + + input = 0; + g_dbus_client_set_ready_watch(client, client_ready, &input); + + g_main_loop_run(main_loop); + + g_dbus_client_unref(client); + g_source_remove(signal); + if (input > 0) + g_source_remove(input); + + rl_message(""); + rl_callback_handler_remove(); + + dbus_connection_unref(dbus_conn); + g_main_loop_unref(main_loop); + + g_list_free_full(ctrl_list, proxy_leak); + g_list_free_full(dev_list, proxy_leak); + + g_free(auto_register_agent); + + return 0; +} diff -Nru bluez-4.101/compat/bnep.c bluez-5.23/compat/bnep.c --- bluez-4.101/compat/bnep.c 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/compat/bnep.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,339 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2002-2003 Maxim Krasnyansky - * Copyright (C) 2002-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "pand.h" - -static int ctl; - -/* Compatibility with old ioctls */ -#define OLD_BNEPCONADD 1 -#define OLD_BNEPCONDEL 2 -#define OLD_BNEPGETCONLIST 3 -#define OLD_BNEPGETCONINFO 4 - -static unsigned long bnepconnadd; -static unsigned long bnepconndel; -static unsigned long bnepgetconnlist; -static unsigned long bnepgetconninfo; - -static struct { - char *str; - uint16_t uuid; -} __svc[] = { - { "PANU", BNEP_SVC_PANU }, - { "NAP", BNEP_SVC_NAP }, - { "GN", BNEP_SVC_GN }, - { NULL } -}; - -int bnep_str2svc(char *svc, uint16_t *uuid) -{ - int i; - for (i = 0; __svc[i].str; i++) - if (!strcasecmp(svc, __svc[i].str)) { - *uuid = __svc[i].uuid; - return 0; - } - return -1; -} - -char *bnep_svc2str(uint16_t uuid) -{ - int i; - for (i = 0; __svc[i].str; i++) - if (__svc[i].uuid == uuid) - return __svc[i].str; - return NULL; -} - -int bnep_init(void) -{ - ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP); - if (ctl < 0) { - perror("Failed to open control socket"); - return 1; - } - - /* Temporary ioctl compatibility hack */ - { - struct bnep_connlist_req req; - struct bnep_conninfo ci[1]; - - req.cnum = 1; - req.ci = ci; - - if (!ioctl(ctl, BNEPGETCONNLIST, &req)) { - /* New ioctls */ - bnepconnadd = BNEPCONNADD; - bnepconndel = BNEPCONNDEL; - bnepgetconnlist = BNEPGETCONNLIST; - bnepgetconninfo = BNEPGETCONNINFO; - } else { - /* Old ioctls */ - bnepconnadd = OLD_BNEPCONADD; - bnepconndel = OLD_BNEPCONDEL; - bnepgetconnlist = OLD_BNEPGETCONLIST; - bnepgetconninfo = OLD_BNEPGETCONINFO; - } - } - - return 0; -} - -int bnep_cleanup(void) -{ - close(ctl); - return 0; -} - -int bnep_show_connections(void) -{ - struct bnep_connlist_req req; - struct bnep_conninfo ci[48]; - unsigned int i; - - req.cnum = 48; - req.ci = ci; - if (ioctl(ctl, bnepgetconnlist, &req)) { - perror("Failed to get connection list"); - return -1; - } - - for (i = 0; i < req.cnum; i++) { - char addr[18]; - ba2str((bdaddr_t *) ci[i].dst, addr); - printf("%s %s %s\n", ci[i].device, - addr, bnep_svc2str(ci[i].role)); - } - return 0; -} - -int bnep_kill_connection(uint8_t *dst) -{ - struct bnep_conndel_req req; - - memcpy(req.dst, dst, ETH_ALEN); - req.flags = 0; - if (ioctl(ctl, bnepconndel, &req)) { - perror("Failed to kill connection"); - return -1; - } - return 0; -} - -int bnep_kill_all_connections(void) -{ - struct bnep_connlist_req req; - struct bnep_conninfo ci[48]; - unsigned int i; - - req.cnum = 48; - req.ci = ci; - if (ioctl(ctl, bnepgetconnlist, &req)) { - perror("Failed to get connection list"); - return -1; - } - - for (i = 0; i < req.cnum; i++) { - struct bnep_conndel_req req; - memcpy(req.dst, ci[i].dst, ETH_ALEN); - req.flags = 0; - ioctl(ctl, bnepconndel, &req); - } - return 0; -} - -static int bnep_connadd(int sk, uint16_t role, char *dev) -{ - struct bnep_connadd_req req; - - strncpy(req.device, dev, 16); - req.device[15] = '\0'; - req.sock = sk; - req.role = role; - if (ioctl(ctl, bnepconnadd, &req)) - return -1; - strncpy(dev, req.device, 16); - return 0; -} - -struct __service_16 { - uint16_t dst; - uint16_t src; -} __attribute__ ((packed)); - -struct __service_32 { - uint16_t unused1; - uint16_t dst; - uint16_t unused2; - uint16_t src; -} __attribute__ ((packed)); - -struct __service_128 { - uint16_t unused1; - uint16_t dst; - uint16_t unused2[8]; - uint16_t src; - uint16_t unused3[7]; -} __attribute__ ((packed)); - -int bnep_accept_connection(int sk, uint16_t role, char *dev) -{ - struct bnep_setup_conn_req *req; - struct bnep_control_rsp *rsp; - unsigned char pkt[BNEP_MTU]; - ssize_t r; - - r = recv(sk, pkt, BNEP_MTU, 0); - if (r <= 0) - return -1; - - errno = EPROTO; - - if ((size_t) r < sizeof(*req)) - return -1; - - req = (void *) pkt; - - /* Highest known Control command ID - * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */ - if (req->type == BNEP_CONTROL && - req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) { - uint8_t pkt[3]; - - pkt[0] = BNEP_CONTROL; - pkt[1] = BNEP_CMD_NOT_UNDERSTOOD; - pkt[2] = req->ctrl; - - send(sk, pkt, sizeof(pkt), 0); - - return -1; - } - - if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) - return -1; - - /* FIXME: Check role UUIDs */ - - rsp = (void *) pkt; - rsp->type = BNEP_CONTROL; - rsp->ctrl = BNEP_SETUP_CONN_RSP; - rsp->resp = htons(BNEP_SUCCESS); - if (send(sk, rsp, sizeof(*rsp), 0) < 0) - return -1; - - return bnep_connadd(sk, role, dev); -} - -/* Create BNEP connection - * sk - Connect L2CAP socket - * role - Local role - * service - Remote service - * dev - Network device (contains actual dev name on return) - */ -int bnep_create_connection(int sk, uint16_t role, uint16_t svc, char *dev) -{ - struct bnep_setup_conn_req *req; - struct bnep_control_rsp *rsp; - struct __service_16 *s; - struct timeval timeo; - unsigned char pkt[BNEP_MTU]; - ssize_t r; - - /* Send request */ - req = (void *) pkt; - req->type = BNEP_CONTROL; - req->ctrl = BNEP_SETUP_CONN_REQ; - req->uuid_size = 2; /* 16bit UUID */ - - s = (void *) req->service; - s->dst = htons(svc); - s->src = htons(role); - - memset(&timeo, 0, sizeof(timeo)); - timeo.tv_sec = 30; - - setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); - - if (send(sk, pkt, sizeof(*req) + sizeof(*s), 0) < 0) - return -1; - -receive: - /* Get response */ - r = recv(sk, pkt, BNEP_MTU, 0); - if (r <= 0) - return -1; - - memset(&timeo, 0, sizeof(timeo)); - timeo.tv_sec = 0; - - setsockopt(sk, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo)); - - errno = EPROTO; - - if ((size_t) r < sizeof(*rsp)) - return -1; - - rsp = (void *) pkt; - if (rsp->type != BNEP_CONTROL) - return -1; - - if (rsp->ctrl != BNEP_SETUP_CONN_RSP) - goto receive; - - r = ntohs(rsp->resp); - - switch (r) { - case BNEP_SUCCESS: - break; - - case BNEP_CONN_INVALID_DST: - case BNEP_CONN_INVALID_SRC: - case BNEP_CONN_INVALID_SVC: - errno = EPROTO; - return -1; - - case BNEP_CONN_NOT_ALLOWED: - errno = EACCES; - return -1; - } - - return bnep_connadd(sk, role, dev); -} diff -Nru bluez-4.101/compat/dun.c bluez-5.23/compat/dun.c --- bluez-4.101/compat/dun.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/compat/dun.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,334 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2002-2003 Maxim Krasnyansky - * Copyright (C) 2002-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include "dund.h" -#include "lib.h" - -#define PROC_BASE "/proc" - -static int for_each_port(int (*func)(struct rfcomm_dev_info *, unsigned long), unsigned long arg) -{ - struct rfcomm_dev_list_req *dl; - struct rfcomm_dev_info *di; - long r = 0; - int sk, i; - - sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); - if (sk < 0 ) { - perror("Can't open RFCOMM control socket"); - exit(1); - } - - dl = malloc(sizeof(*dl) + RFCOMM_MAX_DEV * sizeof(*di)); - if (!dl) { - perror("Can't allocate request memory"); - close(sk); - exit(1); - } - - dl->dev_num = RFCOMM_MAX_DEV; - di = dl->dev_info; - - if (ioctl(sk, RFCOMMGETDEVLIST, (void *) dl) < 0) { - perror("Can't get device list"); - exit(1); - } - - for (i = 0; i < dl->dev_num; i++) { - r = func(di + i, arg); - if (r) break; - } - - close(sk); - free(dl); - return r; -} - -static int uses_rfcomm(char *path, char *dev) -{ - struct dirent *de; - DIR *dir; - - dir = opendir(path); - if (!dir) - return 0; - - if (chdir(path) < 0) - return 0; - - while ((de = readdir(dir)) != NULL) { - char link[PATH_MAX + 1]; - int len = readlink(de->d_name, link, PATH_MAX); - if (len > 0) { - link[len] = 0; - if (strstr(link, dev)) { - closedir(dir); - return 1; - } - } - } - - closedir(dir); - - return 0; -} - -static int find_pppd(int id, pid_t *pid) -{ - struct dirent *de; - char path[PATH_MAX + 1]; - char dev[10]; - int empty = 1; - DIR *dir; - - dir = opendir(PROC_BASE); - if (!dir) { - perror(PROC_BASE); - return -1; - } - - sprintf(dev, "rfcomm%d", id); - - *pid = 0; - while ((de = readdir(dir)) != NULL) { - empty = 0; - if (isdigit(de->d_name[0])) { - sprintf(path, "%s/%s/fd", PROC_BASE, de->d_name); - if (uses_rfcomm(path, dev)) { - *pid = atoi(de->d_name); - break; - } - } - } - closedir(dir); - - if (empty) - fprintf(stderr, "%s is empty (not mounted ?)\n", PROC_BASE); - - return *pid != 0; -} - -static int dun_exec(char *tty, char *prog, char **args) -{ - int pid = fork(); - int fd; - - switch (pid) { - case -1: - return -1; - - case 0: - break; - - default: - return pid; - } - - setsid(); - - /* Close all FDs */ - for (fd = 3; fd < 20; fd++) - close(fd); - - execvp(prog, args); - - syslog(LOG_ERR, "Error while executing %s", prog); - - exit(1); -} - -static int dun_create_tty(int sk, char *tty, int size) -{ - struct sockaddr_rc sa; - struct stat st; - socklen_t alen; - int id, try = 30; - - struct rfcomm_dev_req req = { - .flags = (1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP), - .dev_id = -1 - }; - - alen = sizeof(sa); - if (getpeername(sk, (struct sockaddr *) &sa, &alen) < 0) - return -1; - bacpy(&req.dst, &sa.rc_bdaddr); - - alen = sizeof(sa); - if (getsockname(sk, (struct sockaddr *) &sa, &alen) < 0) - return -1; - bacpy(&req.src, &sa.rc_bdaddr); - req.channel = sa.rc_channel; - - id = ioctl(sk, RFCOMMCREATEDEV, &req); - if (id < 0) - return id; - - snprintf(tty, size, "/dev/rfcomm%d", id); - while (stat(tty, &st) < 0) { - snprintf(tty, size, "/dev/bluetooth/rfcomm/%d", id); - if (stat(tty, &st) < 0) { - snprintf(tty, size, "/dev/rfcomm%d", id); - if (try--) { - usleep(100 * 1000); - continue; - } - - memset(&req, 0, sizeof(req)); - req.dev_id = id; - ioctl(sk, RFCOMMRELEASEDEV, &req); - - return -1; - } - } - - return id; -} - -int dun_init(void) -{ - return 0; -} - -int dun_cleanup(void) -{ - return 0; -} - -static int show_conn(struct rfcomm_dev_info *di, unsigned long arg) -{ - pid_t pid; - - if (di->state == BT_CONNECTED && - (di->flags & (1<flags & (1<flags & (1<id, &pid)) { - char dst[18]; - ba2str(&di->dst, dst); - - printf("rfcomm%d: %s channel %d pppd pid %d\n", - di->id, dst, di->channel, pid); - } - } - return 0; -} - -static int kill_conn(struct rfcomm_dev_info *di, unsigned long arg) -{ - bdaddr_t *dst = (bdaddr_t *) arg; - pid_t pid; - - if (di->state == BT_CONNECTED && - (di->flags & (1<flags & (1<flags & (1<dst, dst)) - return 0; - - if (find_pppd(di->id, &pid)) { - if (kill(pid, SIGINT) < 0) - perror("Kill"); - - if (!dst) - return 0; - return 1; - } - } - return 0; -} - -int dun_show_connections(void) -{ - for_each_port(show_conn, 0); - return 0; -} - -int dun_kill_connection(uint8_t *dst) -{ - for_each_port(kill_conn, (unsigned long) dst); - return 0; -} - -int dun_kill_all_connections(void) -{ - for_each_port(kill_conn, 0); - return 0; -} - -int dun_open_connection(int sk, char *pppd, char **args, int wait) -{ - char tty[100]; - int pid; - - if (dun_create_tty(sk, tty, sizeof(tty) - 1) < 0) { - syslog(LOG_ERR, "RFCOMM TTY creation failed. %s(%d)", strerror(errno), errno); - return -1; - } - - args[0] = "pppd"; - args[1] = tty; - args[2] = "nodetach"; - - pid = dun_exec(tty, pppd, args); - if (pid < 0) { - syslog(LOG_ERR, "Exec failed. %s(%d)", strerror(errno), errno); - return -1; - } - - if (wait) { - int status; - waitpid(pid, &status, 0); - /* FIXME: Check for waitpid errors */ - } - - return 0; -} diff -Nru bluez-4.101/compat/dund.1 bluez-5.23/compat/dund.1 --- bluez-4.101/compat/dund.1 2008-10-04 13:08:56.000000000 +0000 +++ bluez-5.23/compat/dund.1 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29. -.TH BlueZ "1" "February 2003" "DUN daemon" "User Commands" -.SH NAME -dund \- BlueZ Bluetooth dial-up networking daemon -.SH DESCRIPTION -DUN daemon -.SH SYNOPSIS -dund [pppd options] -.SH OPTIONS -.TP -\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR -Show active DUN connections -.TP -\fB\-\-listen\fR \fB\-s\fR -Listen for DUN connections -.TP -\fB\-\-dialup\fR \fB\-u\fR -Listen for dialup/telephone connections -.TP -\fB\-\-connect\fR \fB\-c\fR -Create DUN connection -.TP -\fB\-\-mrouter\fR \fB\-m\fR -Create mRouter connection -.TP -\fB\-\-search\fR \fB\-Q[duration]\fR -Search and connect -.TP -\fB\-\-kill\fR \fB\-k\fR -Kill DUN connection -.TP -\fB\-\-killall\fR \fB\-K\fR -Kill all DUN connections -.TP -\fB\-\-channel\fR \fB\-C\fR -RFCOMM channel -.TP -\fB\-\-device\fR \fB\-i\fR -Source bdaddr -.TP -\fB\-\-nosdp\fR \fB\-D\fR -Disable SDP -.TP -\fB\-\-auth\fR \fB\-A\fR -Enable authentification -.TP -\fB\-\-encrypt\fR \fB\-E\fR -Enable encryption -.TP -\fB\-\-secure\fR \fB\-S\fR -Secure connection -.TP -\fB\-\-master\fR \fB\-M\fR -Become the master of a piconet -.TP -\fB\-\-nodetach\fR \fB\-n\fR -Do not become a daemon -.TP -\fB\-\-persist\fR \fB\-p[interval]\fR -Persist mode -.TP -\fB\-\-pppd\fR \fB\-d\fR -Location of the PPP daemon (pppd) -.TP -\fB\-\-msdun\fR \fB\-X\fR [timeo] -Enable Microsoft dialup networking support -.TP -\fB\-\-activesync\fR \fB\-a\fR -Enable Microsoft ActiveSync networking -.TP -\fB\-\-cache\fR \fB\-C\fR [valid] -Enable address cache diff -Nru bluez-4.101/compat/dund.c bluez-5.23/compat/dund.c --- bluez-4.101/compat/dund.c 2010-05-23 12:47:33.000000000 +0000 +++ bluez-5.23/compat/dund.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,645 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2002-2003 Maxim Krasnyansky - * Copyright (C) 2002-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "sdp.h" -#include "dund.h" -#include "lib.h" - -volatile sig_atomic_t __io_canceled; - -/* MS dialup networking support (i.e. CLIENT / CLIENTSERVER thing) */ -static int msdun = 0; - -static char *pppd = "/usr/sbin/pppd"; -static char *pppd_opts[DUN_MAX_PPP_OPTS] = - { - /* First 3 are reserved */ - "", "", "", - "noauth", - "noipdefault", - NULL - }; - -static int detach = 1; -static int persist; -static int use_sdp = 1; -static int auth; -static int encrypt; -static int secure; -static int master; -static int type = LANACCESS; -static int search_duration = 10; -static uint use_cache; - -static int channel; - -static struct { - uint valid; - char dst[40]; - bdaddr_t bdaddr; - int channel; -} cache; - -static bdaddr_t src_addr = *BDADDR_ANY; -static int src_dev = -1; - -volatile int terminate; - -enum { - NONE, - SHOW, - LISTEN, - CONNECT, - KILL -} modes; - -static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter); - -static int do_listen(void) -{ - struct sockaddr_rc sa; - int sk, lm; - - if (type == MROUTER) { - if (!cache.valid) - return -1; - - if (create_connection(cache.dst, &cache.bdaddr, type) < 0) { - syslog(LOG_ERR, "Cannot connect to mRouter device. %s(%d)", - strerror(errno), errno); - return -1; - } - } - - if (!channel) - channel = DUN_DEFAULT_CHANNEL; - - if (use_sdp) - dun_sdp_register(&src_addr, channel, type); - - if (type == MROUTER) - syslog(LOG_INFO, "Waiting for mRouter callback on channel %d", channel); - - /* Create RFCOMM socket */ - sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sk < 0) { - syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)", - strerror(errno), errno); - return -1; - } - - sa.rc_family = AF_BLUETOOTH; - sa.rc_channel = channel; - sa.rc_bdaddr = src_addr; - - if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) { - syslog(LOG_ERR, "Bind failed. %s(%d)", strerror(errno), errno); - return -1; - } - - /* Set link mode */ - lm = 0; - if (master) - lm |= RFCOMM_LM_MASTER; - if (auth) - lm |= RFCOMM_LM_AUTH; - if (encrypt) - lm |= RFCOMM_LM_ENCRYPT; - if (secure) - lm |= RFCOMM_LM_SECURE; - - if (lm && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) { - syslog(LOG_ERR, "Failed to set link mode. %s(%d)", strerror(errno), errno); - return -1; - } - - listen(sk, 10); - - while (!terminate) { - socklen_t alen = sizeof(sa); - int nsk; - char ba[40]; - char ch[10]; - - nsk = accept(sk, (struct sockaddr *) &sa, &alen); - if (nsk < 0) { - syslog(LOG_ERR, "Accept failed. %s(%d)", strerror(errno), errno); - continue; - } - - switch (fork()) { - case 0: - break; - case -1: - syslog(LOG_ERR, "Fork failed. %s(%d)", strerror(errno), errno); - default: - close(nsk); - if (type == MROUTER) { - close(sk); - terminate = 1; - } - continue; - } - - close(sk); - - if (msdun && ms_dun(nsk, 1, msdun) < 0) { - syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno); - exit(0); - } - - ba2str(&sa.rc_bdaddr, ba); - snprintf(ch, sizeof(ch), "%d", channel); - - /* Setup environment */ - setenv("DUN_BDADDR", ba, 1); - setenv("DUN_CHANNEL", ch, 1); - - if (!dun_open_connection(nsk, pppd, pppd_opts, 0)) - syslog(LOG_INFO, "New connection from %s", ba); - - close(nsk); - exit(0); - } - - if (use_sdp) - dun_sdp_unregister(); - return 0; -} - -/* Connect and initiate RFCOMM session - * Returns: - * -1 - critical error (exit persist mode) - * 1 - non critical error - * 0 - success - */ -static int create_connection(char *dst, bdaddr_t *bdaddr, int mrouter) -{ - struct sockaddr_rc sa; - int sk, err = 0, ch; - - if (use_cache && cache.valid && cache.channel) { - /* Use cached channel */ - ch = cache.channel; - - } else if (!channel) { - syslog(LOG_INFO, "Searching for %s on %s", mrouter ? "SP" : "LAP", dst); - - if (dun_sdp_search(&src_addr, bdaddr, &ch, mrouter) <= 0) - return 0; - } else - ch = channel; - - syslog(LOG_INFO, "Connecting to %s channel %d", dst, ch); - - sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sk < 0) { - syslog(LOG_ERR, "Cannot create RFCOMM socket. %s(%d)", - strerror(errno), errno); - return -1; - } - - sa.rc_family = AF_BLUETOOTH; - sa.rc_channel = 0; - sa.rc_bdaddr = src_addr; - - if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) - syslog(LOG_ERR, "Bind failed. %s(%d)", - strerror(errno), errno); - - sa.rc_channel = ch; - sa.rc_bdaddr = *bdaddr; - - if (!connect(sk, (struct sockaddr *) &sa, sizeof(sa)) ) { - if (mrouter) { - sleep(1); - close(sk); - return 0; - } - - syslog(LOG_INFO, "Connection established"); - - if (msdun && ms_dun(sk, 0, msdun) < 0) { - syslog(LOG_ERR, "MSDUN failed. %s(%d)", strerror(errno), errno); - err = 1; - goto out; - } - - if (!dun_open_connection(sk, pppd, pppd_opts, (persist > 0))) - err = 0; - else - err = 1; - } else { - syslog(LOG_ERR, "Connect to %s failed. %s(%d)", - dst, strerror(errno), errno); - err = 1; - } - -out: - if (use_cache) { - if (!err) { - /* Succesesful connection, validate cache */ - strcpy(cache.dst, dst); - bacpy(&cache.bdaddr, bdaddr); - cache.channel = ch; - cache.valid = use_cache; - } else { - cache.channel = 0; - cache.valid--; - } - } - - close(sk); - return err; -} - -/* Search and connect - * Returns: - * -1 - critical error (exit persist mode) - * 1 - non critical error - * 0 - success - */ -static int do_connect(void) -{ - inquiry_info *ii; - int reconnect = 0; - int i, n, r = 0; - - do { - if (reconnect) - sleep(persist); - reconnect = 1; - - if (cache.valid) { - /* Use cached bdaddr */ - r = create_connection(cache.dst, &cache.bdaddr, 0); - if (r < 0) { - terminate = 1; - break; - } - continue; - } - - syslog(LOG_INFO, "Inquiring"); - - /* FIXME: Should we use non general LAP here ? */ - - ii = NULL; - n = hci_inquiry(src_dev, search_duration, 0, NULL, &ii, 0); - if (n < 0) { - syslog(LOG_ERR, "Inquiry failed. %s(%d)", strerror(errno), errno); - continue; - } - - for (i = 0; i < n; i++) { - char dst[40]; - ba2str(&ii[i].bdaddr, dst); - - r = create_connection(dst, &ii[i].bdaddr, 0); - if (r < 0) { - terminate = 1; - break; - } - } - bt_free(ii); - } while (!terminate && persist); - - return r; -} - -static void do_show(void) -{ - dun_show_connections(); -} - -static void do_kill(char *dst) -{ - if (dst) { - bdaddr_t ba; - str2ba(dst, &ba); - dun_kill_connection((void *) &ba); - } else - dun_kill_all_connections(); -} - -static void sig_hup(int sig) -{ - return; -} - -static void sig_term(int sig) -{ - io_cancel(); - terminate = 1; -} - -static struct option main_lopts[] = { - { "help", 0, 0, 'h' }, - { "listen", 0, 0, 's' }, - { "connect", 1, 0, 'c' }, - { "search", 2, 0, 'Q' }, - { "kill", 1, 0, 'k' }, - { "killall", 0, 0, 'K' }, - { "channel", 1, 0, 'P' }, - { "device", 1, 0, 'i' }, - { "nosdp", 0, 0, 'D' }, - { "list", 0, 0, 'l' }, - { "show", 0, 0, 'l' }, - { "nodetach", 0, 0, 'n' }, - { "persist", 2, 0, 'p' }, - { "auth", 0, 0, 'A' }, - { "encrypt", 0, 0, 'E' }, - { "secure", 0, 0, 'S' }, - { "master", 0, 0, 'M' }, - { "cache", 0, 0, 'C' }, - { "pppd", 1, 0, 'd' }, - { "msdun", 2, 0, 'X' }, - { "activesync", 0, 0, 'a' }, - { "mrouter", 1, 0, 'm' }, - { "dialup", 0, 0, 'u' }, - { 0, 0, 0, 0 } -}; - -static const char *main_sopts = "hsc:k:Kr:i:lnp::DQ::AESMP:C::P:Xam:u"; - -static const char *main_help = - "Bluetooth LAP (LAN Access over PPP) daemon version %s\n" - "Usage:\n" - "\tdund [pppd options]\n" - "Options:\n" - "\t--show --list -l Show active LAP connections\n" - "\t--listen -s Listen for LAP connections\n" - "\t--dialup -u Pretend to be a dialup/telephone\n" - "\t--connect -c Create LAP connection\n" - "\t--mrouter -m Create mRouter connection\n" - "\t--search -Q[duration] Search and connect\n" - "\t--kill -k Kill LAP connection\n" - "\t--killall -K Kill all LAP connections\n" - "\t--channel -P RFCOMM channel\n" - "\t--device -i Source bdaddr\n" - "\t--nosdp -D Disable SDP\n" - "\t--auth -A Enable authentication\n" - "\t--encrypt -E Enable encryption\n" - "\t--secure -S Secure connection\n" - "\t--master -M Become the master of a piconet\n" - "\t--nodetach -n Do not become a daemon\n" - "\t--persist -p[interval] Persist mode\n" - "\t--pppd -d Location of the PPP daemon (pppd)\n" - "\t--msdun -X[timeo] Enable Microsoft dialup networking support\n" - "\t--activesync -a Enable Microsoft ActiveSync networking\n" - "\t--cache -C[valid] Enable address cache\n"; - -int main(int argc, char *argv[]) -{ - char *dst = NULL, *src = NULL; - struct sigaction sa; - int mode = NONE; - int opt; - - while ((opt=getopt_long(argc, argv, main_sopts, main_lopts, NULL)) != -1) { - switch(opt) { - case 'l': - mode = SHOW; - detach = 0; - break; - - case 's': - mode = LISTEN; - type = LANACCESS; - break; - - case 'c': - mode = CONNECT; - dst = strdup(optarg); - break; - - case 'Q': - mode = CONNECT; - dst = NULL; - if (optarg) - search_duration = atoi(optarg); - break; - - case 'k': - mode = KILL; - detach = 0; - dst = strdup(optarg); - break; - - case 'K': - mode = KILL; - detach = 0; - dst = NULL; - break; - - case 'P': - channel = atoi(optarg); - break; - - case 'i': - src = strdup(optarg); - break; - - case 'D': - use_sdp = 0; - break; - - case 'A': - auth = 1; - break; - - case 'E': - encrypt = 1; - break; - - case 'S': - secure = 1; - break; - - case 'M': - master = 1; - break; - - case 'n': - detach = 0; - break; - - case 'p': - if (optarg) - persist = atoi(optarg); - else - persist = 5; - break; - - case 'C': - if (optarg) - use_cache = atoi(optarg); - else - use_cache = 2; - break; - - case 'd': - pppd = strdup(optarg); - break; - - case 'X': - if (optarg) - msdun = atoi(optarg); - else - msdun = 10; - break; - - case 'a': - msdun = 10; - type = ACTIVESYNC; - break; - - case 'm': - mode = LISTEN; - dst = strdup(optarg); - type = MROUTER; - break; - - case 'u': - mode = LISTEN; - type = DIALUP; - break; - - case 'h': - default: - printf(main_help, VERSION); - exit(0); - } - } - - argc -= optind; - argv += optind; - - /* The rest is pppd options */ - if (argc > 0) { - for (opt = 3; argc && opt < DUN_MAX_PPP_OPTS - 1; - argc--, opt++) - pppd_opts[opt] = *argv++; - pppd_opts[opt] = NULL; - } - - io_init(); - - if (dun_init()) { - free(dst); - return -1; - } - - /* Check non daemon modes first */ - switch (mode) { - case SHOW: - do_show(); - free(dst); - return 0; - - case KILL: - do_kill(dst); - free(dst); - return 0; - - case NONE: - printf(main_help, VERSION); - free(dst); - return 0; - } - - /* Initialize signals */ - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - sa.sa_handler = SIG_IGN; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - sa.sa_handler = sig_hup; - sigaction(SIGHUP, &sa, NULL); - - if (detach && daemon(0, 0)) { - perror("Can't start daemon"); - exit(1); - } - - openlog("dund", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON); - syslog(LOG_INFO, "Bluetooth DUN daemon version %s", VERSION); - - if (src) { - src_dev = hci_devid(src); - if (src_dev < 0 || hci_devba(src_dev, &src_addr) < 0) { - syslog(LOG_ERR, "Invalid source. %s(%d)", strerror(errno), errno); - free(dst); - return -1; - } - } - - if (dst) { - strncpy(cache.dst, dst, sizeof(cache.dst) - 1); - str2ba(dst, &cache.bdaddr); - - /* Disable cache invalidation */ - use_cache = cache.valid = ~0; - } - - switch (mode) { - case CONNECT: - do_connect(); - break; - - case LISTEN: - do_listen(); - break; - } - - free(dst); - return 0; -} diff -Nru bluez-4.101/compat/dund.h bluez-5.23/compat/dund.h --- bluez-4.101/compat/dund.h 2010-05-23 12:47:33.000000000 +0000 +++ bluez-5.23/compat/dund.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2002-2003 Maxim Krasnyansky - * Copyright (C) 2002-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define DUN_CONFIG_DIR "/etc/bluetooth/dun" - -#define DUN_DEFAULT_CHANNEL 1 - -#define DUN_MAX_PPP_OPTS 40 - -int dun_init(void); -int dun_cleanup(void); - -int dun_show_connections(void); -int dun_kill_connection(uint8_t *dst); -int dun_kill_all_connections(void); - -int dun_open_connection(int sk, char *pppd, char **pppd_opts, int wait); - -int ms_dun(int fd, int server, int timeo); diff -Nru bluez-4.101/compat/fakehid.c bluez-5.23/compat/fakehid.c --- bluez-4.101/compat/fakehid.c 2011-05-31 02:38:52.000000000 +0000 +++ bluez-5.23/compat/fakehid.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,669 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2003-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "hidd.h" -#include "uinput.h" - -#include - -#ifdef NEED_PPOLL -#include "ppoll.h" -#endif - -static volatile sig_atomic_t __io_canceled = 0; - -static void sig_hup(int sig) -{ -} - -static void sig_term(int sig) -{ - __io_canceled = 1; -} - -static int send_event(int fd, uint16_t type, uint16_t code, int32_t value) -{ - struct uinput_event event; - - if (fd <= fileno(stderr)) - return -EINVAL; - - memset(&event, 0, sizeof(event)); - event.type = type; - event.code = code; - event.value = value; - - return write(fd, &event, sizeof(event)); -} - -static int uinput_create(char *name, int keyboard, int mouse) -{ - struct uinput_dev dev; - int fd, aux; - - fd = open("/dev/uinput", O_RDWR); - if (fd < 0) { - fd = open("/dev/input/uinput", O_RDWR); - if (fd < 0) { - fd = open("/dev/misc/uinput", O_RDWR); - if (fd < 0) { - fprintf(stderr, "Can't open input device: %s (%d)\n", - strerror(errno), errno); - return -1; - } - } - } - - memset(&dev, 0, sizeof(dev)); - - if (name) - strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1); - - dev.id.bustype = BUS_BLUETOOTH; - dev.id.vendor = 0x0000; - dev.id.product = 0x0000; - dev.id.version = 0x0000; - - if (write(fd, &dev, sizeof(dev)) < 0) { - fprintf(stderr, "Can't write device information: %s (%d)\n", - strerror(errno), errno); - close(fd); - return -1; - } - - if (mouse) { - ioctl(fd, UI_SET_EVBIT, EV_REL); - - for (aux = REL_X; aux <= REL_MISC; aux++) - ioctl(fd, UI_SET_RELBIT, aux); - } - - if (keyboard) { - ioctl(fd, UI_SET_EVBIT, EV_KEY); - ioctl(fd, UI_SET_EVBIT, EV_LED); - ioctl(fd, UI_SET_EVBIT, EV_REP); - - for (aux = KEY_RESERVED; aux <= KEY_UNKNOWN; aux++) - ioctl(fd, UI_SET_KEYBIT, aux); - /* - *for (aux = LED_NUML; aux <= LED_MISC; aux++) - * ioctl(fd, UI_SET_LEDBIT, aux); - */ - } - - if (mouse) { - ioctl(fd, UI_SET_EVBIT, EV_KEY); - - for (aux = BTN_LEFT; aux <= BTN_BACK; aux++) - ioctl(fd, UI_SET_KEYBIT, aux); - } - - ioctl(fd, UI_DEV_CREATE); - - return fd; -} - -static int rfcomm_connect(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) -{ - struct sockaddr_rc addr; - int sk; - - sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (sk < 0) { - fprintf(stderr, "Can't create socket: %s (%d)\n", - strerror(errno), errno); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, src); - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - fprintf(stderr, "Can't bind socket: %s (%d)\n", - strerror(errno), errno); - close(sk); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.rc_family = AF_BLUETOOTH; - bacpy(&addr.rc_bdaddr, dst); - addr.rc_channel = channel; - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - fprintf(stderr, "Can't connect: %s (%d)\n", - strerror(errno), errno); - close(sk); - return -1; - } - - return sk; -} - -static void func(int fd) -{ -} - -static void back(int fd) -{ -} - -static void next(int fd) -{ -} - -static void button(int fd, unsigned int button, int is_press) -{ - switch (button) { - case 1: - send_event(fd, EV_KEY, BTN_LEFT, is_press); - break; - case 3: - send_event(fd, EV_KEY, BTN_RIGHT, is_press); - break; - } - - send_event(fd, EV_SYN, SYN_REPORT, 0); -} - -static void move(int fd, unsigned int direction) -{ - double angle; - int32_t x, y; - - angle = (direction * 22.5) * 3.1415926 / 180; - x = (int) (sin(angle) * 8); - y = (int) (cos(angle) * -8); - - send_event(fd, EV_REL, REL_X, x); - send_event(fd, EV_REL, REL_Y, y); - - send_event(fd, EV_SYN, SYN_REPORT, 0); -} - -static inline void epox_decode(int fd, unsigned char event) -{ - switch (event) { - case 48: - func(fd); break; - case 55: - back(fd); break; - case 56: - next(fd); break; - case 53: - button(fd, 1, 1); break; - case 121: - button(fd, 1, 0); break; - case 113: - break; - case 54: - button(fd, 3, 1); break; - case 120: - button(fd, 3, 0); break; - case 112: - break; - case 51: - move(fd, 0); break; - case 97: - move(fd, 1); break; - case 65: - move(fd, 2); break; - case 98: - move(fd, 3); break; - case 50: - move(fd, 4); break; - case 99: - move(fd, 5); break; - case 67: - move(fd, 6); break; - case 101: - move(fd, 7); break; - case 52: - move(fd, 8); break; - case 100: - move(fd, 9); break; - case 66: - move(fd, 10); break; - case 102: - move(fd, 11); break; - case 49: - move(fd, 12); break; - case 103: - move(fd, 13); break; - case 57: - move(fd, 14); break; - case 104: - move(fd, 15); break; - case 69: - break; - default: - printf("Unknown event code %d\n", event); - break; - } -} - -int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) -{ - unsigned char buf[16]; - struct sigaction sa; - struct pollfd p; - sigset_t sigs; - char addr[18]; - int i, fd, sk, len; - - sk = rfcomm_connect(src, dst, channel); - if (sk < 0) - return -1; - - fd = uinput_create("Bluetooth Presenter", 0, 1); - if (fd < 0) { - close(sk); - return -1; - } - - ba2str(dst, addr); - - printf("Connected to %s on channel %d\n", addr, channel); - printf("Press CTRL-C for hangup\n"); - - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - sa.sa_handler = SIG_IGN; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - sa.sa_handler = sig_hup; - sigaction(SIGHUP, &sa, NULL); - - sigfillset(&sigs); - sigdelset(&sigs, SIGCHLD); - sigdelset(&sigs, SIGPIPE); - sigdelset(&sigs, SIGTERM); - sigdelset(&sigs, SIGINT); - sigdelset(&sigs, SIGHUP); - - p.fd = sk; - p.events = POLLIN | POLLERR | POLLHUP; - - while (!__io_canceled) { - p.revents = 0; - if (ppoll(&p, 1, NULL, &sigs) < 1) - continue; - - len = read(sk, buf, sizeof(buf)); - if (len < 0) - break; - - for (i = 0; i < len; i++) - epox_decode(fd, buf[i]); - } - - printf("Disconnected\n"); - - ioctl(fd, UI_DEV_DESTROY); - - close(fd); - close(sk); - - return 0; -} - -int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) -{ - printf("Not implemented\n"); - return -1; -} - -/* The strange meta key close to Ctrl has been assigned to Esc, - Fn key to CtrlR and the left space to Alt*/ - -static unsigned char jthree_keycodes[63] = { - KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, - KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, - KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, - KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, - KEY_LEFTALT, KEY_TAB, KEY_CAPSLOCK, KEY_ESC, - KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, - KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, - KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, - KEY_N, KEY_M, KEY_COMMA, KEY_DOT, KEY_SLASH, KEY_UP, - KEY_SPACE, KEY_COMPOSE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, - KEY_LEFTCTRL, KEY_RIGHTSHIFT, KEY_LEFTSHIFT, KEY_DELETE, KEY_RIGHTCTRL, KEY_RIGHTALT, -}; - -static inline void jthree_decode(int fd, unsigned char event) -{ - if (event > 63) - send_event(fd, EV_KEY, jthree_keycodes[event & 0x3f], 0); - else - send_event(fd, EV_KEY, jthree_keycodes[event - 1], 1); -} - -int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) -{ - unsigned char buf[16]; - struct sigaction sa; - struct pollfd p; - sigset_t sigs; - char addr[18]; - int i, fd, sk, len; - - sk = rfcomm_connect(src, dst, channel); - if (sk < 0) - return -1; - - fd = uinput_create("J-Three Keyboard", 1, 0); - if (fd < 0) { - close(sk); - return -1; - } - - ba2str(dst, addr); - - printf("Connected to %s on channel %d\n", addr, channel); - printf("Press CTRL-C for hangup\n"); - - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - sa.sa_handler = SIG_IGN; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - sa.sa_handler = sig_hup; - sigaction(SIGHUP, &sa, NULL); - - sigfillset(&sigs); - sigdelset(&sigs, SIGCHLD); - sigdelset(&sigs, SIGPIPE); - sigdelset(&sigs, SIGTERM); - sigdelset(&sigs, SIGINT); - sigdelset(&sigs, SIGHUP); - - p.fd = sk; - p.events = POLLIN | POLLERR | POLLHUP; - - while (!__io_canceled) { - p.revents = 0; - if (ppoll(&p, 1, NULL, &sigs) < 1) - continue; - - len = read(sk, buf, sizeof(buf)); - if (len < 0) - break; - - for (i = 0; i < len; i++) - jthree_decode(fd, buf[i]); - } - - printf("Disconnected\n"); - - ioctl(fd, UI_DEV_DESTROY); - - close(fd); - close(sk); - - return 0; -} - -static const int celluon_xlate_num[10] = { - KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9 -}; - -static const int celluon_xlate_char[26] = { - KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, - KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, - KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z -}; - -static int celluon_xlate(int c) -{ - if (c >= '0' && c <= '9') - return celluon_xlate_num[c - '0']; - - if (c >= 'A' && c <= 'Z') - return celluon_xlate_char[c - 'A']; - - switch (c) { - case 0x08: - return KEY_BACKSPACE; - case 0x09: - return KEY_TAB; - case 0x0d: - return KEY_ENTER; - case 0x11: - return KEY_LEFTCTRL; - case 0x14: - return KEY_CAPSLOCK; - case 0x20: - return KEY_SPACE; - case 0x25: - return KEY_LEFT; - case 0x26: - return KEY_UP; - case 0x27: - return KEY_RIGHT; - case 0x28: - return KEY_DOWN; - case 0x2e: - return KEY_DELETE; - case 0x5b: - return KEY_MENU; - case 0xa1: - return KEY_RIGHTSHIFT; - case 0xa0: - return KEY_LEFTSHIFT; - case 0xba: - return KEY_SEMICOLON; - case 0xbd: - return KEY_MINUS; - case 0xbc: - return KEY_COMMA; - case 0xbb: - return KEY_EQUAL; - case 0xbe: - return KEY_DOT; - case 0xbf: - return KEY_SLASH; - case 0xc0: - return KEY_GRAVE; - case 0xdb: - return KEY_LEFTBRACE; - case 0xdc: - return KEY_BACKSLASH; - case 0xdd: - return KEY_RIGHTBRACE; - case 0xde: - return KEY_APOSTROPHE; - case 0xff03: - return KEY_HOMEPAGE; - case 0xff04: - return KEY_TIME; - case 0xff06: - return KEY_OPEN; - case 0xff07: - return KEY_LIST; - case 0xff08: - return KEY_MAIL; - case 0xff30: - return KEY_CALC; - case 0xff1a: /* Map FN to ALT */ - return KEY_LEFTALT; - case 0xff2f: - return KEY_INFO; - default: - printf("Unknown key %x\n", c); - return c; - } -} - -struct celluon_state { - int len; /* Expected length of current packet */ - int count; /* Number of bytes received */ - int action; - int key; -}; - -static void celluon_decode(int fd, struct celluon_state *s, uint8_t c) -{ - if (s->count < 2 && c != 0xa5) { - /* Lost Sync */ - s->count = 0; - return; - } - - switch (s->count) { - case 0: - /* New packet - Reset state */ - s->len = 30; - s->key = 0; - break; - case 1: - break; - case 6: - s->action = c; - break; - case 28: - s->key = c; - if (c == 0xff) - s->len = 31; - break; - case 29: - case 30: - if (s->count == s->len - 1) { - /* TODO: Verify checksum */ - if (s->action < 2) { - send_event(fd, EV_KEY, celluon_xlate(s->key), - s->action); - } - s->count = -1; - } else { - s->key = (s->key << 8) | c; - } - break; - } - - s->count++; - - return; -} - -int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel) -{ - unsigned char buf[16]; - struct sigaction sa; - struct pollfd p; - sigset_t sigs; - char addr[18]; - int i, fd, sk, len; - struct celluon_state s; - - sk = rfcomm_connect(src, dst, channel); - if (sk < 0) - return -1; - - fd = uinput_create("Celluon Keyboard", 1, 0); - if (fd < 0) { - close(sk); - return -1; - } - - ba2str(dst, addr); - - printf("Connected to %s on channel %d\n", addr, channel); - printf("Press CTRL-C for hangup\n"); - - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - sa.sa_handler = SIG_IGN; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - sa.sa_handler = sig_hup; - sigaction(SIGHUP, &sa, NULL); - - sigfillset(&sigs); - sigdelset(&sigs, SIGCHLD); - sigdelset(&sigs, SIGPIPE); - sigdelset(&sigs, SIGTERM); - sigdelset(&sigs, SIGINT); - sigdelset(&sigs, SIGHUP); - - p.fd = sk; - p.events = POLLIN | POLLERR | POLLHUP; - - memset(&s, 0, sizeof(s)); - - while (!__io_canceled) { - p.revents = 0; - if (ppoll(&p, 1, NULL, &sigs) < 1) - continue; - - len = read(sk, buf, sizeof(buf)); - if (len < 0) - break; - - for (i = 0; i < len; i++) - celluon_decode(fd, &s, buf[i]); - } - - printf("Disconnected\n"); - - ioctl(fd, UI_DEV_DESTROY); - - close(fd); - close(sk); - - return 0; -} diff -Nru bluez-4.101/compat/hidd.1 bluez-5.23/compat/hidd.1 --- bluez-4.101/compat/hidd.1 2008-10-04 13:08:56.000000000 +0000 +++ bluez-5.23/compat/hidd.1 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.33. -.TH HIDD "1" "May 2004" "hidd - Bluetooth HID daemon" "User Commands" -.SH NAME -hidd \- Bluetooth HID daemon -.SH DESCRIPTION -hidd - Bluetooth HID daemon -.SS "Usage:" -.IP -hidd [options] [commands] -.SH OPTIONS -.TP -\fB\-i\fR -Local HCI device or BD Address -.TP -\fB\-t\fR -Set idle timeout (in minutes) -.TP -\fB\-n\fR, \fB\-\-nodaemon\fR -Don't fork daemon to background -.TP -\fB\-h\fR, \fB\-\-help\fR -Display help -.SS "Commands:" -.TP -\fB\-\-server\fR -Start HID server -.TP -\fB\-\-search\fR -Search for HID devices -.TP -\fB\-\-connect\fR -Connect remote HID device -.TP -\fB\-\-kill\fR -Terminate HID connection -.TP -\fB\-\-killall\fR -Terminate all connections -.TP -\fB\-\-show\fR -List current HID connections diff -Nru bluez-4.101/compat/hidd.c bluez-5.23/compat/hidd.c --- bluez-4.101/compat/hidd.c 2012-06-13 15:04:20.000000000 +0000 +++ bluez-5.23/compat/hidd.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,848 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2003-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "sdp.h" -#include "hidd.h" - -#ifdef NEED_PPOLL -#include "ppoll.h" -#endif - -enum { - NONE, - SHOW, - SERVER, - SEARCH, - CONNECT, - KILL -}; - -static volatile sig_atomic_t __io_canceled = 0; - -static void sig_hup(int sig) -{ -} - -static void sig_term(int sig) -{ - __io_canceled = 1; -} - -static int l2cap_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm) -{ - struct sockaddr_l2 addr; - struct l2cap_options opts; - int sk; - - if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) - return -1; - - memset(&addr, 0, sizeof(addr)); - addr.l2_family = AF_BLUETOOTH; - bacpy(&addr.l2_bdaddr, src); - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return -1; - } - - memset(&opts, 0, sizeof(opts)); - opts.imtu = HIDP_DEFAULT_MTU; - opts.omtu = HIDP_DEFAULT_MTU; - opts.flush_to = 0xffff; - - setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)); - - memset(&addr, 0, sizeof(addr)); - addr.l2_family = AF_BLUETOOTH; - bacpy(&addr.l2_bdaddr, dst); - addr.l2_psm = htobs(psm); - - if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return -1; - } - - return sk; -} - -static int l2cap_listen(const bdaddr_t *bdaddr, unsigned short psm, int lm, int backlog) -{ - struct sockaddr_l2 addr; - struct l2cap_options opts; - int sk; - - if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) - return -1; - - memset(&addr, 0, sizeof(addr)); - addr.l2_family = AF_BLUETOOTH; - bacpy(&addr.l2_bdaddr, bdaddr); - addr.l2_psm = htobs(psm); - - if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(sk); - return -1; - } - - setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)); - - memset(&opts, 0, sizeof(opts)); - opts.imtu = HIDP_DEFAULT_MTU; - opts.omtu = HIDP_DEFAULT_MTU; - opts.flush_to = 0xffff; - - setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)); - - if (listen(sk, backlog) < 0) { - close(sk); - return -1; - } - - return sk; -} - -static int l2cap_accept(int sk, bdaddr_t *bdaddr) -{ - struct sockaddr_l2 addr; - socklen_t addrlen; - int nsk; - - memset(&addr, 0, sizeof(addr)); - addrlen = sizeof(addr); - - if ((nsk = accept(sk, (struct sockaddr *) &addr, &addrlen)) < 0) - return -1; - - if (bdaddr) - bacpy(bdaddr, &addr.l2_bdaddr); - - return nsk; -} - -static int request_authentication(bdaddr_t *src, bdaddr_t *dst) -{ - struct hci_conn_info_req *cr; - char addr[18]; - int err, dd, dev_id; - - ba2str(src, addr); - dev_id = hci_devid(addr); - if (dev_id < 0) - return dev_id; - - dd = hci_open_dev(dev_id); - if (dd < 0) - return dd; - - cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); - if (!cr) - return -ENOMEM; - - bacpy(&cr->bdaddr, dst); - cr->type = ACL_LINK; - err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr); - if (err < 0) { - free(cr); - hci_close_dev(dd); - return err; - } - - err = hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000); - - free(cr); - hci_close_dev(dd); - - return err; -} - -static int request_encryption(bdaddr_t *src, bdaddr_t *dst) -{ - struct hci_conn_info_req *cr; - char addr[18]; - int err, dd, dev_id; - - ba2str(src, addr); - dev_id = hci_devid(addr); - if (dev_id < 0) - return dev_id; - - dd = hci_open_dev(dev_id); - if (dd < 0) - return dd; - - cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); - if (!cr) - return -ENOMEM; - - bacpy(&cr->bdaddr, dst); - cr->type = ACL_LINK; - err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr); - if (err < 0) { - free(cr); - hci_close_dev(dd); - return err; - } - - err = hci_encrypt_link(dd, htobs(cr->conn_info->handle), 1, 25000); - - free(cr); - hci_close_dev(dd); - - return err; -} - -static int create_device(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout) -{ - struct hidp_connadd_req req; - struct sockaddr_l2 addr; - socklen_t addrlen; - bdaddr_t src, dst; - char bda[18]; - int err; - - memset(&addr, 0, sizeof(addr)); - addrlen = sizeof(addr); - - if (getsockname(csk, (struct sockaddr *) &addr, &addrlen) < 0) - return -1; - - bacpy(&src, &addr.l2_bdaddr); - - memset(&addr, 0, sizeof(addr)); - addrlen = sizeof(addr); - - if (getpeername(csk, (struct sockaddr *) &addr, &addrlen) < 0) - return -1; - - bacpy(&dst, &addr.l2_bdaddr); - - memset(&req, 0, sizeof(req)); - req.ctrl_sock = csk; - req.intr_sock = isk; - req.flags = 0; - req.idle_to = timeout * 60; - - err = get_stored_device_info(&src, &dst, &req); - if (!err) - goto create; - - if (!nocheck) { - ba2str(&dst, bda); - syslog(LOG_ERR, "Rejected connection from unknown device %s", bda); - /* Return no error to avoid run_server() complaining too */ - return 0; - } - - if (!nosdp) { - err = get_sdp_device_info(&src, &dst, &req); - if (err < 0) - goto error; - } else { - struct l2cap_conninfo conn; - socklen_t size; - uint8_t class[3]; - - memset(&conn, 0, sizeof(conn)); - size = sizeof(conn); - if (getsockopt(csk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &size) < 0) - memset(class, 0, 3); - else - memcpy(class, conn.dev_class, 3); - - if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) - req.subclass = class[0]; - else - req.subclass = 0xc0; - } - -create: - if (subclass != 0x00) - req.subclass = subclass; - - ba2str(&dst, bda); - syslog(LOG_INFO, "New HID device %s (%s)", bda, req.name); - - if (encrypt && (req.subclass & 0x40)) { - err = request_authentication(&src, &dst); - if (err < 0) { - syslog(LOG_ERR, "Authentication for %s failed", bda); - goto error; - } - - err = request_encryption(&src, &dst); - if (err < 0) - syslog(LOG_ERR, "Encryption for %s failed", bda); - } - - if (bootonly) { - req.rd_size = 0; - req.flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); - } - - err = ioctl(ctl, HIDPCONNADD, &req); - -error: - free(req.rd_data); - - return err; -} - -static void run_server(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout) -{ - struct pollfd p[2]; - sigset_t sigs; - short events; - int err, ncsk, nisk; - - sigfillset(&sigs); - sigdelset(&sigs, SIGCHLD); - sigdelset(&sigs, SIGPIPE); - sigdelset(&sigs, SIGTERM); - sigdelset(&sigs, SIGINT); - sigdelset(&sigs, SIGHUP); - - p[0].fd = csk; - p[0].events = POLLIN | POLLERR | POLLHUP; - - p[1].fd = isk; - p[1].events = POLLIN | POLLERR | POLLHUP; - - while (!__io_canceled) { - p[0].revents = 0; - p[1].revents = 0; - - if (ppoll(p, 2, NULL, &sigs) < 1) - continue; - - events = p[0].revents | p[1].revents; - - if (events & POLLIN) { - ncsk = l2cap_accept(csk, NULL); - nisk = l2cap_accept(isk, NULL); - - err = create_device(ctl, ncsk, nisk, subclass, nosdp, nocheck, bootonly, encrypt, timeout); - if (err < 0) - syslog(LOG_ERR, "HID create error %d (%s)", - errno, strerror(errno)); - - close(nisk); - sleep(1); - close(ncsk); - } - } -} - -static char *hidp_state[] = { - "unknown", - "connected", - "open", - "bound", - "listening", - "connecting", - "connecting", - "config", - "disconnecting", - "closed" -}; - -static char *hidp_flagstostr(uint32_t flags) -{ - static char str[100]; - str[0] = 0; - - strcat(str, "["); - - if (flags & (1 << HIDP_BOOT_PROTOCOL_MODE)) - strcat(str, "boot-protocol"); - - strcat(str, "]"); - - return str; -} - -static void do_show(int ctl) -{ - struct hidp_connlist_req req; - struct hidp_conninfo ci[16]; - char addr[18]; - unsigned int i; - - req.cnum = 16; - req.ci = ci; - - if (ioctl(ctl, HIDPGETCONNLIST, &req) < 0) { - perror("Can't get connection list"); - close(ctl); - exit(1); - } - - for (i = 0; i < req.cnum; i++) { - ba2str(&ci[i].bdaddr, addr); - printf("%s %s [%04x:%04x] %s %s\n", addr, ci[i].name, - ci[i].vendor, ci[i].product, hidp_state[ci[i].state], - ci[i].flags ? hidp_flagstostr(ci[i].flags) : ""); - } -} - -static void do_connect(int ctl, bdaddr_t *src, bdaddr_t *dst, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout) -{ - struct hidp_connadd_req req; - uint16_t uuid = HID_SVCLASS_ID; - uint8_t channel = 0; - char name[256]; - int csk, isk, err; - - memset(&req, 0, sizeof(req)); - name[0] = '\0'; - - err = get_sdp_device_info(src, dst, &req); - if (err < 0 && fakehid) - err = get_alternate_device_info(src, dst, - &uuid, &channel, name, sizeof(name) - 1); - - if (err < 0) { - perror("Can't get device information"); - close(ctl); - exit(1); - } - - switch (uuid) { - case HID_SVCLASS_ID: - goto connect; - - case SERIAL_PORT_SVCLASS_ID: - if (subclass == 0x40 || !strcmp(name, "Cable Replacement")) { - if (epox_presenter(src, dst, channel) < 0) { - close(ctl); - exit(1); - } - break; - } - if (subclass == 0x1f || !strcmp(name, "SPP slave")) { - if (jthree_keyboard(src, dst, channel) < 0) { - close(ctl); - exit(1); - } - break; - } - if (subclass == 0x02 || !strcmp(name, "Serial Port")) { - if (celluon_keyboard(src, dst, channel) < 0) { - close(ctl); - exit(1); - } - break; - } - break; - - case HEADSET_SVCLASS_ID: - case HANDSFREE_SVCLASS_ID: - if (headset_presenter(src, dst, channel) < 0) { - close(ctl); - exit(1); - } - break; - } - - return; - -connect: - csk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_CTRL); - if (csk < 0) { - perror("Can't create HID control channel"); - close(ctl); - exit(1); - } - - isk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_INTR); - if (isk < 0) { - perror("Can't create HID interrupt channel"); - close(csk); - close(ctl); - exit(1); - } - - err = create_device(ctl, csk, isk, subclass, 1, 1, bootonly, encrypt, timeout); - if (err < 0) { - fprintf(stderr, "HID create error %d (%s)\n", - errno, strerror(errno)); - close(isk); - sleep(1); - close(csk); - close(ctl); - exit(1); - } -} - -static void do_search(int ctl, bdaddr_t *bdaddr, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout) -{ - inquiry_info *info = NULL; - bdaddr_t src, dst; - int i, dev_id, num_rsp, length, flags; - char addr[18]; - uint8_t class[3]; - - ba2str(bdaddr, addr); - dev_id = hci_devid(addr); - if (dev_id < 0) { - dev_id = hci_get_route(NULL); - hci_devba(dev_id, &src); - } else - bacpy(&src, bdaddr); - - length = 8; /* ~10 seconds */ - num_rsp = 0; - flags = IREQ_CACHE_FLUSH; - - printf("Searching ...\n"); - - num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags); - - for (i = 0; i < num_rsp; i++) { - memcpy(class, (info+i)->dev_class, 3); - if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) { - bacpy(&dst, &(info+i)->bdaddr); - ba2str(&dst, addr); - - printf("\tConnecting to device %s\n", addr); - do_connect(ctl, &src, &dst, subclass, fakehid, bootonly, encrypt, timeout); - } - } - - if (!fakehid) - goto done; - - for (i = 0; i < num_rsp; i++) { - memcpy(class, (info+i)->dev_class, 3); - if ((class[0] == 0x00 && class[2] == 0x00 && - (class[1] == 0x40 || class[1] == 0x1f)) || - (class[0] == 0x10 && class[1] == 0x02 && class[2] == 0x40)) { - bacpy(&dst, &(info+i)->bdaddr); - ba2str(&dst, addr); - - printf("\tConnecting to device %s\n", addr); - do_connect(ctl, &src, &dst, subclass, 1, bootonly, 0, timeout); - } - } - -done: - bt_free(info); - - if (!num_rsp) { - fprintf(stderr, "\tNo devices in range or visible\n"); - close(ctl); - exit(1); - } -} - -static void do_kill(int ctl, bdaddr_t *bdaddr, uint32_t flags) -{ - struct hidp_conndel_req req; - struct hidp_connlist_req cl; - struct hidp_conninfo ci[16]; - unsigned int i; - - if (!bacmp(bdaddr, BDADDR_ALL)) { - cl.cnum = 16; - cl.ci = ci; - - if (ioctl(ctl, HIDPGETCONNLIST, &cl) < 0) { - perror("Can't get connection list"); - close(ctl); - exit(1); - } - - for (i = 0; i < cl.cnum; i++) { - bacpy(&req.bdaddr, &ci[i].bdaddr); - req.flags = flags; - - if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { - perror("Can't release connection"); - close(ctl); - exit(1); - } - } - - } else { - bacpy(&req.bdaddr, bdaddr); - req.flags = flags; - - if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { - perror("Can't release connection"); - close(ctl); - exit(1); - } - } -} - -static void usage(void) -{ - printf("hidd - Bluetooth HID daemon version %s\n\n", VERSION); - - printf("Usage:\n" - "\thidd [options] [commands]\n" - "\n"); - - printf("Options:\n" - "\t-i Local HCI device or BD Address\n" - "\t-t Set idle timeout (in minutes)\n" - "\t-b Overwrite the boot mode subclass\n" - "\t-n, --nodaemon Don't fork daemon to background\n" - "\t-h, --help Display help\n" - "\n"); - - printf("Commands:\n" - "\t--server Start HID server\n" - "\t--search Search for HID devices\n" - "\t--connect Connect remote HID device\n" - "\t--unplug Unplug the HID connection\n" - "\t--kill Terminate HID connection\n" - "\t--killall Terminate all connections\n" - "\t--show List current HID connections\n" - "\n"); -} - -static struct option main_options[] = { - { "help", 0, 0, 'h' }, - { "nodaemon", 0, 0, 'n' }, - { "subclass", 1, 0, 'b' }, - { "timeout", 1, 0, 't' }, - { "device", 1, 0, 'i' }, - { "master", 0, 0, 'M' }, - { "encrypt", 0, 0, 'E' }, - { "nosdp", 0, 0, 'D' }, - { "nocheck", 0, 0, 'Z' }, - { "bootonly", 0, 0, 'B' }, - { "hidonly", 0, 0, 'H' }, - { "show", 0, 0, 'l' }, - { "list", 0, 0, 'l' }, - { "server", 0, 0, 'd' }, - { "listen", 0, 0, 'd' }, - { "search", 0, 0, 's' }, - { "create", 1, 0, 'c' }, - { "connect", 1, 0, 'c' }, - { "disconnect", 1, 0, 'k' }, - { "terminate", 1, 0, 'k' }, - { "release", 1, 0, 'k' }, - { "kill", 1, 0, 'k' }, - { "killall", 0, 0, 'K' }, - { "unplug", 1, 0, 'u' }, - { 0, 0, 0, 0 } -}; - -int main(int argc, char *argv[]) -{ - struct sigaction sa; - bdaddr_t bdaddr, dev; - uint32_t flags = 0; - uint8_t subclass = 0x00; - char addr[18]; - int log_option = LOG_NDELAY | LOG_PID; - int opt, ctl, csk, isk; - int mode = SHOW, detach = 1, nosdp = 0, nocheck = 0, bootonly = 0; - int fakehid = 1, encrypt = 0, timeout = 30, lm = 0; - - bacpy(&bdaddr, BDADDR_ANY); - - while ((opt = getopt_long(argc, argv, "+i:nt:b:MEDZBHldsc:k:Ku:h", main_options, NULL)) != -1) { - switch(opt) { - case 'i': - if (!strncasecmp(optarg, "hci", 3)) - hci_devba(atoi(optarg + 3), &bdaddr); - else - str2ba(optarg, &bdaddr); - break; - case 'n': - detach = 0; - break; - case 't': - timeout = atoi(optarg); - break; - case 'b': - if (!strncasecmp(optarg, "0x", 2)) - subclass = (uint8_t) strtol(optarg, NULL, 16); - else - subclass = atoi(optarg); - break; - case 'M': - lm |= L2CAP_LM_MASTER; - break; - case 'E': - encrypt = 1; - break; - case 'D': - nosdp = 1; - break; - case 'Z': - nocheck = 1; - break; - case 'B': - bootonly = 1; - break; - case 'H': - fakehid = 0; - break; - case 'l': - mode = SHOW; - break; - case 'd': - mode = SERVER; - break; - case 's': - mode = SEARCH; - break; - case 'c': - str2ba(optarg, &dev); - mode = CONNECT; - break; - case 'k': - str2ba(optarg, &dev); - mode = KILL; - break; - case 'K': - bacpy(&dev, BDADDR_ALL); - mode = KILL; - break; - case 'u': - str2ba(optarg, &dev); - flags = (1 << HIDP_VIRTUAL_CABLE_UNPLUG); - mode = KILL; - break; - case 'h': - usage(); - exit(0); - default: - exit(0); - } - } - - ba2str(&bdaddr, addr); - - ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); - if (ctl < 0) { - perror("Can't open HIDP control socket"); - exit(1); - } - - switch (mode) { - case SERVER: - csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, lm, 10); - if (csk < 0) { - perror("Can't listen on HID control channel"); - close(ctl); - exit(1); - } - - isk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_INTR, lm, 10); - if (isk < 0) { - perror("Can't listen on HID interrupt channel"); - close(ctl); - close(csk); - exit(1); - } - break; - - case SEARCH: - do_search(ctl, &bdaddr, subclass, fakehid, bootonly, encrypt, timeout); - close(ctl); - exit(0); - - case CONNECT: - do_connect(ctl, &bdaddr, &dev, subclass, fakehid, bootonly, encrypt, timeout); - close(ctl); - exit(0); - - case KILL: - do_kill(ctl, &dev, flags); - close(ctl); - exit(0); - - default: - do_show(ctl); - close(ctl); - exit(0); - } - - if (detach) { - if (daemon(0, 0)) { - perror("Can't start daemon"); - exit(1); - } - } else - log_option |= LOG_PERROR; - - openlog("hidd", log_option, LOG_DAEMON); - - if (bacmp(&bdaddr, BDADDR_ANY)) - syslog(LOG_INFO, "Bluetooth HID daemon (%s)", addr); - else - syslog(LOG_INFO, "Bluetooth HID daemon"); - - memset(&sa, 0, sizeof(sa)); - sa.sa_flags = SA_NOCLDSTOP; - - sa.sa_handler = sig_term; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sa.sa_handler = sig_hup; - sigaction(SIGHUP, &sa, NULL); - - sa.sa_handler = SIG_IGN; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL); - - run_server(ctl, csk, isk, subclass, nosdp, nocheck, bootonly, encrypt, timeout); - - syslog(LOG_INFO, "Exit"); - - close(csk); - close(isk); - close(ctl); - - return 0; -} diff -Nru bluez-4.101/compat/hidd.h bluez-5.23/compat/hidd.h --- bluez-4.101/compat/hidd.h 2010-05-23 12:47:33.000000000 +0000 +++ bluez-5.23/compat/hidd.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2003-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#define L2CAP_PSM_HIDP_CTRL 0x11 -#define L2CAP_PSM_HIDP_INTR 0x13 - -int epox_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel); -int headset_presenter(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel); -int jthree_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel); -int celluon_keyboard(const bdaddr_t *src, const bdaddr_t *dst, uint8_t channel); diff -Nru bluez-4.101/compat/lib.h bluez-5.23/compat/lib.h --- bluez-4.101/compat/lib.h 2010-05-23 12:47:33.000000000 +0000 +++ bluez-5.23/compat/lib.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2002-2003 Maxim Krasnyansky - * Copyright (C) 2002-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include -#include -#include - -#ifndef min -#define min(a,b) ( (a)<(b) ? (a):(b) ) -#endif - -/* IO cancelation */ -extern volatile sig_atomic_t __io_canceled; - -static inline void io_init(void) -{ - __io_canceled = 0; -} - -static inline void io_cancel(void) -{ - __io_canceled = 1; -} - -/* Read exactly len bytes (Signal safe)*/ -static inline int read_n(int fd, char *buf, int len) -{ - register int t = 0, w; - - while (!__io_canceled && len > 0) { - if ((w = read(fd, buf, len)) < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return -1; - } - if (!w) - return 0; - len -= w; - buf += w; - t += w; - } - - return t; -} - -/* Write exactly len bytes (Signal safe)*/ -static inline int write_n(int fd, char *buf, int len) -{ - register int t = 0, w; - - while (!__io_canceled && len > 0) { - if ((w = write(fd, buf, len)) < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - return -1; - } - if (!w) - return 0; - len -= w; - buf += w; - t += w; - } - - return t; -} diff -Nru bluez-4.101/compat/msdun.c bluez-5.23/compat/msdun.c --- bluez-4.101/compat/msdun.c 2010-05-23 12:47:33.000000000 +0000 +++ bluez-5.23/compat/msdun.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,153 +0,0 @@ -/* - * - * BlueZ - Bluetooth protocol stack for Linux - * - * Copyright (C) 2002-2003 Maxim Krasnyansky - * Copyright (C) 2002-2010 Marcel Holtmann - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lib.h" -#include "dund.h" - -#define MS_PPP 2 -#define MS_SUCCESS 1 -#define MS_FAILED -1 -#define MS_TIMEOUT -2 - -static sigjmp_buf jmp; -static int retry; -static int timeout; - -static void sig_alarm(int sig) -{ - siglongjmp(jmp, MS_TIMEOUT); -} - -static int w4_str(int fd, char *str) -{ - char buf[40]; - unsigned len = 0; - int r; - - while (1) { - r = read(fd, buf + len, sizeof(buf) - len - 1); - if (r < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - break; - } - if (!r) - break; - - len += r; - - if (len < strlen(str)) - continue; - buf[len] = 0; - - if (strstr(buf, str)) - return MS_SUCCESS; - - /* Detect PPP */ - if (strchr(buf, '~')) - return MS_PPP; - } - return MS_FAILED; -} - -static int ms_server(int fd) -{ - switch (w4_str(fd, "CLIENT")) { - case MS_SUCCESS: - write_n(fd, "CLIENTSERVER", 12); - case MS_PPP: - return MS_SUCCESS; - default: - return MS_FAILED; - } -} - -static int ms_client(int fd) -{ - write_n(fd, "CLIENT", 6); - return w4_str(fd, "CLIENTSERVER"); -} - -int ms_dun(int fd, int server, int timeo) -{ - sig_t osig; - - retry = 4; - timeout = timeo; - - if (!server) - timeout /= retry; - - osig = signal(SIGALRM, sig_alarm); - - while (1) { - int r = sigsetjmp(jmp, 1); - if (r) { - if (r == MS_TIMEOUT && !server && --retry) - continue; - - alarm(0); - signal(SIGALRM, osig); - - switch (r) { - case MS_SUCCESS: - case MS_PPP: - errno = 0; - return 0; - - case MS_FAILED: - errno = EPROTO; - break; - - case MS_TIMEOUT: - errno = ETIMEDOUT; - break; - } - return -1; - } - - alarm(timeout); - - if (server) - r = ms_server(fd); - else - r = ms_client(fd); - - siglongjmp(jmp, r); - } -} diff -Nru bluez-4.101/compat/pand.1 bluez-5.23/compat/pand.1 --- bluez-4.101/compat/pand.1 2008-10-04 13:08:56.000000000 +0000 +++ bluez-5.23/compat/pand.1 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.29. -.TH BlueZ "1" "February 2003" "PAN daemon" "User Commands" -.SH NAME -pand \- BlueZ Bluetooth PAN daemon -.SH DESCRIPTION -The pand PAN daemon allows your computer to connect to ethernet -networks using Bluetooth. -.SH SYNPOSIS -pand -.SH OPTIONS -.TP -\fB\-\-show\fR \fB\-\-list\fR \fB\-l\fR -Show active PAN connections -.TP -\fB\-\-listen\fR \fB\-s\fR -Listen for PAN connections -.TP -\fB\-\-connect\fR \fB\-c\fR -Create PAN connection -.TP -\fB\-\-search\fR \fB\-Q[duration]\fR -Search and connect -.TP -\fB\-\-kill\fR \fB\-k\fR -Kill PAN connection -.TP -\fB\-\-killall\fR \fB\-K\fR -Kill all PAN connections -.TP -\fB\-\-role\fR \fB\-r\fR -Local PAN role (PANU, NAP, GN) -.TP -\fB\-\-service\fR \fB\-d\fR -Remote PAN service (PANU, NAP, GN) -.TP -\fB\-\-ethernet\fR \fB\-e\fR -Network interface name -.TP -\fB\-\-device\fR \fB\-i\fR -Source bdaddr -.TP -\fB\-\-nosdp\fR \fB\-D\fR -Disable SDP -.TP -\fB\-\-encrypt\fR \fB\-E\fR -Enable encryption -.TP -\fB\-\-secure\fR \fB\-S\fR -Secure connection -.TP -\fB\-\-master\fR \fB\-M\fR -Become the master of a piconet -.TP -\fB\-\-nodetach\fR \fB\-n\fR -Do not become a daemon -.TP -\fB\-\-persist\fR \fB\-p[interval]\fR -Persist mode -.TP -\fB\-\-cache\fR \fB\-C[valid]\fR -Cache addresses -.TP -\fB\-\-pidfile\fR \fB\-P \fR -Create PID file -.TP -\fB\-\-devup\fR \fB\-u