├── COPYING ├── Makefile.am ├── Makefile.in ├── README.md ├── aclocal.m4 ├── build-aux ├── ar-lib ├── ax_cxx_compile_stdcxx.m4 ├── compile ├── config.guess ├── config.rpath ├── config.sub ├── depcomp ├── install-sh └── missing ├── config.h.in ├── configure ├── configure.ac ├── include └── Gobbledegook.h └── src ├── DBusInterface.cpp ├── DBusInterface.h ├── DBusMethod.cpp ├── DBusMethod.h ├── DBusObject.cpp ├── DBusObject.h ├── DBusObjectPath.h ├── GattCharacteristic.cpp ├── GattCharacteristic.h ├── GattDescriptor.cpp ├── GattDescriptor.h ├── GattInterface.cpp ├── GattInterface.h ├── GattProperty.cpp ├── GattProperty.h ├── GattService.cpp ├── GattService.h ├── GattUuid.h ├── Globals.h ├── Gobbledegook.cpp ├── HciAdapter.cpp ├── HciAdapter.h ├── HciSocket.cpp ├── HciSocket.h ├── Init.cpp ├── Init.h ├── Logger.cpp ├── Logger.h ├── Makefile.am ├── Makefile.in ├── Mgmt.cpp ├── Mgmt.h ├── Server.cpp ├── Server.h ├── ServerUtils.cpp ├── ServerUtils.h ├── TickEvent.h ├── Utils.cpp ├── Utils.h └── standalone.cpp /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | EXTRA_DIST = README.md 3 | ACLOCAL_AMFLAGS = -I build-aux -------------------------------------------------------------------------------- /build-aux/ar-lib: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Wrapper for Microsoft lib.exe 3 | 4 | me=ar-lib 5 | scriptversion=2012-03-01.08; # UTC 6 | 7 | # Copyright (C) 2010-2014 Free Software Foundation, Inc. 8 | # Written by Peter Rosin . 9 | # 10 | # This program is free software; you can redistribute it and/or modify 11 | # it under the terms of the GNU General Public License as published by 12 | # the Free Software Foundation; either version 2, or (at your option) 13 | # any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU General Public License 21 | # along with this program. If not, see . 22 | 23 | # As a special exception to the GNU General Public License, if you 24 | # distribute this file as part of a program that contains a 25 | # configuration script generated by Autoconf, you may include it under 26 | # the same distribution terms that you use for the rest of that program. 27 | 28 | # This file is maintained in Automake, please report 29 | # bugs to or send patches to 30 | # . 31 | 32 | 33 | # func_error message 34 | func_error () 35 | { 36 | echo "$me: $1" 1>&2 37 | exit 1 38 | } 39 | 40 | file_conv= 41 | 42 | # func_file_conv build_file 43 | # Convert a $build file to $host form and store it in $file 44 | # Currently only supports Windows hosts. 45 | func_file_conv () 46 | { 47 | file=$1 48 | case $file in 49 | / | /[!/]*) # absolute file, and not a UNC file 50 | if test -z "$file_conv"; then 51 | # lazily determine how to convert abs files 52 | case `uname -s` in 53 | MINGW*) 54 | file_conv=mingw 55 | ;; 56 | CYGWIN*) 57 | file_conv=cygwin 58 | ;; 59 | *) 60 | file_conv=wine 61 | ;; 62 | esac 63 | fi 64 | case $file_conv in 65 | mingw) 66 | file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` 67 | ;; 68 | cygwin) 69 | file=`cygpath -m "$file" || echo "$file"` 70 | ;; 71 | wine) 72 | file=`winepath -w "$file" || echo "$file"` 73 | ;; 74 | esac 75 | ;; 76 | esac 77 | } 78 | 79 | # func_at_file at_file operation archive 80 | # Iterate over all members in AT_FILE performing OPERATION on ARCHIVE 81 | # for each of them. 82 | # When interpreting the content of the @FILE, do NOT use func_file_conv, 83 | # since the user would need to supply preconverted file names to 84 | # binutils ar, at least for MinGW. 85 | func_at_file () 86 | { 87 | operation=$2 88 | archive=$3 89 | at_file_contents=`cat "$1"` 90 | eval set x "$at_file_contents" 91 | shift 92 | 93 | for member 94 | do 95 | $AR -NOLOGO $operation:"$member" "$archive" || exit $? 96 | done 97 | } 98 | 99 | case $1 in 100 | '') 101 | func_error "no command. Try '$0 --help' for more information." 102 | ;; 103 | -h | --h*) 104 | cat <. 8 | # 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | # This file is maintained in Automake, please report 28 | # bugs to or send patches to 29 | # . 30 | 31 | nl=' 32 | ' 33 | 34 | # We need space, tab and new line, in precisely that order. Quoting is 35 | # there to prevent tools from complaining about whitespace usage. 36 | IFS=" "" $nl" 37 | 38 | file_conv= 39 | 40 | # func_file_conv build_file lazy 41 | # Convert a $build file to $host form and store it in $file 42 | # Currently only supports Windows hosts. If the determined conversion 43 | # type is listed in (the comma separated) LAZY, no conversion will 44 | # take place. 45 | func_file_conv () 46 | { 47 | file=$1 48 | case $file in 49 | / | /[!/]*) # absolute file, and not a UNC file 50 | if test -z "$file_conv"; then 51 | # lazily determine how to convert abs files 52 | case `uname -s` in 53 | MINGW*) 54 | file_conv=mingw 55 | ;; 56 | CYGWIN*) 57 | file_conv=cygwin 58 | ;; 59 | *) 60 | file_conv=wine 61 | ;; 62 | esac 63 | fi 64 | case $file_conv/,$2, in 65 | *,$file_conv,*) 66 | ;; 67 | mingw/*) 68 | file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` 69 | ;; 70 | cygwin/*) 71 | file=`cygpath -m "$file" || echo "$file"` 72 | ;; 73 | wine/*) 74 | file=`winepath -w "$file" || echo "$file"` 75 | ;; 76 | esac 77 | ;; 78 | esac 79 | } 80 | 81 | # func_cl_dashL linkdir 82 | # Make cl look for libraries in LINKDIR 83 | func_cl_dashL () 84 | { 85 | func_file_conv "$1" 86 | if test -z "$lib_path"; then 87 | lib_path=$file 88 | else 89 | lib_path="$lib_path;$file" 90 | fi 91 | linker_opts="$linker_opts -LIBPATH:$file" 92 | } 93 | 94 | # func_cl_dashl library 95 | # Do a library search-path lookup for cl 96 | func_cl_dashl () 97 | { 98 | lib=$1 99 | found=no 100 | save_IFS=$IFS 101 | IFS=';' 102 | for dir in $lib_path $LIB 103 | do 104 | IFS=$save_IFS 105 | if $shared && test -f "$dir/$lib.dll.lib"; then 106 | found=yes 107 | lib=$dir/$lib.dll.lib 108 | break 109 | fi 110 | if test -f "$dir/$lib.lib"; then 111 | found=yes 112 | lib=$dir/$lib.lib 113 | break 114 | fi 115 | if test -f "$dir/lib$lib.a"; then 116 | found=yes 117 | lib=$dir/lib$lib.a 118 | break 119 | fi 120 | done 121 | IFS=$save_IFS 122 | 123 | if test "$found" != yes; then 124 | lib=$lib.lib 125 | fi 126 | } 127 | 128 | # func_cl_wrapper cl arg... 129 | # Adjust compile command to suit cl 130 | func_cl_wrapper () 131 | { 132 | # Assume a capable shell 133 | lib_path= 134 | shared=: 135 | linker_opts= 136 | for arg 137 | do 138 | if test -n "$eat"; then 139 | eat= 140 | else 141 | case $1 in 142 | -o) 143 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 144 | eat=1 145 | case $2 in 146 | *.o | *.[oO][bB][jJ]) 147 | func_file_conv "$2" 148 | set x "$@" -Fo"$file" 149 | shift 150 | ;; 151 | *) 152 | func_file_conv "$2" 153 | set x "$@" -Fe"$file" 154 | shift 155 | ;; 156 | esac 157 | ;; 158 | -I) 159 | eat=1 160 | func_file_conv "$2" mingw 161 | set x "$@" -I"$file" 162 | shift 163 | ;; 164 | -I*) 165 | func_file_conv "${1#-I}" mingw 166 | set x "$@" -I"$file" 167 | shift 168 | ;; 169 | -l) 170 | eat=1 171 | func_cl_dashl "$2" 172 | set x "$@" "$lib" 173 | shift 174 | ;; 175 | -l*) 176 | func_cl_dashl "${1#-l}" 177 | set x "$@" "$lib" 178 | shift 179 | ;; 180 | -L) 181 | eat=1 182 | func_cl_dashL "$2" 183 | ;; 184 | -L*) 185 | func_cl_dashL "${1#-L}" 186 | ;; 187 | -static) 188 | shared=false 189 | ;; 190 | -Wl,*) 191 | arg=${1#-Wl,} 192 | save_ifs="$IFS"; IFS=',' 193 | for flag in $arg; do 194 | IFS="$save_ifs" 195 | linker_opts="$linker_opts $flag" 196 | done 197 | IFS="$save_ifs" 198 | ;; 199 | -Xlinker) 200 | eat=1 201 | linker_opts="$linker_opts $2" 202 | ;; 203 | -*) 204 | set x "$@" "$1" 205 | shift 206 | ;; 207 | *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) 208 | func_file_conv "$1" 209 | set x "$@" -Tp"$file" 210 | shift 211 | ;; 212 | *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) 213 | func_file_conv "$1" mingw 214 | set x "$@" "$file" 215 | shift 216 | ;; 217 | *) 218 | set x "$@" "$1" 219 | shift 220 | ;; 221 | esac 222 | fi 223 | shift 224 | done 225 | if test -n "$linker_opts"; then 226 | linker_opts="-link$linker_opts" 227 | fi 228 | exec "$@" $linker_opts 229 | exit 1 230 | } 231 | 232 | eat= 233 | 234 | case $1 in 235 | '') 236 | echo "$0: No command. Try '$0 --help' for more information." 1>&2 237 | exit 1; 238 | ;; 239 | -h | --h*) 240 | cat <<\EOF 241 | Usage: compile [--help] [--version] PROGRAM [ARGS] 242 | 243 | Wrapper for compilers which do not understand '-c -o'. 244 | Remove '-o dest.o' from ARGS, run PROGRAM with the remaining 245 | arguments, and rename the output as expected. 246 | 247 | If you are trying to build a whole package this is not the 248 | right script to run: please start by reading the file 'INSTALL'. 249 | 250 | Report bugs to . 251 | EOF 252 | exit $? 253 | ;; 254 | -v | --v*) 255 | echo "compile $scriptversion" 256 | exit $? 257 | ;; 258 | cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) 259 | func_cl_wrapper "$@" # Doesn't return... 260 | ;; 261 | esac 262 | 263 | ofile= 264 | cfile= 265 | 266 | for arg 267 | do 268 | if test -n "$eat"; then 269 | eat= 270 | else 271 | case $1 in 272 | -o) 273 | # configure might choose to run compile as 'compile cc -o foo foo.c'. 274 | # So we strip '-o arg' only if arg is an object. 275 | eat=1 276 | case $2 in 277 | *.o | *.obj) 278 | ofile=$2 279 | ;; 280 | *) 281 | set x "$@" -o "$2" 282 | shift 283 | ;; 284 | esac 285 | ;; 286 | *.c) 287 | cfile=$1 288 | set x "$@" "$1" 289 | shift 290 | ;; 291 | *) 292 | set x "$@" "$1" 293 | shift 294 | ;; 295 | esac 296 | fi 297 | shift 298 | done 299 | 300 | if test -z "$ofile" || test -z "$cfile"; then 301 | # If no '-o' option was seen then we might have been invoked from a 302 | # pattern rule where we don't need one. That is ok -- this is a 303 | # normal compilation that the losing compiler can handle. If no 304 | # '.c' file was seen then we are probably linking. That is also 305 | # ok. 306 | exec "$@" 307 | fi 308 | 309 | # Name of file we expect compiler to create. 310 | cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` 311 | 312 | # Create the lock directory. 313 | # Note: use '[/\\:.-]' here to ensure that we don't use the same name 314 | # that we are using for the .o file. Also, base the name on the expected 315 | # object file name, since that is what matters with a parallel build. 316 | lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d 317 | while true; do 318 | if mkdir "$lockdir" >/dev/null 2>&1; then 319 | break 320 | fi 321 | sleep 1 322 | done 323 | # FIXME: race condition here if user kills between mkdir and trap. 324 | trap "rmdir '$lockdir'; exit 1" 1 2 15 325 | 326 | # Run the compile. 327 | "$@" 328 | ret=$? 329 | 330 | if test -f "$cofile"; then 331 | test "$cofile" = "$ofile" || mv "$cofile" "$ofile" 332 | elif test -f "${cofile}bj"; then 333 | test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" 334 | fi 335 | 336 | rmdir "$lockdir" 337 | exit $ret 338 | 339 | # Local Variables: 340 | # mode: shell-script 341 | # sh-indentation: 2 342 | # eval: (add-hook 'write-file-hooks 'time-stamp) 343 | # time-stamp-start: "scriptversion=" 344 | # time-stamp-format: "%:y-%02m-%02d.%02H" 345 | # time-stamp-time-zone: "UTC" 346 | # time-stamp-end: "; # UTC" 347 | # End: 348 | -------------------------------------------------------------------------------- /build-aux/config.rpath: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moovel/gatt-server/5ec4136c00b2a5acd7243316b50a0ebf34c56c56/build-aux/config.rpath -------------------------------------------------------------------------------- /build-aux/missing: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Common wrapper for a few potentially missing GNU programs. 3 | 4 | scriptversion=2013-10-28.13; # UTC 5 | 6 | # Copyright (C) 1996-2014 Free Software Foundation, Inc. 7 | # Originally written by Fran,cois Pinard , 1996. 8 | 9 | # This program is free software; you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation; either version 2, or (at your option) 12 | # any later version. 13 | 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | 22 | # As a special exception to the GNU General Public License, if you 23 | # distribute this file as part of a program that contains a 24 | # configuration script generated by Autoconf, you may include it under 25 | # the same distribution terms that you use for the rest of that program. 26 | 27 | if test $# -eq 0; then 28 | echo 1>&2 "Try '$0 --help' for more information" 29 | exit 1 30 | fi 31 | 32 | case $1 in 33 | 34 | --is-lightweight) 35 | # Used by our autoconf macros to check whether the available missing 36 | # script is modern enough. 37 | exit 0 38 | ;; 39 | 40 | --run) 41 | # Back-compat with the calling convention used by older automake. 42 | shift 43 | ;; 44 | 45 | -h|--h|--he|--hel|--help) 46 | echo "\ 47 | $0 [OPTION]... PROGRAM [ARGUMENT]... 48 | 49 | Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due 50 | to PROGRAM being missing or too old. 51 | 52 | Options: 53 | -h, --help display this help and exit 54 | -v, --version output version information and exit 55 | 56 | Supported PROGRAM values: 57 | aclocal autoconf autoheader autom4te automake makeinfo 58 | bison yacc flex lex help2man 59 | 60 | Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 61 | 'g' are ignored when checking the name. 62 | 63 | Send bug reports to ." 64 | exit $? 65 | ;; 66 | 67 | -v|--v|--ve|--ver|--vers|--versi|--versio|--version) 68 | echo "missing $scriptversion (GNU Automake)" 69 | exit $? 70 | ;; 71 | 72 | -*) 73 | echo 1>&2 "$0: unknown '$1' option" 74 | echo 1>&2 "Try '$0 --help' for more information" 75 | exit 1 76 | ;; 77 | 78 | esac 79 | 80 | # Run the given program, remember its exit status. 81 | "$@"; st=$? 82 | 83 | # If it succeeded, we are done. 84 | test $st -eq 0 && exit 0 85 | 86 | # Also exit now if we it failed (or wasn't found), and '--version' was 87 | # passed; such an option is passed most likely to detect whether the 88 | # program is present and works. 89 | case $2 in --version|--help) exit $st;; esac 90 | 91 | # Exit code 63 means version mismatch. This often happens when the user 92 | # tries to use an ancient version of a tool on a file that requires a 93 | # minimum version. 94 | if test $st -eq 63; then 95 | msg="probably too old" 96 | elif test $st -eq 127; then 97 | # Program was missing. 98 | msg="missing on your system" 99 | else 100 | # Program was found and executed, but failed. Give up. 101 | exit $st 102 | fi 103 | 104 | perl_URL=http://www.perl.org/ 105 | flex_URL=http://flex.sourceforge.net/ 106 | gnu_software_URL=http://www.gnu.org/software 107 | 108 | program_details () 109 | { 110 | case $1 in 111 | aclocal|automake) 112 | echo "The '$1' program is part of the GNU Automake package:" 113 | echo "<$gnu_software_URL/automake>" 114 | echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" 115 | echo "<$gnu_software_URL/autoconf>" 116 | echo "<$gnu_software_URL/m4/>" 117 | echo "<$perl_URL>" 118 | ;; 119 | autoconf|autom4te|autoheader) 120 | echo "The '$1' program is part of the GNU Autoconf package:" 121 | echo "<$gnu_software_URL/autoconf/>" 122 | echo "It also requires GNU m4 and Perl in order to run:" 123 | echo "<$gnu_software_URL/m4/>" 124 | echo "<$perl_URL>" 125 | ;; 126 | esac 127 | } 128 | 129 | give_advice () 130 | { 131 | # Normalize program name to check for. 132 | normalized_program=`echo "$1" | sed ' 133 | s/^gnu-//; t 134 | s/^gnu//; t 135 | s/^g//; t'` 136 | 137 | printf '%s\n' "'$1' is $msg." 138 | 139 | configure_deps="'configure.ac' or m4 files included by 'configure.ac'" 140 | case $normalized_program in 141 | autoconf*) 142 | echo "You should only need it if you modified 'configure.ac'," 143 | echo "or m4 files included by it." 144 | program_details 'autoconf' 145 | ;; 146 | autoheader*) 147 | echo "You should only need it if you modified 'acconfig.h' or" 148 | echo "$configure_deps." 149 | program_details 'autoheader' 150 | ;; 151 | automake*) 152 | echo "You should only need it if you modified 'Makefile.am' or" 153 | echo "$configure_deps." 154 | program_details 'automake' 155 | ;; 156 | aclocal*) 157 | echo "You should only need it if you modified 'acinclude.m4' or" 158 | echo "$configure_deps." 159 | program_details 'aclocal' 160 | ;; 161 | autom4te*) 162 | echo "You might have modified some maintainer files that require" 163 | echo "the 'autom4te' program to be rebuilt." 164 | program_details 'autom4te' 165 | ;; 166 | bison*|yacc*) 167 | echo "You should only need it if you modified a '.y' file." 168 | echo "You may want to install the GNU Bison package:" 169 | echo "<$gnu_software_URL/bison/>" 170 | ;; 171 | lex*|flex*) 172 | echo "You should only need it if you modified a '.l' file." 173 | echo "You may want to install the Fast Lexical Analyzer package:" 174 | echo "<$flex_URL>" 175 | ;; 176 | help2man*) 177 | echo "You should only need it if you modified a dependency" \ 178 | "of a man page." 179 | echo "You may want to install the GNU Help2man package:" 180 | echo "<$gnu_software_URL/help2man/>" 181 | ;; 182 | makeinfo*) 183 | echo "You should only need it if you modified a '.texi' file, or" 184 | echo "any other file indirectly affecting the aspect of the manual." 185 | echo "You might want to install the Texinfo package:" 186 | echo "<$gnu_software_URL/texinfo/>" 187 | echo "The spurious makeinfo call might also be the consequence of" 188 | echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" 189 | echo "want to install GNU make:" 190 | echo "<$gnu_software_URL/make/>" 191 | ;; 192 | *) 193 | echo "You might have modified some files without having the proper" 194 | echo "tools for further handling them. Check the 'README' file, it" 195 | echo "often tells you about the needed prerequisites for installing" 196 | echo "this package. You may also peek at any GNU archive site, in" 197 | echo "case some other package contains this missing '$1' program." 198 | ;; 199 | esac 200 | } 201 | 202 | give_advice "$1" | sed -e '1s/^/WARNING: /' \ 203 | -e '2,$s/^/ /' >&2 204 | 205 | # Propagate the correct exit status (expected to be 127 for a program 206 | # not found, 63 for a program that failed due to version mismatch). 207 | exit $st 208 | 209 | # Local variables: 210 | # eval: (add-hook 'write-file-hooks 'time-stamp) 211 | # time-stamp-start: "scriptversion=" 212 | # time-stamp-format: "%:y-%02m-%02d.%02H" 213 | # time-stamp-time-zone: "UTC" 214 | # time-stamp-end: "; # UTC" 215 | # End: 216 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | /* config.h.in. Generated from configure.ac by autoheader. */ 2 | 3 | /* define if the compiler supports basic C++11 syntax */ 4 | #undef HAVE_CXX11 5 | 6 | /* Define to 1 if you have the `gio-2.0' library (-lgio-2.0). */ 7 | #undef HAVE_LIBGIO_2_0 8 | 9 | /* Define to 1 if you have the `glib-2.0' library (-lglib-2.0). */ 10 | #undef HAVE_LIBGLIB_2_0 11 | 12 | /* Define to 1 if you have the `gobject-2.0' library (-lgobject-2.0). */ 13 | #undef HAVE_LIBGOBJECT_2_0 14 | 15 | /* Define to 1 if you have the `pthread' library (-lpthread). */ 16 | #undef HAVE_LIBPTHREAD 17 | 18 | /* Name of package */ 19 | #undef PACKAGE 20 | 21 | /* Define to the address where bug reports for this package should be sent. */ 22 | #undef PACKAGE_BUGREPORT 23 | 24 | /* Define to the full name of this package. */ 25 | #undef PACKAGE_NAME 26 | 27 | /* Define to the full name and version of this package. */ 28 | #undef PACKAGE_STRING 29 | 30 | /* Define to the one symbol short name of this package. */ 31 | #undef PACKAGE_TARNAME 32 | 33 | /* Define to the home page for this package. */ 34 | #undef PACKAGE_URL 35 | 36 | /* Define to the version of this package. */ 37 | #undef PACKAGE_VERSION 38 | 39 | /* Version number of package */ 40 | #undef VERSION 41 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([ggk], [1.0]) 2 | AC_CONFIG_AUX_DIR([build-aux]) 3 | AC_CONFIG_MACRO_DIR([build-aux]) 4 | AM_INIT_AUTOMAKE([-Wall -Werror foreign]) 5 | AC_CHECK_LIB([pthread], pthread_create, [], [AC_MSG_ERROR([pthread library not found])]) 6 | AC_CHECK_LIB([glib-2.0], g_variant_new, [], [AC_MSG_ERROR([glib-2.0 library not found])]) 7 | AC_CHECK_LIB([gio-2.0], g_dbus_method_invocation_return_dbus_error, [], [AC_MSG_ERROR([gio-2.0 library not found])]) 8 | AC_CHECK_LIB([gobject-2.0], g_object_unref, [], [AC_MSG_ERROR([gobject-2.0 library not found])]) 9 | AX_CXX_COMPILE_STDCXX(11) 10 | AC_PROG_RANLIB 11 | AC_PROG_CXX 12 | AM_PROG_AR 13 | 14 | AC_SUBST(GLIB_CFLAGS) 15 | AC_SUBST(GIO_CFLAGS) 16 | AC_SUBST(GOBJECT_CFLAGS) 17 | 18 | if pkg-config --atleast-version=2.00 glib-2.0; then 19 | GLIB_CFLAGS=`pkg-config --cflags glib-2.0` 20 | else 21 | AC_MSG_ERROR(glib-2.0 not found) 22 | fi 23 | 24 | if pkg-config --atleast-version=2.00 gio-2.0; then 25 | GIO_CFLAGS=`pkg-config --cflags gio-2.0` 26 | else 27 | AC_MSG_ERROR(gio-2.0 not found) 28 | fi 29 | 30 | if pkg-config --atleast-version=2.00 gobject-2.0; then 31 | GOBJECT_CFLAGS=`pkg-config --cflags gobject-2.0` 32 | else 33 | AC_MSG_ERROR(gobject-2.0 not found) 34 | fi 35 | 36 | AC_CONFIG_HEADERS([config.h]) 37 | AC_CONFIG_FILES([ 38 | Makefile 39 | src/Makefile 40 | ]) 41 | AC_OUTPUT 42 | -------------------------------------------------------------------------------- /src/DBusInterface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is an abstraction layer for a D-Bus interface, the base class for all interfaces. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // Not sure what a D-Bus Interface is? Well, chedk the Readme for resources, but here's the TL;DR: 31 | // 32 | // A D-Bus interface is a contract (similar to programming language interfaces.) An interface defines a set of methods and 33 | // properties for others to use. 34 | // 35 | // Interfaces are identified by their name, such as "org.freedesktop.DBus.Properties". In fact, if an object on the bus is found 36 | // to have that interface, then you know that it provides an interface to access its properties via the methods "Get", "GetAll" and 37 | // "Set". To see the details for this interface (and all of the D-Bus defined interfaces), see: 38 | // 39 | // https://dbus.freedesktop.org/doc/dbus-specification.html 40 | // 41 | // We're also interested in working with BlueZ which has their own set of interfaces. One example is "org.bluez.GattManager1" which 42 | // is the interface used to create and register GATT services with BlueZ. 43 | // 44 | // Remember, interfaces are not implementations; they're just contracts to provide an implementation. That means some interfaces 45 | // are intended for us to implement. One example is "org.bluez.GattService1" which defines the interface that we must conform to 46 | // so that others (likely BlueZ) can access our GATT service(s). For more information on these, have a look at: 47 | // 48 | // https://git.kernel.org/pub/scm/bluetooth/bluez.git/plain/doc/gatt-api.txt 49 | // 50 | // Our interfaces also store a collection of events. Here, an event is much like a timer in modern UIs, which repeatedly fires 51 | // after a defined time. A practical example of an event would be a BLE server that provides a Battery service. By adding a timer 52 | // to the interface for this service, the server could wake up every minute to check the battery level and if it has changed, send 53 | // a notifications to clients over BLE with the new battery level. This saves a lot of additional code on the server's part. 54 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 55 | 56 | #include "DBusInterface.h" 57 | #include "GattProperty.h" 58 | #include "DBusObject.h" 59 | #include "Logger.h" 60 | 61 | namespace ggk { 62 | 63 | // 64 | // Construction 65 | // 66 | 67 | DBusInterface::DBusInterface(DBusObject &owner, const std::string &name) 68 | : owner(owner), name(name) 69 | { 70 | } 71 | 72 | DBusInterface::~DBusInterface() 73 | { 74 | } 75 | 76 | // 77 | // Interface name 78 | // 79 | 80 | // Returns the name of this interface (ex: "org.freedesktop.DBus.Properties") 81 | const std::string &DBusInterface::getName() const 82 | { 83 | return name; 84 | } 85 | 86 | // Sets the name of the interface (ex: "org.freedesktop.DBus.Properties") 87 | DBusInterface &DBusInterface::setName(const std::string &name) 88 | { 89 | this->name = name; 90 | return *this; 91 | } 92 | 93 | // 94 | // Owner information 95 | // 96 | 97 | // Returns the owner (DBusObject) of this interface 98 | DBusObject &DBusInterface::getOwner() const 99 | { 100 | return owner; 101 | } 102 | 103 | // Returns the path node of this interface's owner 104 | DBusObjectPath DBusInterface::getPathNode() const 105 | { 106 | return owner.getPathNode(); 107 | } 108 | 109 | // Returns the full path of this interface's owner 110 | DBusObjectPath DBusInterface::getPath() const 111 | { 112 | return owner.getPath(); 113 | } 114 | 115 | // 116 | // D-Bus interface methods 117 | // 118 | 119 | // Add a named method to this interface 120 | // 121 | // This method returns a reference to `this` in order to enable chaining inside the server description. 122 | DBusInterface &DBusInterface::addMethod(const std::string &name, const char *pInArgs[], const char *pOutArgs, DBusMethod::Callback callback) 123 | { 124 | methods.push_back(DBusMethod(this, name, pInArgs, pOutArgs, callback)); 125 | return *this; 126 | } 127 | 128 | // Calls a named method on this interface 129 | // 130 | // This method returns false if the method could not be found, otherwise it returns true. Note that the return value is not related 131 | // to the result of the method call itself (methods do not return values.) 132 | // 133 | // NOTE: Subclasses are encouraged to override this method in order to support different callback types that are specific to 134 | // their subclass type. 135 | bool DBusInterface::callMethod(const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const 136 | { 137 | for (const DBusMethod &method : methods) 138 | { 139 | if (methodName == method.getName()) 140 | { 141 | method.call(pConnection, getPath(), getName(), methodName, pParameters, pInvocation, pUserData); 142 | return true; 143 | } 144 | } 145 | 146 | return false; 147 | } 148 | 149 | // Add an event to this interface 150 | // 151 | // For details on events, see TickEvent.cpp. 152 | // 153 | // This method returns a reference to `this` in order to enable chaining inside the server description. 154 | // 155 | // NOTE: Subclasses are encouraged to overload this method in order to support different callback types that are specific to 156 | // their subclass type. In addition, they should return their own type. This simplifies the server description by allowing 157 | // calls to chain. 158 | DBusInterface &DBusInterface::onEvent(int tickFrequency, void *pUserData, TickEvent::Callback callback) 159 | { 160 | events.push_back(TickEvent(this, tickFrequency, callback, pUserData)); 161 | return *this; 162 | } 163 | 164 | // Ticks each event within this interface 165 | // 166 | // For details on events, see TickEvent.cpp. 167 | // 168 | // NOTE: Subclasses are encouraged to override this method in order to support different callback types that are specific to 169 | // their subclass type. 170 | void DBusInterface::tickEvents(GDBusConnection *pConnection, void *pUserData) const 171 | { 172 | for (const TickEvent &event : events) 173 | { 174 | event.tick(getPath(), pConnection, pUserData); 175 | } 176 | } 177 | 178 | // Internal method used to generate introspection XML used to describe our services on D-Bus 179 | std::string DBusInterface::generateIntrospectionXML(int depth) const 180 | { 181 | std::string prefix; 182 | prefix.insert(0, depth * 2, ' '); 183 | 184 | std::string xml = std::string(); 185 | 186 | if (methods.empty()) 187 | { 188 | xml += prefix + "\n"; 189 | } 190 | else 191 | { 192 | xml += prefix + "\n"; 193 | 194 | // Describe our methods 195 | for (const DBusMethod &method : methods) 196 | { 197 | xml += method.generateIntrospectionXML(depth + 1); 198 | } 199 | 200 | xml += prefix + "\n"; 201 | } 202 | 203 | return xml; 204 | } 205 | 206 | }; // namespace ggk -------------------------------------------------------------------------------- /src/DBusInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is an abstraction layer for a D-Bus interface, the base class for all interfaces. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussion in DBusInterface.cpp. 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "TickEvent.h" 40 | #include "DBusMethod.h" 41 | 42 | namespace ggk { 43 | 44 | // --------------------------------------------------------------------------------------------------------------------------------- 45 | // Forward declarations 46 | // --------------------------------------------------------------------------------------------------------------------------------- 47 | 48 | struct DBusInterface; 49 | struct GattProperty; 50 | struct DBusObject; 51 | struct DBusObjectPath; 52 | 53 | // --------------------------------------------------------------------------------------------------------------------------------- 54 | // Useful Lambdas 55 | // --------------------------------------------------------------------------------------------------------------------------------- 56 | 57 | #define INTERFACE_METHOD_CALLBACK_LAMBDA [] \ 58 | ( \ 59 | const DBusInterface &self, \ 60 | GDBusConnection *pConnection, \ 61 | const std::string &methodName, \ 62 | GVariant *pParameters, \ 63 | GDBusMethodInvocation *pInvocation, \ 64 | void *pUserData \ 65 | ) 66 | 67 | #define TRY_GET_INTERFACE_OF_TYPE(pInterface, type) \ 68 | (pInterface->getInterfaceType() == type::kInterfaceType ? \ 69 | std::static_pointer_cast(pInterface) : \ 70 | nullptr) 71 | 72 | #define TRY_GET_CONST_INTERFACE_OF_TYPE(pInterface, type) \ 73 | (pInterface->getInterfaceType() == type::kInterfaceType ? \ 74 | std::static_pointer_cast(pInterface) : \ 75 | nullptr) 76 | 77 | // --------------------------------------------------------------------------------------------------------------------------------- 78 | // Representation of a D-Bus interface 79 | // --------------------------------------------------------------------------------------------------------------------------------- 80 | 81 | struct DBusInterface 82 | { 83 | // Our interface type 84 | static constexpr const char *kInterfaceType = "DBusInterface"; 85 | 86 | typedef void (*MethodCallback)(const DBusInterface &self, GDBusConnection *pConnection, const std::string &methodName, GVariant *pParameters, GDBusMethodInvocation *pInvocation, void *pUserData); 87 | typedef void (*EventCallback)(const DBusInterface &self, const TickEvent &event, GDBusConnection *pConnection, void *pUserData); 88 | 89 | // Standard constructor 90 | DBusInterface(DBusObject &owner, const std::string &name); 91 | virtual ~DBusInterface(); 92 | 93 | // Returns a string identifying the type of interface 94 | virtual const std::string getInterfaceType() const { return DBusInterface::kInterfaceType; } 95 | 96 | // 97 | // Interface name (ex: "org.freedesktop.DBus.Properties") 98 | // 99 | 100 | const std::string &getName() const; 101 | DBusInterface &setName(const std::string &name); 102 | 103 | // 104 | // Owner information 105 | // 106 | 107 | DBusObject &getOwner() const; 108 | DBusObjectPath getPathNode() const; 109 | DBusObjectPath getPath() const; 110 | 111 | // 112 | // D-Bus interface methods 113 | // 114 | 115 | DBusInterface &addMethod(const std::string &name, const char *pInArgs[], const char *pOutArgs, DBusMethod::Callback callback); 116 | 117 | // NOTE: Subclasses are encouraged to override this method in order to support different callback types that are specific to 118 | // their subclass type. 119 | virtual bool callMethod(const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const; 120 | 121 | // 122 | // Interface events (our home-grown poor-mans's method of allowing interfaces to do things periodically) 123 | // 124 | 125 | // NOTE: Subclasses are encouraged to overload this method in order to support different callback types that are specific to 126 | // their subclass type. In addition, they should return their own type. This simplifies the server description by allowing 127 | // calls to chain. 128 | DBusInterface &onEvent(int tickFrequency, void *pUserData, TickEvent::Callback callback); 129 | 130 | // NOTE: Subclasses are encouraged to override this method in order to support different callback types that are specific to 131 | // their subclass type. 132 | virtual void tickEvents(GDBusConnection *pConnection, void *pUserData) const; 133 | 134 | // Internal method used to generate introspection XML used to describe our services on D-Bus 135 | virtual std::string generateIntrospectionXML(int depth) const; 136 | 137 | protected: 138 | DBusObject &owner; 139 | std::string name; 140 | std::list methods; 141 | std::list events; 142 | }; 143 | 144 | }; // namespace ggk -------------------------------------------------------------------------------- /src/DBusMethod.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is a representation of a D-Bus interface method. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // Methods are identified by their name (such as "ReadValue" or "WriteValue"). They have argument definitions (defined as part of 31 | // their interface) that describe the type of arguments passed into the method and returned from the method. 32 | // 33 | // In addition to the method itself, we also store a callback delegate that is responsible for performing the tasks for this method. 34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #include "DBusMethod.h" 41 | 42 | namespace ggk { 43 | 44 | // Instantiate a named method on a given interface (pOwner) with a given set of arguments and a callback delegate 45 | DBusMethod::DBusMethod(const DBusInterface *pOwner, const std::string &name, const char *pInArgs[], const char *pOutArgs, Callback callback) 46 | : pOwner(pOwner), name(name), callback(callback) 47 | { 48 | const char **ppInArg = pInArgs; 49 | while(*ppInArg) 50 | { 51 | this->inArgs.push_back(std::string(*ppInArg)); 52 | ppInArg++; 53 | } 54 | 55 | if (nullptr != pOutArgs) 56 | { 57 | this->outArgs = pOutArgs; 58 | } 59 | } 60 | 61 | // Internal method used to generate introspection XML used to describe our services on D-Bus 62 | std::string DBusMethod::generateIntrospectionXML(int depth) const 63 | { 64 | std::string prefix; 65 | prefix.insert(0, depth * 2, ' '); 66 | 67 | std::string xml = std::string(); 68 | 69 | xml += prefix + "\n"; 70 | 71 | // Add our input arguments 72 | for (const std::string &inArg : getInArgs()) 73 | { 74 | xml += prefix + " \n"; 75 | xml += prefix + " \n"; 76 | xml += prefix + " \n"; 77 | } 78 | 79 | const std::string &outArgs = getOutArgs(); 80 | if (!outArgs.empty()) 81 | { 82 | xml += prefix + " \n"; 83 | xml += prefix + " \n"; 84 | xml += prefix + " \n"; 85 | } 86 | 87 | xml += prefix + "\n"; 88 | 89 | return xml; 90 | } 91 | 92 | }; // namespace ggk -------------------------------------------------------------------------------- /src/DBusMethod.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is a representation of a D-Bus interface method. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussion at the top of DBusMethod.cpp 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | // This file contains a representation of a D-Bus interface method. 33 | // 34 | // Methods are identified by their name (such as "Get" or "Set"). They have argument definitions (defined as part of their 35 | // interface) that describe the type of arguments passed into the method and returned from the method. 36 | // 37 | // In addition to the method itself, we also store a callback that can be called whenever the method is invoked. 38 | 39 | #pragma once 40 | 41 | #include 42 | #include 43 | #include 44 | 45 | #include "Globals.h" 46 | #include "DBusObjectPath.h" 47 | #include "Logger.h" 48 | #include "Server.h" 49 | 50 | namespace ggk { 51 | 52 | struct DBusInterface; 53 | 54 | struct DBusMethod 55 | { 56 | // A method callback delegate 57 | typedef void (*Callback)(const DBusInterface &self, GDBusConnection *pConnection, const std::string &methodName, GVariant *pParameters, GDBusMethodInvocation *pInvocation, void *pUserData); 58 | 59 | // Instantiate a named method on a given interface (pOwner) with a given set of arguments and a callback delegate 60 | DBusMethod(const DBusInterface *pOwner, const std::string &name, const char *pInArgs[], const char *pOutArgs, Callback callback); 61 | 62 | // 63 | // Accessors 64 | // 65 | 66 | // Returns the name of the method 67 | const std::string &getName() const { return name; } 68 | 69 | // Sets the name of the method 70 | // 71 | // This method should generally not be called directly. Rather, the name should be set by the constructor 72 | DBusMethod &setName(const std::string &name) { this->name = name; return *this; } 73 | 74 | // Get the input argument type string (a GVariant type string format) 75 | const std::vector &getInArgs() const { return inArgs; } 76 | 77 | // Get the output argument type string (a GVariant type string format) 78 | const std::string &getOutArgs() const { return outArgs; } 79 | 80 | // Set the argument types for this method 81 | // 82 | // This method should generally not be called directly. Rather, the arguments should be set by the constructor 83 | DBusMethod &setArgs(const std::vector &inArgs, const std::string &outArgs) 84 | { 85 | this->inArgs = inArgs; 86 | this->outArgs = outArgs; 87 | return *this; 88 | } 89 | 90 | // 91 | // Call the method 92 | // 93 | 94 | // Calls the method 95 | // 96 | // If a callback delegate has been set, then this method will call that delegate, otherwise this method will do nothing 97 | template 98 | void call(GDBusConnection *pConnection, const DBusObjectPath &path, const std::string &interfaceName, const std::string &methodName, GVariant *pParameters, GDBusMethodInvocation *pInvocation, void *pUserData) const 99 | { 100 | // This should never happen, but technically possible if instantiated with a nullptr for `callback` 101 | if (!callback) 102 | { 103 | Logger::error(SSTR << "DBusMethod contains no callback: [" << path << "]:[" << interfaceName << "]:[" << methodName << "]"); 104 | g_dbus_method_invocation_return_dbus_error(pInvocation, kErrorNotImplemented.c_str(), "This method is not implemented"); 105 | return; 106 | } 107 | 108 | Logger::info(SSTR << "Calling method: [" << path << "]:[" << interfaceName << "]:[" << methodName << "]"); 109 | callback(*static_cast(pOwner), pConnection, methodName, pParameters, pInvocation, pUserData); 110 | } 111 | 112 | // Internal method used to generate introspection XML used to describe our services on D-Bus 113 | std::string generateIntrospectionXML(int depth) const; 114 | 115 | private: 116 | const DBusInterface *pOwner; 117 | std::string name; 118 | std::vector inArgs; 119 | std::string outArgs; 120 | Callback callback; 121 | }; 122 | 123 | }; // namespace ggk -------------------------------------------------------------------------------- /src/DBusObject.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is an abstraction of a D-Bus object. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // A D-Bus object is a container for any number of functional interfaces to expose on the bus. Objects are referred to by their 31 | // path ("/com/acme/widgets"). Here is a simple example of how D-Bus objects relate to Bluetooth services: 32 | // 33 | // Object (path) Interface (name) 34 | // 35 | // /com/acme/widget org.bluez.GattService1 36 | // /com/acme/widget/manufacturer_name org.bluez.GattCharacteristic1 37 | // /com/acme/widget/serial_number org.bluez.GattCharacteristic1 38 | // 39 | // In English, this would be read as "The Acme company has a widget, which has two characteristics defining the manufacturer name 40 | // and serial number for the widget." 41 | // 42 | // Finally, we'll include a published flag. Here's what that's all about: 43 | // 44 | // BlueZ uses the GetManagedObjects method (from the org.freedesktop.DBus.ObjectManager interface) to interrogate our 45 | // service(s). Our Server, however, includes all objects and interfaces, including the GetManagedObjects as well as the various 46 | // interfaces we expose over Bluetooth. Therefore, we'll need a way to know which ones to expose over Bluetooth (which is, in 47 | // general, everything EXCEPT the object containing the org.freedesktop.DBus.ObjectManager interface.) Since we manage our 48 | // objects in a hierarchy, only the root object's publish flag matters. 49 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 50 | 51 | #include "GattProperty.h" 52 | #include "DBusInterface.h" 53 | #include "GattService.h" 54 | #include "DBusObject.h" 55 | #include "Utils.h" 56 | #include "GattUuid.h" 57 | #include "Logger.h" 58 | 59 | namespace ggk { 60 | 61 | // Construct a root object with no parent 62 | // 63 | // We'll include a publish flag since only root objects can be published 64 | DBusObject::DBusObject(const DBusObjectPath &path, bool publish) 65 | : publish(publish), path(path), pParent(nullptr) 66 | { 67 | } 68 | 69 | // Construct a node object 70 | // 71 | // Nodes inherit their parent's publish path 72 | DBusObject::DBusObject(DBusObject *pParent, const DBusObjectPath &pathElement) 73 | : publish(pParent->publish), path(pathElement), pParent(pParent) 74 | { 75 | } 76 | 77 | // 78 | // Accessors 79 | // 80 | 81 | // Returns the `publish` flag 82 | bool DBusObject::isPublished() const 83 | { 84 | return publish; 85 | } 86 | 87 | // Returns the path node for this object within the hierarchy 88 | // 89 | // This method only returns the node. To get the full path, use `getPath()` 90 | const DBusObjectPath &DBusObject::getPathNode() const 91 | { 92 | return path; 93 | } 94 | 95 | // Returns the full path for this object within the hierarchy 96 | // 97 | // This method returns the full path. To get the current node, use `getPathNode()` 98 | DBusObjectPath DBusObject::getPath() const 99 | { 100 | DBusObjectPath path = getPathNode(); 101 | const DBusObject *pCurrent = pParent; 102 | 103 | // Traverse up my chain, adding nodes to the path until we have the full thing 104 | while(nullptr != pCurrent) 105 | { 106 | path = pCurrent->getPathNode() + path; 107 | pCurrent = pCurrent->pParent; 108 | } 109 | 110 | return path; 111 | } 112 | 113 | // Returns the parent object in the hierarchy 114 | DBusObject &DBusObject::getParent() 115 | { 116 | return *pParent; 117 | } 118 | 119 | // Returns the list of children objects 120 | const std::list &DBusObject::getChildren() const 121 | { 122 | return children; 123 | } 124 | 125 | // Add a child to this object 126 | DBusObject &DBusObject::addChild(const DBusObjectPath &pathElement) 127 | { 128 | children.push_back(DBusObject(this, pathElement)); 129 | return children.back(); 130 | } 131 | 132 | // Returns a list of interfaces for this object 133 | const DBusObject::InterfaceList &DBusObject::getInterfaces() const 134 | { 135 | return interfaces; 136 | } 137 | 138 | // Convenience functions to add a GATT service to the hierarchy 139 | // 140 | // We simply add a new child at the given path and add an interface configured as a GATT service to it using the given UUID. 141 | GattService &DBusObject::gattServiceBegin(const std::string &pathElement, const GattUuid &uuid) 142 | { 143 | DBusObject &child = addChild(DBusObjectPath(pathElement)); 144 | GattService &service = *child.addInterface(std::make_shared(child, "org.bluez.GattService1")); 145 | service.addProperty("UUID", uuid); 146 | service.addProperty("Primary", true); 147 | return service; 148 | } 149 | 150 | // 151 | // Helpful routines for searching objects 152 | // 153 | 154 | // Finds an interface by name within this D-Bus object 155 | std::shared_ptr DBusObject::findInterface(const DBusObjectPath &path, const std::string &interfaceName, const DBusObjectPath &basePath) const 156 | { 157 | if ((basePath + getPathNode()) == path) 158 | { 159 | for (std::shared_ptr interface : interfaces) 160 | { 161 | if (interfaceName == interface->getName()) 162 | { 163 | return interface; 164 | } 165 | } 166 | } 167 | 168 | for (const DBusObject &child : getChildren()) 169 | { 170 | std::shared_ptr pInterface = child.findInterface(path, interfaceName, basePath + getPathNode()); 171 | if (nullptr != pInterface) 172 | { 173 | return pInterface; 174 | } 175 | } 176 | 177 | return nullptr; 178 | } 179 | 180 | // Finds a BlueZ method by name within the specified D-Bus interface 181 | bool DBusObject::callMethod(const DBusObjectPath &path, const std::string &interfaceName, const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData, const DBusObjectPath &basePath) const 182 | { 183 | if ((basePath + getPathNode()) == path) 184 | { 185 | for (std::shared_ptr interface : interfaces) 186 | { 187 | if (interfaceName == interface->getName()) 188 | { 189 | if (interface->callMethod(methodName, pConnection, pParameters, pInvocation, pUserData)) 190 | { 191 | return true; 192 | } 193 | } 194 | } 195 | } 196 | 197 | for (const DBusObject &child : getChildren()) 198 | { 199 | if (child.callMethod(path, interfaceName, methodName, pConnection, pParameters, pInvocation, pUserData, basePath + getPathNode())) 200 | { 201 | return true; 202 | } 203 | } 204 | 205 | return false; 206 | } 207 | 208 | // Periodic timer tick propagation 209 | void DBusObject::tickEvents(GDBusConnection *pConnection, void *pUserData) const 210 | { 211 | for (std::shared_ptr interface : interfaces) 212 | { 213 | interface->tickEvents(pConnection, pUserData); 214 | } 215 | 216 | for (const DBusObject &child : getChildren()) 217 | { 218 | child.tickEvents(pConnection, pUserData); 219 | } 220 | } 221 | 222 | // --------------------------------------------------------------------------------------------------------------------------------- 223 | // XML generation for a D-Bus introspection 224 | // --------------------------------------------------------------------------------------------------------------------------------- 225 | 226 | // Internal method used to generate introspection XML used to describe our services on D-Bus 227 | std::string DBusObject::generateIntrospectionXML(int depth) const 228 | { 229 | std::string prefix; 230 | prefix.insert(0, depth * 2, ' '); 231 | 232 | std::string xml = std::string(); 233 | 234 | if (depth == 0) 235 | { 236 | xml += "\n"; 237 | xml += "\n"; 238 | } 239 | 240 | xml += prefix + "\n"; 241 | xml += prefix + " \n"; 242 | 243 | for (std::shared_ptr interface : interfaces) 244 | { 245 | xml += interface->generateIntrospectionXML(depth + 1); 246 | } 247 | 248 | for (DBusObject child : getChildren()) 249 | { 250 | xml += child.generateIntrospectionXML(depth + 1); 251 | } 252 | 253 | xml += prefix + "\n"; 254 | 255 | if (depth == 0) 256 | { 257 | Logger::debug("Generated XML:"); 258 | Logger::debug(xml); 259 | } 260 | 261 | return xml; 262 | } 263 | 264 | // --------------------------------------------------------------------------------------------------------------------------------- 265 | // D-Bus signals 266 | // --------------------------------------------------------------------------------------------------------------------------------- 267 | 268 | // Emits a signal on the bus from the given path, interface name and signal name, containing a GVariant set of parameters 269 | void DBusObject::emitSignal(GDBusConnection *pBusConnection, const std::string &interfaceName, const std::string &signalName, GVariant *pParameters) 270 | { 271 | GError *pError = nullptr; 272 | gboolean result = g_dbus_connection_emit_signal 273 | ( 274 | pBusConnection, // GDBusConnection *connection 275 | NULL, // const gchar *destination_bus_name 276 | getPath().c_str(), // const gchar *object_path 277 | interfaceName.c_str(), // const gchar *interface_name 278 | signalName.c_str(), // const gchar *signal_name 279 | pParameters, // GVariant *parameters 280 | &pError // GError **error 281 | ); 282 | 283 | if (0 == result) 284 | { 285 | Logger::error(SSTR << "Failed to emit signal named '" << signalName << "': " << (nullptr == pError ? "Unknown" : pError->message)); 286 | } 287 | } 288 | 289 | 290 | }; // namespace ggk -------------------------------------------------------------------------------- /src/DBusObject.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is an abstraction of a D-Bus object. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussino at the top of DBusObject.cpp 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "DBusObjectPath.h" 41 | 42 | namespace ggk { 43 | 44 | struct GattProperty; 45 | struct GattService; 46 | struct GattUuid; 47 | struct DBusInterface; 48 | 49 | struct DBusObject 50 | { 51 | // A convenience typedef for describing our list of interface 52 | typedef std::list > InterfaceList; 53 | 54 | // Construct a root object with no parent 55 | // 56 | // We'll include a publish flag since only root objects can be published 57 | DBusObject(const DBusObjectPath &path, bool publish = true); 58 | 59 | // Construct a node object 60 | // 61 | // Nodes inherit their parent's publish path 62 | DBusObject(DBusObject *pParent, const DBusObjectPath &pathElement); 63 | 64 | // 65 | // Accessors 66 | // 67 | 68 | // Returns the `publish` flag 69 | bool isPublished() const; 70 | 71 | // Returns the path node for this object within the hierarchy 72 | // 73 | // This method only returns the node. To get the full path, use `getPath()` 74 | const DBusObjectPath &getPathNode() const; 75 | 76 | // Returns the full path for this object within the hierarchy 77 | // 78 | // This method returns the full path. To get the current node, use `getPathNode()` 79 | DBusObjectPath getPath() const; 80 | 81 | // Returns the parent object in the hierarchy 82 | DBusObject &getParent(); 83 | 84 | // Returns the list of children objects 85 | const std::list &getChildren() const; 86 | 87 | // Add a child to this object 88 | DBusObject &addChild(const DBusObjectPath &pathElement); 89 | 90 | // Returns a list of interfaces for this object 91 | const InterfaceList &getInterfaces() const; 92 | 93 | // Templated method for adding typed interfaces to the object 94 | template 95 | std::shared_ptr addInterface(std::shared_ptr interface) 96 | { 97 | interfaces.push_back(interface); 98 | return std::static_pointer_cast(interfaces.back()); 99 | } 100 | 101 | // Internal method used to generate introspection XML used to describe our services on D-Bus 102 | std::string generateIntrospectionXML(int depth = 0) const; 103 | 104 | // Convenience functions to add a GATT service to the hierarchy 105 | // 106 | // We simply add a new child at the given path and add an interface configured as a GATT service to it using the given UUID. 107 | // 108 | // To end a service, call `gattServiceEnd()` 109 | GattService &gattServiceBegin(const std::string &pathElement, const GattUuid &uuid); 110 | 111 | // 112 | // Helpful routines for searching objects 113 | // 114 | 115 | // Finds an interface by name within this D-Bus object 116 | std::shared_ptr findInterface(const DBusObjectPath &path, const std::string &interfaceName, const DBusObjectPath &basePath = DBusObjectPath()) const; 117 | 118 | // Finds a BlueZ method by name within the specified D-Bus interface 119 | bool callMethod(const DBusObjectPath &path, const std::string &interfaceName, const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData, const DBusObjectPath &basePath = DBusObjectPath()) const; 120 | 121 | // Periodic timer tick propagation 122 | void tickEvents(GDBusConnection *pConnection, void *pUserData = nullptr) const; 123 | 124 | // ----------------------------------------------------------------------------------------------------------------------------- 125 | // D-Bus signals 126 | // ----------------------------------------------------------------------------------------------------------------------------- 127 | 128 | // Emits a signal on the bus from the given path, interface name and signal name, containing a GVariant set of parameters 129 | void emitSignal(GDBusConnection *pBusConnection, const std::string &interfaceName, const std::string &signalName, GVariant *pParameters); 130 | 131 | private: 132 | bool publish; 133 | DBusObjectPath path; 134 | InterfaceList interfaces; 135 | std::list children; 136 | DBusObject *pParent; 137 | }; 138 | 139 | }; // namespace ggk -------------------------------------------------------------------------------- /src/DBusObjectPath.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This represents a custom string type for a D-Bus object path. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // A D-Bus object path is normal string in the form "/com/example/foo/bar". This class provides a set of methods for building 31 | // these paths safely in such a way that they are guaranteed to always provide a valid path. 32 | // 33 | // In addition to this functionality, our DBusObjectPath is its own distinct type requiring explicit conversion, providing a level 34 | // of protection against accidentally using an arbitrary string as an object path. 35 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 36 | 37 | #pragma once 38 | 39 | #include 40 | #include 41 | 42 | namespace ggk { 43 | 44 | struct DBusObjectPath 45 | { 46 | // Default constructor (creates a root path) 47 | inline DBusObjectPath() { path = "/"; } 48 | 49 | // Copy constructor 50 | inline DBusObjectPath(const DBusObjectPath &path) : path(path.path) {} 51 | 52 | // Constructor that accepts a C string 53 | // 54 | // Note: explicit because we don't want accidental conversion. Creating a DBusObjectPath must be intentional. 55 | inline explicit DBusObjectPath(const char *pPath) : path(pPath) {} 56 | 57 | // Constructor that accepts a std::string 58 | // 59 | // Note: explicit because we don't want accidental conversion. Creating a DBusObjectPath must be intentional. 60 | inline explicit DBusObjectPath(const std::string &path) : path(path) {} 61 | 62 | // Explicit conversion to std::string 63 | inline const std::string &toString() const { return path; } 64 | 65 | // Explicit conversion to a C string 66 | inline const char *c_str() const { return path.c_str(); } 67 | 68 | // Assignment 69 | inline DBusObjectPath &operator =(const DBusObjectPath &rhs) 70 | { 71 | if (this == &rhs) return *this; 72 | path = rhs.path; 73 | return *this; 74 | } 75 | 76 | // Concatenation 77 | inline const DBusObjectPath &append(const char *rhs) 78 | { 79 | if (nullptr == rhs || !*rhs) { return *this; } 80 | if (path.empty()) { path = rhs; return *this; } 81 | 82 | bool ls = path.back() == '/'; 83 | bool rs = *rhs == '/'; 84 | if (ls && rs) { path.erase(path.length()-1); } 85 | if (!ls && !rs) { path += "/"; } 86 | 87 | path += rhs; 88 | return *this; 89 | } 90 | 91 | // Adds a path node (in the form of an std::string) to the end of the path 92 | inline const DBusObjectPath &append(const std::string &rhs) 93 | { 94 | return append(rhs.c_str()); 95 | } 96 | 97 | // Adds a path node (in the form of a DBusObjectPath) to the end of the path 98 | inline const DBusObjectPath &append(const DBusObjectPath &rhs) 99 | { 100 | return append(rhs.path.c_str()); 101 | } 102 | 103 | // Adds a path node (in the form of a DBusObjectPath) to the end of the path 104 | inline void operator +=(const DBusObjectPath &rhs) 105 | { 106 | append(rhs); 107 | } 108 | 109 | // Adds a path node (in the form of a C string) to the end of the path 110 | inline void operator +=(const char *rhs) 111 | { 112 | append(rhs); 113 | } 114 | 115 | // Adds a path node (in the form of an std::string) to the end of the path 116 | inline void operator +=(const std::string &rhs) 117 | { 118 | append(rhs); 119 | } 120 | 121 | // Concats two DBusObjectPaths into one, returning the resulting path 122 | inline DBusObjectPath operator +(const DBusObjectPath &rhs) const 123 | { 124 | DBusObjectPath result(*this); 125 | result += rhs; 126 | return result; 127 | } 128 | 129 | // Concats a C string onto a DBusObjectPath, returning the resulting path 130 | inline DBusObjectPath operator +(const char *rhs) const 131 | { 132 | DBusObjectPath result(*this); 133 | result += rhs; 134 | return result; 135 | } 136 | 137 | // Concats a std::string onto a DBusObjectPath, returning the resulting path 138 | inline DBusObjectPath operator +(const std::string &rhs) const 139 | { 140 | DBusObjectPath result(*this); 141 | result += rhs; 142 | return result; 143 | } 144 | 145 | // Tests two DBusObjectPaths for equality, returning true of the two strings are identical 146 | inline bool operator ==(const DBusObjectPath &rhs) const 147 | { 148 | return path == rhs.path; 149 | } 150 | 151 | private: 152 | 153 | std::string path; 154 | }; 155 | 156 | // Mixed-mode override for adding a DBusObjectPath to a C string, returning a new DBusObjectPath result 157 | inline DBusObjectPath operator +(const char *lhs, const DBusObjectPath &rhs) { return DBusObjectPath(lhs) + rhs; } 158 | 159 | // Mixed-mode override for adding a DBusObjectPath to a std::string, returning a new DBusObjectPath result 160 | inline DBusObjectPath operator +(const std::string &lhs, const DBusObjectPath &rhs) { return DBusObjectPath(lhs) + rhs; } 161 | 162 | // Streaming support for our DBusObjectPath (useful for our logging mechanism) 163 | inline std::ostream& operator<<(std::ostream &os, const DBusObjectPath &path) 164 | { 165 | os << path.toString(); 166 | return os; 167 | } 168 | 169 | // Streaming support for our DBusObjectPath (useful for our logging mechanism) 170 | inline std::ostream& operator +(std::ostream &os, const DBusObjectPath &path) 171 | { 172 | os << path.toString(); 173 | return os; 174 | } 175 | 176 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattCharacteristic.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is our representation of a GATT Characteristic which is intended to be used in our server description 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // A GATT characteristic is the component within the Bluetooth LE standard that holds and serves data over Bluetooth. This class 31 | // is intended to be used within the server description. For an explanation of how this class is used, see the detailed discussion 32 | // in Server.cpp. 33 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 34 | 35 | #include "GattCharacteristic.h" 36 | #include "GattDescriptor.h" 37 | #include "GattProperty.h" 38 | #include "GattUuid.h" 39 | #include "DBusObject.h" 40 | #include "GattService.h" 41 | #include "Utils.h" 42 | #include "Logger.h" 43 | 44 | namespace ggk { 45 | 46 | // 47 | // Standard constructor 48 | // 49 | 50 | // Construct a GattCharacteristic 51 | // 52 | // Genreally speaking, these objects should not be constructed directly. Rather, use the `gattCharacteristicBegin()` method 53 | // in `GattService`. 54 | GattCharacteristic::GattCharacteristic(DBusObject &owner, GattService &service, const std::string &name) 55 | : GattInterface(owner, name), service(service), pOnUpdatedValueFunc(nullptr) 56 | { 57 | } 58 | 59 | // Returning the owner pops us one level up the hierarchy 60 | // 61 | // This method compliments `GattService::gattCharacteristicBegin()` 62 | GattService &GattCharacteristic::gattCharacteristicEnd() 63 | { 64 | return service; 65 | } 66 | 67 | // Locates a D-Bus method within this D-Bus interface and invokes the method 68 | bool GattCharacteristic::callMethod(const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const 69 | { 70 | for (const DBusMethod &method : methods) 71 | { 72 | if (methodName == method.getName()) 73 | { 74 | method.call(pConnection, getPath(), getName(), methodName, pParameters, pInvocation, pUserData); 75 | return true; 76 | } 77 | } 78 | 79 | return false; 80 | } 81 | 82 | // Adds an event to the characteristic and returns a refereence to 'this` to enable method chaining in the server description 83 | // 84 | // NOTE: We specifically overload this method in order to accept our custom EventCallback type and transform it into a 85 | // TickEvent::Callback type. We also return our own type. This simplifies the server description by allowing call to chain. 86 | GattCharacteristic &GattCharacteristic::onEvent(int tickFrequency, void *pUserData, EventCallback callback) 87 | { 88 | events.push_back(TickEvent(this, tickFrequency, reinterpret_cast(callback), pUserData)); 89 | return *this; 90 | } 91 | 92 | // Ticks events within this characteristic 93 | // 94 | // Note: we specifically override this method in order to translate the generic TickEvent::Callback into our own EventCallback 95 | void GattCharacteristic::tickEvents(GDBusConnection *pConnection, void *pUserData) const 96 | { 97 | for (const TickEvent &event : events) 98 | { 99 | event.tick(getPath(), pConnection, pUserData); 100 | } 101 | } 102 | 103 | // Specialized support for ReadlValue method 104 | // 105 | // Defined as: array{byte} ReadValue(dict options) 106 | // 107 | // D-Bus breakdown: 108 | // 109 | // Input args: options - "a{sv}" 110 | // Output args: value - "ay" 111 | GattCharacteristic &GattCharacteristic::onReadValue(MethodCallback callback) 112 | { 113 | // array{byte} ReadValue(dict options) 114 | static const char *inArgs[] = {"a{sv}", nullptr}; 115 | addMethod("ReadValue", inArgs, "ay", reinterpret_cast(callback)); 116 | return *this; 117 | } 118 | 119 | // Specialized support for WriteValue method 120 | // 121 | // Defined as: void WriteValue(array{byte} value, dict options) 122 | // 123 | // D-Bus breakdown: 124 | // 125 | // Input args: value - "ay" 126 | // options - "a{sv}" 127 | // Output args: void 128 | GattCharacteristic &GattCharacteristic::onWriteValue(MethodCallback callback) 129 | { 130 | static const char *inArgs[] = {"ay", "a{sv}", nullptr}; 131 | addMethod("WriteValue", inArgs, nullptr, reinterpret_cast(callback)); 132 | return *this; 133 | } 134 | 135 | // Custom support for handling updates to our characteristic's value 136 | // 137 | // Defined as: (NOT defined by Bluetooth or BlueZ - this method is internal only) 138 | // 139 | // This method is called by our framework whenever a characteristic's value is updated. If you need to perform any actions 140 | // when a value is updatd, this is a good place to do that work. 141 | // 142 | // If you need to perform the same action(s) when a value is updated from the client (via `onWriteValue`) or from this server, 143 | // then it may be beneficial to call this method from within your onWriteValue callback to reduce duplicated code. See 144 | // `callOnUpdatedValue` for more information. 145 | GattCharacteristic &GattCharacteristic::onUpdatedValue(UpdatedValueCallback callback) 146 | { 147 | pOnUpdatedValueFunc = callback; 148 | return *this; 149 | } 150 | 151 | // Calls the onUpdatedValue method, if one was set. 152 | // 153 | // Returns false if there was no method set, otherwise, returns the boolean result of the method call. 154 | // 155 | // If you need to perform the same action(s) when a value is updated from the client (via onWriteValue) or from this server, 156 | // then it may be beneficial to place those actions in the `onUpdatedValue` method and call it from from within your 157 | // `onWriteValue` callback to reduce duplicated code. To call the `onUpdatedValue` method from within your `onWriteValue`, you 158 | // can use this pattern: 159 | // 160 | // .onWriteValue(CHARACTERISTIC_UPDATED_VALUE_CALLBACK_LAMBDA 161 | // { 162 | // // Update your value 163 | // ... 164 | // 165 | // // Call the onUpdateValue method that was set in the same Characteristic 166 | // self.callOnUpdatedValue(pConnection, pUserData); 167 | // }) 168 | bool GattCharacteristic::callOnUpdatedValue(GDBusConnection *pConnection, void *pUserData) const 169 | { 170 | if (nullptr == pOnUpdatedValueFunc) 171 | { 172 | return false; 173 | } 174 | 175 | Logger::debug(SSTR << "Calling OnUpdatedValue function for interface at path '" << getPath() << "'"); 176 | return pOnUpdatedValueFunc(*this, pConnection, pUserData); 177 | } 178 | 179 | // Convenience functions to add a GATT descriptor to the hierarchy 180 | // 181 | // We simply add a new child at the given path and add an interface configured as a GATT descriptor to it. The 182 | // new descriptor is declared with a UUID and a variable argument list of flags (in string form.) For a complete and 183 | // up-to-date list of flag values, see: https://git.kernel.org/pub/scm/bluetooth/bluez.git/plain/doc/gatt-api.txt 184 | // 185 | // At the time of this writing, the list of flags is as follows: 186 | // 187 | // "read" 188 | // "write" 189 | // "encrypt-read" 190 | // "encrypt-write" 191 | // "encrypt-authenticated-read" 192 | // "encrypt-authenticated-write" 193 | // "secure-read" (Server Only) 194 | // "secure-write" (Server Only) 195 | // 196 | // 197 | // To end a descriptor, call `GattDescriptor::gattDescriptorEnd()` 198 | GattDescriptor &GattCharacteristic::gattDescriptorBegin(const std::string &pathElement, const GattUuid &uuid, const std::vector &flags) 199 | { 200 | DBusObject &child = owner.addChild(DBusObjectPath(pathElement)); 201 | GattDescriptor &descriptor = *child.addInterface(std::make_shared(child, *this, "org.bluez.GattDescriptor1")); 202 | descriptor.addProperty("UUID", uuid); 203 | descriptor.addProperty("Characteristic", getPath()); 204 | descriptor.addProperty("Flags", flags); 205 | return descriptor; 206 | } 207 | 208 | // Sends a change notification to subscribers to this characteristic 209 | // 210 | // This is a generalized method that accepts a `GVariant *`. A templated version is available that supports common types called 211 | // `sendChangeNotificationValue()`. 212 | // 213 | // The caller may choose to consult HciAdapter::getInstance().getActiveConnectionCount() in order to determine if there are any 214 | // active connections before sending a change notification. 215 | void GattCharacteristic::sendChangeNotificationVariant(GDBusConnection *pBusConnection, GVariant *pNewValue) const 216 | { 217 | g_auto(GVariantBuilder) builder; 218 | g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); 219 | g_variant_builder_add(&builder, "{sv}", "Value", pNewValue); 220 | GVariant *pSasv = g_variant_new("(sa{sv})", "org.bluez.GattCharacteristic1", &builder); 221 | owner.emitSignal(pBusConnection, "org.freedesktop.DBus.Properties", "PropertiesChanged", pSasv); 222 | } 223 | 224 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattCharacteristic.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is our representation of a GATT Characteristic which is intended to be used in our server description 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussion at the top of GattCharacteristic.cpp 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "Utils.h" 41 | #include "TickEvent.h" 42 | #include "GattInterface.h" 43 | #include "HciAdapter.h" 44 | 45 | namespace ggk { 46 | 47 | // --------------------------------------------------------------------------------------------------------------------------------- 48 | // Forward declarations 49 | // --------------------------------------------------------------------------------------------------------------------------------- 50 | 51 | struct GattCharacteristic; 52 | struct GattDescriptor; 53 | struct GattProperty; 54 | struct GattService; 55 | struct GattUuid; 56 | struct DBusObject; 57 | 58 | // --------------------------------------------------------------------------------------------------------------------------------- 59 | // Useful Lambdas 60 | // --------------------------------------------------------------------------------------------------------------------------------- 61 | 62 | #define CHARACTERISTIC_UPDATED_VALUE_CALLBACK_LAMBDA [] \ 63 | ( \ 64 | const GattCharacteristic &self, \ 65 | GDBusConnection *pConnection, \ 66 | void *pUserData \ 67 | ) -> bool 68 | 69 | #define CHARACTERISTIC_EVENT_CALLBACK_LAMBDA [] \ 70 | ( \ 71 | const GattCharacteristic &self, \ 72 | const TickEvent &event, \ 73 | GDBusConnection *pConnection, \ 74 | void *pUserData \ 75 | ) 76 | 77 | #define CHARACTERISTIC_METHOD_CALLBACK_LAMBDA [] \ 78 | ( \ 79 | const GattCharacteristic &self, \ 80 | GDBusConnection *pConnection, \ 81 | const std::string &methodName, \ 82 | GVariant *pParameters, \ 83 | GDBusMethodInvocation *pInvocation, \ 84 | void *pUserData \ 85 | ) 86 | 87 | // --------------------------------------------------------------------------------------------------------------------------------- 88 | // Representation of a Bluetooth GATT Characteristic 89 | // --------------------------------------------------------------------------------------------------------------------------------- 90 | 91 | struct GattCharacteristic : GattInterface 92 | { 93 | // Our interface type 94 | static constexpr const char *kInterfaceType = "GattCharacteristic"; 95 | 96 | typedef void (*MethodCallback)(const GattCharacteristic &self, GDBusConnection *pConnection, const std::string &methodName, GVariant *pParameters, GDBusMethodInvocation *pInvocation, void *pUserData); 97 | typedef void (*EventCallback)(const GattCharacteristic &self, const TickEvent &event, GDBusConnection *pConnection, void *pUserData); 98 | typedef bool (*UpdatedValueCallback)(const GattCharacteristic &self, GDBusConnection *pConnection, void *pUserData); 99 | 100 | // Construct a GattCharacteristic 101 | // 102 | // Genreally speaking, these objects should not be constructed directly. Rather, use the `gattCharacteristicBegin()` method 103 | // in `GattService`. 104 | GattCharacteristic(DBusObject &owner, GattService &service, const std::string &name); 105 | virtual ~GattCharacteristic() {} 106 | 107 | // Returns a string identifying the type of interface 108 | virtual const std::string getInterfaceType() const { return GattCharacteristic::kInterfaceType; } 109 | 110 | // Returning the owner pops us one level up the hierarchy 111 | // 112 | // This method compliments `GattService::gattCharacteristicBegin()` 113 | GattService &gattCharacteristicEnd(); 114 | 115 | // Locates a D-Bus method within this D-Bus interface and invokes the method 116 | virtual bool callMethod(const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const; 117 | 118 | // Adds an event to the characteristic and returns a refereence to 'this` to enable method chaining in the server description 119 | // 120 | // NOTE: We specifically overload this method in order to accept our custom EventCallback type and transform it into a 121 | // TickEvent::Callback type. We also return our own type. This simplifies the server description by allowing call to chain. 122 | GattCharacteristic &onEvent(int tickFrequency, void *pUserData, EventCallback callback); 123 | 124 | // Ticks events within this characteristic 125 | // 126 | // Note: we specifically override this method in order to translate the generic TickEvent::Callback into our own EventCallback 127 | virtual void tickEvents(GDBusConnection *pConnection, void *pUserData) const; 128 | 129 | // Specialized support for Characteristic ReadlValue method 130 | // 131 | // Defined as: array{byte} ReadValue(dict options) 132 | // 133 | // D-Bus breakdown: 134 | // 135 | // Input args: options - "a{sv}" 136 | // Output args: value - "ay" 137 | GattCharacteristic &onReadValue(MethodCallback callback); 138 | 139 | // Specialized support for Characteristic WriteValue method 140 | // 141 | // Defined as: void WriteValue(array{byte} value, dict options) 142 | // 143 | // D-Bus breakdown: 144 | // 145 | // Input args: value - "ay" 146 | // options - "a{sv}" 147 | // Output args: void 148 | GattCharacteristic &onWriteValue(MethodCallback callback); 149 | 150 | // Custom support for handling updates to our characteristic's value 151 | // 152 | // Defined as: (NOT defined by Bluetooth or BlueZ - this method is internal only) 153 | // 154 | // This method is called by our framework whenever a characteristic's value is updated. If you need to perform any actions 155 | // when a value is updatd, this is a good place to do that work. 156 | // 157 | // If you need to perform the same action(s) when a value is updated from the client (via `onWriteValue`) or from this server, 158 | // then it may be beneficial to call this method from within your onWriteValue callback to reduce duplicated code. See 159 | // `callOnUpdatedValue` for more information. 160 | GattCharacteristic &onUpdatedValue(UpdatedValueCallback callback); 161 | 162 | // Calls the onUpdatedValue method, if one was set. 163 | // 164 | // Returns false if there was no method set, otherwise, returns the boolean result of the method call. 165 | // 166 | // If you need to perform the same action(s) when a value is updated from the client (via onWriteValue) or from this server, 167 | // then it may be beneficial to place those actions in the `onUpdatedValue` method and call it from from within your 168 | // `onWriteValue` callback to reduce duplicated code. To call the `onUpdatedValue` method from within your `onWriteValue`, you 169 | // can use this pattern: 170 | // 171 | // .onWriteValue(CHARACTERISTIC_UPDATED_VALUE_CALLBACK_LAMBDA 172 | // { 173 | // // Update your value 174 | // ... 175 | // 176 | // // Call the onUpdateValue method that was set in the same Characteristic 177 | // self.callOnUpdatedValue(pConnection, pUserData); 178 | // }) 179 | bool callOnUpdatedValue(GDBusConnection *pConnection, void *pUserData) const; 180 | 181 | // Convenience functions to add a GATT descriptor to the hierarchy 182 | // 183 | // We simply add a new child at the given path and add an interface configured as a GATT descriptor to it. The 184 | // new descriptor is declared with a UUID and a variable argument list of flags (in string form.) For a complete and 185 | // up-to-date list of flag values, see: https://git.kernel.org/pub/scm/bluetooth/bluez.git/plain/doc/gatt-api.txt 186 | // 187 | // At the time of this writing, the list of flags is as follows: 188 | // 189 | // "read" 190 | // "write" 191 | // "encrypt-read" 192 | // "encrypt-write" 193 | // "encrypt-authenticated-read" 194 | // "encrypt-authenticated-write" 195 | // "secure-read" (Server Only) 196 | // "secure-write" (Server Only) 197 | // 198 | // To end the descriptor, call `gattDescriptorEnd()` 199 | GattDescriptor &gattDescriptorBegin(const std::string &pathElement, const GattUuid &uuid, const std::vector &flags); 200 | 201 | // Sends a change notification to subscribers to this characteristic 202 | // 203 | // This is a generalized method that accepts a `GVariant *`. A templated version is available that supports common types called 204 | // `sendChangeNotificationValue()`. 205 | // 206 | // The caller may choose to consult HciAdapter::getInstance().getActiveConnectionCount() in order to determine if there are any 207 | // active connections before sending a change notification. 208 | void sendChangeNotificationVariant(GDBusConnection *pBusConnection, GVariant *pNewValue) const; 209 | 210 | // Sends a change notification to subscribers to this characteristic 211 | // 212 | // This is a helper method that accepts common types. For custom types, there is a form that accepts a `GVariant *`, called 213 | // `sendChangeNotificationVariant()`. 214 | // 215 | // The caller may choose to consult HciAdapter::getInstance().getActiveConnectionCount() in order to determine if there are any 216 | // active connections before sending a change notification. 217 | template 218 | void sendChangeNotificationValue(GDBusConnection *pBusConnection, T value) const 219 | { 220 | GVariant *pVariant = Utils::gvariantFromByteArray(value); 221 | sendChangeNotificationVariant(pBusConnection, pVariant); 222 | } 223 | 224 | protected: 225 | 226 | GattService &service; 227 | UpdatedValueCallback pOnUpdatedValueFunc; 228 | }; 229 | 230 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattDescriptor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is our representation of a GATT Characteristic which is intended to be used in our server description 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // A GATT descriptor is the component within the Bluetooth LE standard that holds and serves metadata about a Characteristic over 31 | // Bluetooth. This class is intended to be used within the server description. For an explanation of how this class is used, see the 32 | // detailed discussion in Server.cpp. 33 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 34 | 35 | #include "GattDescriptor.h" 36 | #include "GattProperty.h" 37 | #include "DBusObject.h" 38 | #include "Utils.h" 39 | #include "Logger.h" 40 | 41 | namespace ggk { 42 | 43 | // 44 | // Standard constructor 45 | // 46 | 47 | // Construct a GattDescriptor 48 | // 49 | // Genreally speaking, these objects should not be constructed directly. Rather, use the `gattDescriptorBegin()` method 50 | // in `GattCharacteristic`. 51 | GattDescriptor::GattDescriptor(DBusObject &owner, GattCharacteristic &characteristic, const std::string &name) 52 | : GattInterface(owner, name), characteristic(characteristic), pOnUpdatedValueFunc(nullptr) 53 | { 54 | } 55 | 56 | // Returning the owner pops us one level up the hierarchy 57 | // 58 | // This method compliments `GattCharacteristic::gattDescriptorBegin()` 59 | GattCharacteristic &GattDescriptor::gattDescriptorEnd() 60 | { 61 | return characteristic; 62 | } 63 | 64 | // 65 | // D-Bus interface methods 66 | // 67 | 68 | // Locates a D-Bus method within this D-Bus interface 69 | bool GattDescriptor::callMethod(const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const 70 | { 71 | for (const DBusMethod &method : methods) 72 | { 73 | if (methodName == method.getName()) 74 | { 75 | method.call(pConnection, getPath(), getName(), methodName, pParameters, pInvocation, pUserData); 76 | return true; 77 | } 78 | } 79 | 80 | return false; 81 | } 82 | 83 | // Adds an event to the descriptor and returns a refereence to 'this` to enable method chaining in the server description 84 | // 85 | // NOTE: We specifically overload this method in order to accept our custom EventCallback type and transform it into a 86 | // TickEvent::Callback type. We also return our own type. This simplifies the server description by allowing call to chain. 87 | GattDescriptor &GattDescriptor::onEvent(int tickFrequency, void *pUserData, EventCallback callback) 88 | { 89 | events.push_back(TickEvent(this, tickFrequency, reinterpret_cast(callback), pUserData)); 90 | return *this; 91 | } 92 | 93 | // Ticks events within this descriptor 94 | // 95 | // Note: we specifically override this method in order to translate the generic TickEvent::Callback into our own EventCallback 96 | void GattDescriptor::tickEvents(GDBusConnection *pConnection, void *pUserData) const 97 | { 98 | for (const TickEvent &event : events) 99 | { 100 | event.tick(getPath(), pConnection, pUserData); 101 | } 102 | } 103 | 104 | // Specialized support for ReadlValue method 105 | // 106 | // Defined as: array{byte} ReadValue(dict options) 107 | // 108 | // D-Bus breakdown: 109 | // 110 | // Input args: options - "a{sv}" 111 | // Output args: value - "ay" 112 | GattDescriptor &GattDescriptor::onReadValue(MethodCallback callback) 113 | { 114 | // array{byte} ReadValue(dict options) 115 | const char *inArgs[] = {"a{sv}", nullptr}; 116 | addMethod("ReadValue", inArgs, "ay", reinterpret_cast(callback)); 117 | return *this; 118 | } 119 | 120 | // Specialized support for WriteValue method 121 | // 122 | // Defined as: void WriteValue(array{byte} value, dict options) 123 | // 124 | // D-Bus breakdown: 125 | // 126 | // Input args: value - "ay" 127 | // options - "a{sv}" 128 | // Output args: void 129 | GattDescriptor &GattDescriptor::onWriteValue(MethodCallback callback) 130 | { 131 | const char *inArgs[] = {"ay", "a{sv}", nullptr}; 132 | addMethod("WriteValue", inArgs, nullptr, reinterpret_cast(callback)); 133 | return *this; 134 | } 135 | 136 | // Custom support for handling updates to our descriptor's value 137 | // 138 | // Defined as: (NOT defined by Bluetooth or BlueZ - this method is internal only) 139 | // 140 | // This method is called by our framework whenever a Descriptor's value is updated. If you need to perform any actions 141 | // when a value is updatd, this is a good place to do that work. 142 | // 143 | // If you need to perform the same action(s) when a value is updated from the client (via `onWriteValue`) or from this server, 144 | // then it may be beneficial to call this method from within your onWriteValue callback to reduce duplicated code. See 145 | // `callOnUpdatedValue` for more information. 146 | GattDescriptor &GattDescriptor::onUpdatedValue(UpdatedValueCallback callback) 147 | { 148 | pOnUpdatedValueFunc = callback; 149 | return *this; 150 | } 151 | 152 | // Calls the onUpdatedValue method, if one was set. 153 | // 154 | // Returns false if there was no method set, otherwise, returns the boolean result of the method call. 155 | // 156 | // If you need to perform the same action(s) when a value is updated from the client (via onWriteValue) or from this server, 157 | // then it may be beneficial to place those actions in the `onUpdatedValue` method and call it from from within your 158 | // `onWriteValue` callback to reduce duplicated code. To call the `onUpdatedValue` method from within your `onWriteValue`, you 159 | // can use this pattern: 160 | // 161 | // .onUpdatedValue(DESCRIPTOR_UPDATED_VALUE_CALLBACK_LAMBDA 162 | // { 163 | // // Update your value 164 | // ... 165 | // 166 | // // Call the onUpdateValue method that was set in the same Descriptor 167 | // self.callOnUpdatedValue(pConnection, pUserData); 168 | // }) 169 | bool GattDescriptor::callOnUpdatedValue(GDBusConnection *pConnection, void *pUserData) const 170 | { 171 | if (nullptr == pOnUpdatedValueFunc) 172 | { 173 | return false; 174 | } 175 | 176 | Logger::debug(SSTR << "Calling OnUpdatedValue function for interface at path '" << getPath() << "'"); 177 | return pOnUpdatedValueFunc(*this, pConnection, pUserData); 178 | } 179 | 180 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattDescriptor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is our representation of a GATT Characteristic which is intended to be used in our server description 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussion at the top of GattDescriptor.cpp 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "TickEvent.h" 40 | #include "Utils.h" 41 | #include "GattInterface.h" 42 | 43 | namespace ggk { 44 | 45 | // --------------------------------------------------------------------------------------------------------------------------------- 46 | // Forward declarations 47 | // --------------------------------------------------------------------------------------------------------------------------------- 48 | 49 | struct GattCharacteristic; 50 | struct GattDescriptor; 51 | struct GattProperty; 52 | struct DBusObject; 53 | 54 | // --------------------------------------------------------------------------------------------------------------------------------- 55 | // Useful Lambdas 56 | // --------------------------------------------------------------------------------------------------------------------------------- 57 | 58 | #define DESCRIPTOR_UPDATED_VALUE_CALLBACK_LAMBDA [] \ 59 | ( \ 60 | const GattDescriptor &self, \ 61 | GDBusConnection *pConnection, \ 62 | void *pUserData \ 63 | ) -> bool 64 | 65 | #define DESCRIPTOR_EVENT_CALLBACK_LAMBDA [] \ 66 | ( \ 67 | const GattDescriptor &self, \ 68 | const TickEvent &event, \ 69 | GDBusConnection *pConnection, \ 70 | void *pUserData \ 71 | ) 72 | 73 | #define DESCRIPTOR_METHOD_CALLBACK_LAMBDA [] \ 74 | ( \ 75 | const GattDescriptor &self, \ 76 | GDBusConnection *pConnection, \ 77 | const std::string &methodName, \ 78 | GVariant *pParameters, \ 79 | GDBusMethodInvocation *pInvocation, \ 80 | void *pUserData \ 81 | ) 82 | 83 | // --------------------------------------------------------------------------------------------------------------------------------- 84 | // Representation of a Bluetooth GATT Descriptor 85 | // --------------------------------------------------------------------------------------------------------------------------------- 86 | 87 | struct GattDescriptor : GattInterface 88 | { 89 | // Our interface type 90 | static constexpr const char *kInterfaceType = "GattDescriptor"; 91 | 92 | typedef void (*MethodCallback)(const GattDescriptor &self, GDBusConnection *pConnection, const std::string &methodName, GVariant *pParameters, GDBusMethodInvocation *pInvocation, void *pUserData); 93 | typedef void (*EventCallback)(const GattDescriptor &self, const TickEvent &event, GDBusConnection *pConnection, void *pUserData); 94 | typedef bool (*UpdatedValueCallback)(const GattDescriptor &self, GDBusConnection *pConnection, void *pUserData); 95 | 96 | // 97 | // Standard constructor 98 | // 99 | 100 | // Construct a GattDescriptor 101 | // 102 | // Genreally speaking, these objects should not be constructed directly. Rather, use the `gattDescriptorBegin()` method 103 | // in `GattCharacteristic`. 104 | GattDescriptor(DBusObject &owner, GattCharacteristic &characteristic, const std::string &name); 105 | virtual ~GattDescriptor() {} 106 | 107 | // Returns a string identifying the type of interface 108 | virtual const std::string getInterfaceType() const { return GattDescriptor::kInterfaceType; } 109 | 110 | // Returning the owner pops us one level up the hierarchy 111 | // 112 | // This method compliments `GattCharacteristic::gattDescriptorBegin()` 113 | GattCharacteristic &gattDescriptorEnd(); 114 | 115 | // Locates a D-Bus method within this D-Bus interface and invokes the method 116 | virtual bool callMethod(const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const; 117 | 118 | // Adds an event to the descriptor and returns a refereence to 'this` to enable method chaining in the server description 119 | // 120 | // NOTE: We specifically overload this method in order to accept our custom EventCallback type and transform it into a 121 | // TickEvent::Callback type. We also return our own type. This simplifies the server description by allowing call to chain. 122 | GattDescriptor &onEvent(int tickFrequency, void *pUserData, EventCallback callback); 123 | 124 | // Ticks events within this descriptor 125 | // 126 | // Note: we specifically override this method in order to translate the generic TickEvent::Callback into our own EventCallback 127 | virtual void tickEvents(GDBusConnection *pConnection, void *pUserData) const; 128 | 129 | // Specialized support for Descriptor ReadlValue method 130 | // 131 | // Defined as: array{byte} ReadValue(dict options) 132 | // 133 | // D-Bus breakdown: 134 | // 135 | // Input args: options - "a{sv}" 136 | // Output args: value - "ay" 137 | GattDescriptor &onReadValue(MethodCallback callback); 138 | 139 | // Specialized support for Descriptor WriteValue method 140 | // 141 | // Defined as: void WriteValue(array{byte} value, dict options) 142 | // 143 | // D-Bus breakdown: 144 | // 145 | // Input args: value - "ay" 146 | // options - "a{sv}" 147 | // Output args: void 148 | GattDescriptor &onWriteValue(MethodCallback callback); 149 | 150 | // Custom support for handling updates to our descriptor's value 151 | // 152 | // Defined as: (NOT defined by Bluetooth or BlueZ - this method is internal only) 153 | // 154 | // This method is called by our framework whenever a Descriptor's value is updated. If you need to perform any actions 155 | // when a value is updatd, this is a good place to do that work. 156 | // 157 | // If you need to perform the same action(s) when a value is updated from the client (via `onWriteValue`) or from this server, 158 | // then it may be beneficial to call this method from within your onWriteValue callback to reduce duplicated code. See 159 | // `callOnUpdatedValue` for more information. 160 | GattDescriptor &onUpdatedValue(UpdatedValueCallback callback); 161 | 162 | // Calls the onUpdatedValue method, if one was set. 163 | // 164 | // Returns false if there was no method set, otherwise, returns the boolean result of the method call. 165 | // 166 | // If you need to perform the same action(s) when a value is updated from the client (via onWriteValue) or from this server, 167 | // then it may be beneficial to place those actions in the `onUpdatedValue` method and call it from from within your 168 | // `onWriteValue` callback to reduce duplicated code. To call the `onUpdatedValue` method from within your `onWriteValue`, you 169 | // can use this pattern: 170 | // 171 | // .onUpdatedValue(DESCRIPTOR_UPDATED_VALUE_CALLBACK_LAMBDA 172 | // { 173 | // // Update your value 174 | // ... 175 | // 176 | // // Call the onUpdateValue method that was set in the same Descriptor 177 | // self.callOnUpdatedValue(pConnection, pUserData); 178 | // }) 179 | bool callOnUpdatedValue(GDBusConnection *pConnection, void *pUserData) const; 180 | 181 | protected: 182 | 183 | GattCharacteristic &characteristic; 184 | UpdatedValueCallback pOnUpdatedValueFunc; 185 | }; 186 | 187 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattInterface.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is our abstraction layer for GATT interfaces, used by GattService, GattCharacteristic & GattDescriptor 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // This class is intended to be used within the server description. For an explanation of how this class is used, see the detailed 31 | // description in Server.cpp. 32 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33 | 34 | #include "GattInterface.h" 35 | #include "GattProperty.h" 36 | #include "DBusObject.h" 37 | #include "Logger.h" 38 | 39 | namespace ggk { 40 | 41 | // 42 | // Standard constructor 43 | // 44 | GattInterface::GattInterface(DBusObject &owner, const std::string &name) 45 | : DBusInterface(owner, name) 46 | { 47 | } 48 | 49 | GattInterface::~GattInterface() 50 | { 51 | } 52 | 53 | // 54 | // GATT Characteristic properties 55 | // 56 | 57 | // Returns the list of GATT properties 58 | const std::list &GattInterface::getProperties() const 59 | { 60 | return properties; 61 | } 62 | 63 | // When responding to a method, we need to return a GVariant value wrapped in a tuple. This method will simplify this slightly by 64 | // wrapping a GVariant of the type "ay" and wrapping it in a tuple before sending it off as the method response. 65 | // 66 | // This is the generalized form that accepts a GVariant *. There is a templated helper method (`methodReturnValue()`) that accepts 67 | // common types. 68 | void GattInterface::methodReturnVariant(GDBusMethodInvocation *pInvocation, GVariant *pVariant, bool wrapInTuple) const 69 | { 70 | if (wrapInTuple) 71 | { 72 | pVariant = g_variant_new_tuple(&pVariant, 1); 73 | } 74 | g_dbus_method_invocation_return_value(pInvocation, pVariant); 75 | } 76 | 77 | // Locates a `GattProperty` within the interface 78 | // 79 | // This method returns a pointer to the property or nullptr if not found 80 | const GattProperty *GattInterface::findProperty(const std::string &name) const 81 | { 82 | for (const GattProperty &property : properties) 83 | { 84 | if (property.getName() == name) 85 | { 86 | return &property; 87 | } 88 | } 89 | 90 | return nullptr; 91 | } 92 | 93 | // Internal method used to generate introspection XML used to describe our services on D-Bus 94 | std::string GattInterface::generateIntrospectionXML(int depth) const 95 | { 96 | std::string prefix; 97 | prefix.insert(0, depth * 2, ' '); 98 | 99 | std::string xml = std::string(); 100 | 101 | if (methods.size() && getProperties().empty()) 102 | { 103 | xml += prefix + "\n"; 104 | } 105 | else 106 | { 107 | xml += prefix + "\n"; 108 | 109 | // Describe our methods 110 | for (const DBusMethod &method : methods) 111 | { 112 | xml += method.generateIntrospectionXML(depth + 1); 113 | } 114 | 115 | // Describe our properties 116 | for (const GattProperty &property : getProperties()) 117 | { 118 | xml += property.generateIntrospectionXML(depth + 1); 119 | } 120 | 121 | xml += prefix + "\n"; 122 | } 123 | 124 | return xml; 125 | } 126 | 127 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is our abstraction layer for GATT interfaces, used by GattService, GattCharacteristic & GattDescriptor 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussion at the top of GattInterface.cpp 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "TickEvent.h" 40 | #include "DBusInterface.h" 41 | #include "GattProperty.h" 42 | #include "GattUuid.h" 43 | #include "Server.h" 44 | #include "Utils.h" 45 | 46 | namespace ggk { 47 | 48 | // --------------------------------------------------------------------------------------------------------------------------------- 49 | // Forward declarations 50 | // --------------------------------------------------------------------------------------------------------------------------------- 51 | 52 | struct GattInterface; 53 | struct DBusObject; 54 | 55 | // --------------------------------------------------------------------------------------------------------------------------------- 56 | // Pure virtual representation of a Bluetooth GATT Interface, the base class for Services, Characteristics and Descriptors 57 | // --------------------------------------------------------------------------------------------------------------------------------- 58 | 59 | struct GattInterface : DBusInterface 60 | { 61 | // Standard constructor 62 | GattInterface(DBusObject &owner, const std::string &name); 63 | virtual ~GattInterface(); 64 | 65 | // Returns a string identifying the type of interface 66 | virtual const std::string getInterfaceType() const = 0; 67 | 68 | // 69 | // GATT Characteristic properties 70 | // 71 | 72 | // Returns the list of GATT properties 73 | const std::list &getProperties() const; 74 | 75 | // Add a `GattProperty` to the interface 76 | // 77 | // There are helper methods for adding properties for common types as well as a generalized helper method for adding a 78 | // `GattProperty` of a generic GVariant * type. 79 | template 80 | T &addProperty(const GattProperty &property) 81 | { 82 | properties.push_back(property); 83 | return *static_cast(this); 84 | } 85 | 86 | // Add a named property with a GVariant * 87 | // 88 | // There are helper methods for common types (UUIDs, strings, boolean, etc.) Use this method when no helper method exists for 89 | // the type you want to use. There is also a helper method for adding a named property of a pre-built `GattProperty`. 90 | template 91 | T &addProperty(const std::string &name, GVariant *pValue, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr) 92 | { 93 | return addProperty(GattProperty(name, pValue, getter, setter)); 94 | } 95 | 96 | // Helper method for adding a named property with a `GattUuid` 97 | template 98 | T &addProperty(const std::string &name, const GattUuid &uuid, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr) 99 | { 100 | return addProperty(GattProperty(name, Utils::gvariantFromString(uuid.toString128().c_str()), getter, setter)); 101 | } 102 | 103 | // Helper method for adding a named property with a `DBusObjectPath` 104 | template 105 | T &addProperty(const std::string &name, const DBusObjectPath &path, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr) 106 | { 107 | return addProperty(GattProperty(name, Utils::gvariantFromObject(path), getter, setter)); 108 | } 109 | 110 | // Helper method for adding a named property with a std::strings 111 | template 112 | T &addProperty(const std::string &name, const std::string &str, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr) 113 | { 114 | return addProperty(GattProperty(name, Utils::gvariantFromString(str), getter, setter)); 115 | } 116 | 117 | // Helper method for adding a named property with an array of std::strings 118 | template 119 | T &addProperty(const std::string &name, const std::vector &arr, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr) 120 | { 121 | return addProperty(GattProperty(name, Utils::gvariantFromStringArray(arr), getter, setter)); 122 | } 123 | 124 | // Helper method for adding a named property with an array of C strings 125 | template 126 | T &addProperty(const std::string &name, const std::vector &arr, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr) 127 | { 128 | return addProperty(GattProperty(name, Utils::gvariantFromStringArray(arr), getter, setter)); 129 | } 130 | 131 | // Helper method for adding a named property with a given C string 132 | template 133 | T &addProperty(const std::string &name, const char *pStr, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr) 134 | { 135 | return addProperty(GattProperty(name, Utils::gvariantFromString(pStr), getter, setter)); 136 | } 137 | 138 | // Helper method for adding a named property with a given boolean value 139 | template 140 | T &addProperty(const std::string &name, bool value, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr) 141 | { 142 | return addProperty(GattProperty(name, Utils::gvariantFromBoolean(value), getter, setter)); 143 | } 144 | 145 | // Return a data value from the server's registered data getter (GGKServerDataGetter) 146 | // 147 | // This method is for use with non-pointer types. For pointer types, use `getDataPointer()` instead. 148 | // 149 | // This method is intended to be used in the server description. An example usage would be: 150 | // 151 | // uint8_t batteryLevel = self.getDataValue("battery/level", 0); 152 | template 153 | T getDataValue(const char *pName, const T defaultValue) const 154 | { 155 | const void *pData = TheServer->getDataGetter()(pName); 156 | return nullptr == pData ? defaultValue : *static_cast(pData); 157 | } 158 | 159 | // Return a data pointer from the server's registered data getter (GGKServerDataGetter) 160 | // 161 | // This method is for use with pointer types. For non-pointer types, use `getDataValue()` instead. 162 | // 163 | // This method is intended to be used in the server description. An example usage would be: 164 | // 165 | // const char *pTextString = self.getDataPointer("text/string", ""); 166 | template 167 | T getDataPointer(const char *pName, const T defaultValue) const 168 | { 169 | const void *pData = TheServer->getDataGetter()(pName); 170 | return nullptr == pData ? defaultValue : static_cast(pData); 171 | } 172 | 173 | // Sends a data value from the server back to the application through the server's registered data setter 174 | // (GGKServerDataSetter) 175 | // 176 | // This method is for use with non-pointer types. For pointer types, use `setDataPointer()` instead. 177 | // 178 | // This method is intended to be used in the server description. An example usage would be: 179 | // 180 | // self.setDataValue("battery/level", batteryLevel); 181 | template 182 | bool setDataValue(const char *pName, const T value) const 183 | { 184 | return TheServer->getDataSetter()(pName, static_cast(&value)) != 0; 185 | } 186 | 187 | // Sends a data pointer from the server back to the application through the server's registered data setter 188 | // (GGKServerDataSetter) 189 | // 190 | // This method is for use with pointer types. For non-pointer types, use `setDataValue()` instead. 191 | // 192 | // This method is intended to be used in the server description. An example usage would be: 193 | // 194 | // self.setDataPointer("text/string", stringFromGVariantByteArray(pAyBuffer).c_str()); 195 | template 196 | bool setDataPointer(const char *pName, const T pointer) const 197 | { 198 | return TheServer->getDataSetter()(pName, static_cast(pointer)) != 0; 199 | } 200 | 201 | // When responding to a ReadValue method, we need to return a GVariant value in the form "(ay)" (a tuple containing an array of 202 | // bytes). This method will simplify this slightly by wrapping a GVariant of the type "ay" and wrapping it in a tuple before 203 | // sending it off as the method response. 204 | // 205 | // This is the generalized form that accepts a GVariant *. There is a templated helper method (`methodReturnValue()`) that accepts 206 | // common types. 207 | void methodReturnVariant(GDBusMethodInvocation *pInvocation, GVariant *pVariant, bool wrapInTuple = false) const; 208 | 209 | // When responding to a ReadValue method, we need to return a GVariant value in the form "(ay)" (a tuple containing an array of 210 | // bytes). This method will simplify this slightly by wrapping a GVariant of the type "ay" and wrapping it in a tuple before 211 | // sending it off as the method response. 212 | // 213 | // This is a templated helper method that only works with common types. For a more generic form which can be used for custom 214 | // types, see `methodReturnVariant()'. 215 | template 216 | void methodReturnValue(GDBusMethodInvocation *pInvocation, T value, bool wrapInTuple = false) const 217 | { 218 | GVariant *pVariant = Utils::gvariantFromByteArray(value); 219 | methodReturnVariant(pInvocation, pVariant, wrapInTuple); 220 | } 221 | 222 | // Locates a `GattProperty` within the interface 223 | // 224 | // This method returns a pointer to the property or nullptr if not found 225 | const GattProperty *findProperty(const std::string &name) const; 226 | 227 | // Internal method used to generate introspection XML used to describe our services on D-Bus 228 | virtual std::string generateIntrospectionXML(int depth) const; 229 | 230 | protected: 231 | 232 | std::list properties; 233 | }; 234 | 235 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattProperty.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // A GATT Property is simply a name/value pair. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // This class is intended to be used within the server description. For an explanation of how this class is used, see the detailed 31 | // description in Server.cpp. 32 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33 | 34 | #include 35 | #include 36 | 37 | #include "Utils.h" 38 | #include "GattProperty.h" 39 | 40 | namespace ggk { 41 | 42 | // Constructs a named property 43 | // 44 | // In general, properties should not be constructed directly as properties are typically instanticated by adding them to to an 45 | // interface using one of the the interface's `addProperty` methods. 46 | GattProperty::GattProperty(const std::string &name, GVariant *pValue, GDBusInterfaceGetPropertyFunc getter, GDBusInterfaceSetPropertyFunc setter) 47 | : name(name), pValue(pValue), getterFunc(getter), setterFunc(setter) 48 | { 49 | } 50 | 51 | // 52 | // Name 53 | // 54 | 55 | // Returns the name of the property 56 | const std::string &GattProperty::getName() const 57 | { 58 | return name; 59 | } 60 | 61 | // Sets the name of the property 62 | // 63 | // In general, this method should not be called directly as properties are typically added to an interface using one of the the 64 | // interface's `addProperty` methods. 65 | GattProperty &GattProperty::setName(const std::string &name) 66 | { 67 | this->name = name; 68 | return *this; 69 | } 70 | 71 | // 72 | // Value 73 | // 74 | 75 | // Returns the property's value 76 | const GVariant *GattProperty::getValue() const 77 | { 78 | return pValue; 79 | } 80 | 81 | // Sets the property's value 82 | // 83 | // In general, this method should not be called directly as properties are typically added to an interface using one of the the 84 | // interface's `addProperty` methods. 85 | GattProperty &GattProperty::setValue(GVariant *pValue) 86 | { 87 | this->pValue = pValue; 88 | return *this; 89 | } 90 | 91 | // 92 | // Callbacks to get/set this property 93 | // 94 | 95 | // Internal use method to retrieve the getter delegate method used to return custom values for a property 96 | GDBusInterfaceGetPropertyFunc GattProperty::getGetterFunc() const 97 | { 98 | return getterFunc; 99 | } 100 | 101 | // Internal use method to set the getter delegate method used to return custom values for a property 102 | // 103 | // In general, this method should not be called directly as properties are typically added to an interface using one of the the 104 | // interface's `addProperty` methods. 105 | GattProperty &GattProperty::setGetterFunc(GDBusInterfaceGetPropertyFunc func) 106 | { 107 | getterFunc = func; 108 | return *this; 109 | } 110 | 111 | // Internal use method to retrieve the setter delegate method used to return custom values for a property 112 | GDBusInterfaceSetPropertyFunc GattProperty::getSetterFunc() const 113 | { 114 | return setterFunc; 115 | } 116 | 117 | // Internal use method to set the setter delegate method used to return custom values for a property 118 | // 119 | // In general, this method should not be called directly as properties are typically added to an interface using one of the the 120 | // interface's `addProperty` methods. 121 | GattProperty &GattProperty::setSetterFunc(GDBusInterfaceSetPropertyFunc func) 122 | { 123 | setterFunc = func; 124 | return *this; 125 | } 126 | 127 | // Internal method used to generate introspection XML used to describe our services on D-Bus 128 | std::string GattProperty::generateIntrospectionXML(int depth) const 129 | { 130 | std::string prefix; 131 | prefix.insert(0, depth * 2, ' '); 132 | 133 | std::string xml = std::string(); 134 | 135 | GVariant *pValue = const_cast(getValue()); 136 | const gchar *pType = g_variant_get_type_string(pValue); 137 | xml += prefix + "\n"; 138 | 139 | if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_BOOLEAN)) 140 | { 141 | xml += prefix + " \n"; 142 | } 143 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_INT16)) 144 | { 145 | xml += prefix + " \n"; 146 | } 147 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_UINT16)) 148 | { 149 | xml += prefix + " \n"; 150 | } 151 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_INT32)) 152 | { 153 | xml += prefix + " \n"; 154 | } 155 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_UINT32)) 156 | { 157 | xml += prefix + " \n"; 158 | } 159 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_INT64)) 160 | { 161 | xml += prefix + " \n"; 162 | } 163 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_UINT64)) 164 | { 165 | xml += prefix + " \n"; 166 | } 167 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_DOUBLE)) 168 | { 169 | xml += prefix + " \n"; 170 | } 171 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_STRING)) 172 | { 173 | xml += prefix + " \n"; 174 | } 175 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_OBJECT_PATH)) 176 | { 177 | xml += prefix + " \n"; 178 | } 179 | else if (g_variant_is_of_type(pValue, G_VARIANT_TYPE_BYTESTRING)) 180 | { 181 | xml += prefix + " \n"; 182 | } 183 | 184 | xml += prefix + "\n"; 185 | 186 | return xml; 187 | } 188 | 189 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattProperty.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // A GATT Property is simply a name/value pair. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussion at the top of GattProperty.cpp 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | 38 | namespace ggk { 39 | 40 | struct DBusObjectPath; 41 | 42 | // Representation of a GATT Property 43 | struct GattProperty 44 | { 45 | // Constructs a named property 46 | // 47 | // In general, properties should not be constructed directly as properties are typically instanticated by adding them to to an 48 | // interface using one of the the interface's `addProperty` methods. 49 | GattProperty(const std::string &name, GVariant *pValue, GDBusInterfaceGetPropertyFunc getter = nullptr, GDBusInterfaceSetPropertyFunc setter = nullptr); 50 | 51 | // 52 | // Name 53 | // 54 | 55 | // Returns the name of the property 56 | const std::string &getName() const; 57 | 58 | // Sets the name of the property 59 | // 60 | // In general, this method should not be called directly as properties are typically added to an interface using one of the the 61 | // interface's `addProperty` methods. 62 | GattProperty &setName(const std::string &name); 63 | 64 | // 65 | // Value 66 | // 67 | 68 | // Returns the property's value 69 | const GVariant *getValue() const; 70 | 71 | // Sets the property's value 72 | // 73 | // In general, this method should not be called directly as properties are typically added to an interface using one of the the 74 | // interface's `addProperty` methods. 75 | GattProperty &setValue(GVariant *pValue); 76 | 77 | // 78 | // Callbacks to get/set this property 79 | // 80 | 81 | // Internal use method to retrieve the getter delegate method used to return custom values for a property 82 | GDBusInterfaceGetPropertyFunc getGetterFunc() const; 83 | 84 | // Internal use method to set the getter delegate method used to return custom values for a property 85 | // 86 | // In general, this method should not be called directly as properties are typically added to an interface using one of the the 87 | // interface's `addProperty` methods. 88 | GattProperty &setGetterFunc(GDBusInterfaceGetPropertyFunc func); 89 | 90 | // Internal use method to retrieve the setter delegate method used to return custom values for a property 91 | GDBusInterfaceSetPropertyFunc getSetterFunc() const; 92 | 93 | // Internal use method to set the setter delegate method used to return custom values for a property 94 | // 95 | // In general, this method should not be called directly as properties are typically added to an interface using one of the the 96 | // interface's `addProperty` methods. 97 | GattProperty &setSetterFunc(GDBusInterfaceSetPropertyFunc func); 98 | 99 | // Internal method used to generate introspection XML used to describe our services on D-Bus 100 | std::string generateIntrospectionXML(int depth) const; 101 | 102 | private: 103 | 104 | std::string name; 105 | GVariant *pValue; 106 | GDBusInterfaceGetPropertyFunc getterFunc; 107 | GDBusInterfaceSetPropertyFunc setterFunc; 108 | }; 109 | 110 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattService.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // A GATT Service, used to add services to a Bluetooth server 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // A GATT Service is really a collection of 31 | // 32 | // This class is intended to be used within the server description. For an explanation of how this class is used, see the detailed 33 | // description in Server.cpp. 34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #include "GattService.h" 41 | #include "GattInterface.h" 42 | #include "DBusObject.h" 43 | #include "GattCharacteristic.h" 44 | 45 | namespace ggk { 46 | 47 | // --------------------------------------------------------------------------------------------------------------------------------- 48 | // Representation of a Bluetooth GATT Service 49 | // --------------------------------------------------------------------------------------------------------------------------------- 50 | 51 | // Standard constructor 52 | GattService::GattService(DBusObject &owner, const std::string &name) 53 | : GattInterface(owner, name) 54 | { 55 | } 56 | 57 | // Returning the parent pops us one level up the hierarchy 58 | DBusObject &GattService::gattServiceEnd() 59 | { 60 | return getOwner().getParent(); 61 | } 62 | 63 | // Convenience functions to add a GATT characteristic to the hierarchy 64 | // 65 | // We simply add a new child at the given path and add an interface configured as a GATT characteristic to it. The 66 | // new characteristic is declared with a UUID and a variable argument list of flags (in string form.) For a complete and 67 | // up-to-date list of flag values, see: https://git.kernel.org/pub/scm/bluetooth/bluez.git/plain/doc/gatt-api.txt 68 | // 69 | // At the time of this writing, the list of flags is as follows: 70 | // 71 | // "broadcast" 72 | // "read" 73 | // "write-without-response" 74 | // "write" 75 | // "notify" 76 | // "indicate" 77 | // "authenticated-signed-writes" 78 | // "reliable-write" 79 | // "writable-auxiliaries" 80 | // "encrypt-read" 81 | // "encrypt-write" 82 | // "encrypt-authenticated-read" 83 | // "encrypt-authenticated-write" 84 | // "secure-read" (Server only) 85 | // "secure-write" (Server only) 86 | // 87 | GattCharacteristic &GattService::gattCharacteristicBegin(const std::string &pathElement, const GattUuid &uuid, const std::vector &flags) 88 | { 89 | DBusObject &child = owner.addChild(DBusObjectPath(pathElement)); 90 | GattCharacteristic &characteristic = *child.addInterface(std::make_shared(child, *this, "org.bluez.GattCharacteristic1")); 91 | characteristic.addProperty("UUID", uuid); 92 | characteristic.addProperty("Service", owner.getPath()); 93 | characteristic.addProperty("Flags", flags); 94 | return characteristic; 95 | } 96 | 97 | }; // namespace ggk -------------------------------------------------------------------------------- /src/GattService.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // A GATT Service, used to add services to a Bluetooth server 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // A GATT Service is really a collection of 31 | // 32 | // This class is intended to be used within the server description. For an explanation of how this class is used, see the detailed 33 | // description in Server.cpp. 34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #include "TickEvent.h" 43 | #include "GattInterface.h" 44 | 45 | namespace ggk { 46 | 47 | // --------------------------------------------------------------------------------------------------------------------------------- 48 | // Forward declarations 49 | // --------------------------------------------------------------------------------------------------------------------------------- 50 | 51 | struct GattService; 52 | struct GattCharacteristic; 53 | struct GattProperty; 54 | struct DBusObject; 55 | 56 | // --------------------------------------------------------------------------------------------------------------------------------- 57 | // Representation of a Bluetooth GATT Service 58 | // --------------------------------------------------------------------------------------------------------------------------------- 59 | 60 | struct GattService : GattInterface 61 | { 62 | // Our interface type 63 | static constexpr const char *kInterfaceType = "GattService"; 64 | 65 | // Standard constructor 66 | GattService(DBusObject &owner, const std::string &name); 67 | 68 | virtual ~GattService() {} 69 | 70 | // Returning the parent pops us one level up the hierarchy 71 | DBusObject &gattServiceEnd(); 72 | 73 | // Convenience functions to add a GATT characteristic to the hierarchy 74 | // 75 | // We simply add a new child at the given path and add an interface configured as a GATT characteristic to it. The 76 | // new characteristic is declared with a UUID and a variable argument list of flags (in string form.) For a complete and 77 | // up-to-date list of flag values, see: https://git.kernel.org/pub/scm/bluetooth/bluez.git/plain/doc/gatt-api.txt 78 | // 79 | // At the time of this writing, the list of flags is as follows: 80 | // 81 | // "broadcast" 82 | // "read" 83 | // "write-without-response" 84 | // "write" 85 | // "notify" 86 | // "indicate" 87 | // "authenticated-signed-writes" 88 | // "reliable-write" 89 | // "writable-auxiliaries" 90 | // "encrypt-read" 91 | // "encrypt-write" 92 | // "encrypt-authenticated-read" 93 | // "encrypt-authenticated-write" 94 | // "secure-read" (Server only) 95 | // "secure-write" (Server only) 96 | // 97 | GattCharacteristic &gattCharacteristicBegin(const std::string &pathElement, const GattUuid &uuid, const std::vector &flags); 98 | 99 | // Returns a string identifying the type of interface 100 | virtual const std::string getInterfaceType() const { return GattService::kInterfaceType; } 101 | }; 102 | 103 | }; // namespace ggk -------------------------------------------------------------------------------- /src/Globals.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // System-wide globals. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // The globals below define the name of the server, along with any name-based items (such as errors.) 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | #include 36 | 37 | // 38 | // Custom defined errors 39 | // 40 | 41 | // In order to avoid confusion, we should use the owned name here, so errors are like extensions to that name. This way, if a 42 | // client gets one of these errors, it'll be clear which server it came from. 43 | #define kErrorNotImplemented (TheServer->getOwnedName() + ".NotImplemented") 44 | -------------------------------------------------------------------------------- /src/HciSocket.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // Low-level socket interface used to communicate with the Bluetooth Management API (see HciAdapter.cpp) 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // This class intended to support `HciAdapter` - see HciAdapter.cpp for more information. 31 | // 32 | // This file contains raw communication layer for the Bluetooth Management API, which is used to configure the Bluetooth adapter 33 | // (such as enabling LE, setting the device name, etc.) This class is used by HciAdapter (HciAdapter.h) to perform higher-level 34 | // functions. 35 | // 36 | // This code is for example purposes only. If you plan to use this in a production environment, I suggest rewriting it. 37 | // 38 | // The information for this implementation (as well as HciAdapter.h) came from: 39 | // 40 | // https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/mgmt-api.txt 41 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "HciSocket.h" 49 | #include "Logger.h" 50 | #include "Utils.h" 51 | 52 | namespace ggk { 53 | 54 | // Initializes an unconnected socket 55 | HciSocket::HciSocket() 56 | : fdSocket(-1) 57 | { 58 | } 59 | 60 | // Socket destructor 61 | // 62 | // This will automatically disconnect the socket if it is currently connected 63 | HciSocket::~HciSocket() 64 | { 65 | disconnect(); 66 | } 67 | 68 | // Connects to an HCI socket using the Bluetooth Management API protocol 69 | // 70 | // Returns true on success, otherwise false 71 | bool HciSocket::connect() 72 | { 73 | disconnect(); 74 | 75 | fdSocket = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI); 76 | if (fdSocket < 0) 77 | { 78 | logErrno("Connect(socket)"); 79 | return false; 80 | } 81 | 82 | struct sockaddr_hci addr; 83 | memset(&addr, 0, sizeof(addr)); 84 | addr.hci_family = AF_BLUETOOTH; 85 | addr.hci_dev = HCI_DEV_NONE; 86 | addr.hci_channel = HCI_CHANNEL_CONTROL; 87 | 88 | if (bind(fdSocket, reinterpret_cast(&addr), sizeof(addr)) < 0) 89 | { 90 | logErrno("Connect(bind)"); 91 | disconnect(); 92 | return false; 93 | } 94 | 95 | Logger::debug(SSTR << "Connected to HCI control socket (fd = " << fdSocket << ")"); 96 | 97 | return true; 98 | } 99 | 100 | // Returns true if the socket is currently connected, otherwise false 101 | bool HciSocket::isConnected() const 102 | { 103 | return fdSocket >= 0; 104 | } 105 | 106 | // Disconnects from the HCI socket 107 | void HciSocket::disconnect() 108 | { 109 | if (isConnected()) 110 | { 111 | Logger::debug("HciSocket disconnecting"); 112 | 113 | if (close(fdSocket) != 0) 114 | { 115 | logErrno("close(fdSocket)"); 116 | } 117 | 118 | fdSocket = -1; 119 | Logger::trace("HciSocket closed"); 120 | } 121 | } 122 | 123 | // Reads data from the HCI socket 124 | // 125 | // Raw data is read and returned in `response`. 126 | // 127 | // Returns true if data was read successfully, otherwise false is returned. A false return code does not necessarily depict 128 | // an error, as this can arise from expected conditions (such as an interrupt.) 129 | bool HciSocket::read(std::vector &response) const 130 | { 131 | // Fill our response with empty data 132 | response.resize(kResponseMaxSize, 0); 133 | 134 | // Wait for data or a cancellation 135 | if (!waitForDataOrShutdown()) 136 | { 137 | return false; 138 | } 139 | 140 | // Block until we receive data, a disconnect, or a signal 141 | ssize_t bytesRead = ::recv(fdSocket, &response[0], kResponseMaxSize, MSG_WAITALL); 142 | 143 | // If there was an error, wipe the data and return an error condition 144 | if (bytesRead < 0) 145 | { 146 | if (errno == EINTR) 147 | { 148 | Logger::debug("HciSocket receive interrupted"); 149 | } 150 | else 151 | { 152 | logErrno("recv"); 153 | } 154 | response.resize(0); 155 | return false; 156 | } 157 | else if (bytesRead == 0) 158 | { 159 | Logger::error("Peer closed the socket"); 160 | response.resize(0); 161 | return false; 162 | } 163 | 164 | // We have data 165 | response.resize(bytesRead); 166 | 167 | std::string dump = ""; 168 | dump += " > Read " + std::to_string(response.size()) + " bytes\n"; 169 | dump += Utils::hex(response.data(), response.size()); 170 | Logger::debug(dump); 171 | 172 | return true; 173 | } 174 | 175 | // Writes the array of bytes of a given count 176 | // 177 | // This method returns true if the bytes were written successfully, otherwise false 178 | bool HciSocket::write(std::vector buffer) const 179 | { 180 | return write(buffer.data(), buffer.size()); 181 | } 182 | 183 | // Writes the array of bytes of a given count 184 | // 185 | // This method returns true if the bytes were written successfully, otherwise false 186 | bool HciSocket::write(const uint8_t *pBuffer, size_t count) const 187 | { 188 | std::string dump = ""; 189 | dump += " > Writing " + std::to_string(count) + " bytes\n"; 190 | dump += Utils::hex(pBuffer, count); 191 | Logger::debug(dump); 192 | 193 | size_t len = ::write(fdSocket, pBuffer, count); 194 | 195 | if (len != count) 196 | { 197 | logErrno("write"); 198 | return false; 199 | } 200 | 201 | return true; 202 | } 203 | 204 | // Wait for data to arrive, or for a shutdown event 205 | // 206 | // Returns true if data is available, false if we are shutting down 207 | bool HciSocket::waitForDataOrShutdown() const 208 | { 209 | while(ggkIsServerRunning()) 210 | { 211 | fd_set rfds; 212 | FD_ZERO(&rfds); 213 | FD_SET(fdSocket, &rfds); 214 | 215 | struct timeval tv; 216 | tv.tv_sec = 0; 217 | tv.tv_usec = kDataWaitTimeMS * 1000; 218 | 219 | int retval = select(fdSocket+1, &rfds, NULL, NULL, &tv); 220 | 221 | // Do we have data? 222 | if (retval > 0) { return true; } 223 | 224 | // We have an error 225 | if (retval < 0) { return false; } 226 | 227 | // No data; keep waiting 228 | continue; 229 | } 230 | 231 | return false; 232 | } 233 | 234 | // Utilitarian function for logging errors for the given operation 235 | void HciSocket::logErrno(const char *pOperation) const 236 | { 237 | std::string errorDetail(strerror(errno)); 238 | if (errno == EAGAIN) 239 | { 240 | errorDetail += " or not enough permission for this operation"; 241 | } 242 | 243 | Logger::error(SSTR << "Error on Bluetooth management socket during " << pOperation << " operation. Error code " << errno << ": " << errorDetail); 244 | } 245 | 246 | }; // namespace ggk 247 | -------------------------------------------------------------------------------- /src/HciSocket.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // Low-level socket interface used to communicate with the Bluetooth Management API (see HciAdapter.cpp) 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussion at the top of HciSocket.cpp 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | namespace ggk { 40 | 41 | class HciSocket 42 | { 43 | public: 44 | // Initializes an unconnected socket 45 | HciSocket(); 46 | 47 | // Socket destructor 48 | // 49 | // This will automatically disconnect the socket if it is currently connected 50 | ~HciSocket(); 51 | 52 | // Connects to an HCI socket using the Bluetooth Management API protocol 53 | // 54 | // Returns true on success, otherwise false 55 | bool connect(); 56 | 57 | // Returns true if the socket is currently connected, otherwise false 58 | bool isConnected() const; 59 | 60 | // Disconnects from the HCI socket 61 | void disconnect(); 62 | 63 | // Reads data from the HCI socket 64 | // 65 | // Raw data is read until no more data is available. If no data is available when this method initially starts to read, it will 66 | // retry for a maximum timeout defined by `kMaxRetryTimeMS`. 67 | // 68 | // Returns true if any data was read successfully, otherwise false is returned in the case of an error or a timeout. 69 | bool read(std::vector &response) const; 70 | 71 | // Writes the array of bytes of a given count 72 | // 73 | // This method returns true if the bytes were written successfully, otherwise false 74 | bool write(std::vector buffer) const; 75 | 76 | // Writes the array of bytes of a given count 77 | // 78 | // This method returns true if the bytes were written successfully, otherwise false 79 | bool write(const uint8_t *pBuffer, size_t count) const; 80 | 81 | private: 82 | 83 | // Wait for data to arrive, or for a shutdown event 84 | // 85 | // Returns true if data is available, false if we are shutting down 86 | bool waitForDataOrShutdown() const; 87 | 88 | // Utilitarian function for logging errors for the given operation 89 | void logErrno(const char *pOperation) const; 90 | 91 | int fdSocket; 92 | 93 | const size_t kResponseMaxSize = 64 * 1024; 94 | const int kDataWaitTimeMS = 10; 95 | }; 96 | 97 | }; // namespace ggk -------------------------------------------------------------------------------- /src/Init.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // Herein lies the code that manages the full initialization (including the running) of the server 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // See the discussion at the top of Init.cpp 31 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 32 | 33 | #pragma once 34 | 35 | namespace ggk { 36 | 37 | // Trigger a graceful, asynchronous shutdown of the server 38 | // 39 | // This method is non-blocking and as such, will only trigger the shutdown process but not wait for it 40 | void shutdown(); 41 | 42 | // Entry point for the asynchronous server thread 43 | // 44 | // This method should not be called directly, instead, direct your attention over to `ggkStart()` 45 | void runServerThread(); 46 | 47 | }; // namespace ggk -------------------------------------------------------------------------------- /src/Logger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is our Logger, which allows for applications to use their own logging mechanisms by registering log receivers for each of 25 | // the logging categories. 26 | // 27 | // >> 28 | // >>> DISCUSSION 29 | // >> 30 | // 31 | // In order for logging to work, the application must register logging receivers with the `ggkLogRegister*` methods. At that point, 32 | // it is up to the application to decide how to handle the incoming log requests. For example, it may filter Error and Fatal logs 33 | // to stderr. 34 | // 35 | // The following is a breakdown of the intended use of each of the available categories. Application receivers can filter then 36 | // (enable/ disable/redirect/etc.) in whatever way works best (via bitmasks, log levels, etc.) 37 | // 38 | // Debug - Lowest logging level; may contain log-spam 39 | // Info - The logging equivalent to the junk drawer 40 | // Status - Current health/service availability info (ex: "Server starting", "Initialization complete", "Stop requested") 41 | // Warn - Problems that may or may not affect functionality 42 | // Error - Problems that will affect functionality, but do not result in a termination of the process 43 | // Fatal - Issues that so severe as to require termination 44 | // 45 | // The following log categories should be considered very important (and possibly never filtered): 46 | // 47 | // Always - Use when something absolutely, positively has to show up in the log output 48 | // Trace - Useful for tracing through code (ex: "Before call to foo()", "Entering function bar") and other development use 49 | // only and should be removed before a release. 50 | // 51 | // Using the logger is simple: 52 | // 53 | // Logger::error("Unable to locate configuration file (this is probably bad)"); 54 | // 55 | // There is an additional macro (SSTR) which can simplify sending dynamic data to the logger via a string stream: 56 | // 57 | // Logger::info(SSTR << "There were " << count << " entries in the list"); 58 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 59 | 60 | #include "Logger.h" 61 | 62 | namespace ggk { 63 | 64 | // 65 | // Log receiver delegates 66 | // 67 | 68 | // The registered log receiver for DEBUG logs - a nullptr will cause the logging for that receiver to be ignored 69 | GGKLogReceiver Logger::logReceiverDebug = nullptr; 70 | 71 | // The registered log receiver for INFO logs - a nullptr will cause the logging for that receiver to be ignored 72 | GGKLogReceiver Logger::logReceiverInfo = nullptr; 73 | 74 | // The registered log receiver for STATUS logs - a nullptr will cause the logging for that receiver to be ignored 75 | GGKLogReceiver Logger::logReceiverStatus = nullptr; 76 | 77 | // The registered log receiver for WARN logs - a nullptr will cause the logging for that receiver to be ignored 78 | GGKLogReceiver Logger::logReceiverWarn = nullptr; 79 | 80 | // The registered log receiver for ERROR logs - a nullptr will cause the logging for that receiver to be ignored 81 | GGKLogReceiver Logger::logReceiverError = nullptr; 82 | 83 | // The registered log receiver for FATAL logs - a nullptr will cause the logging for that receiver to be ignored 84 | GGKLogReceiver Logger::logReceiverFatal = nullptr; 85 | 86 | // The registered log receiver for ALWAYS logs - a nullptr will cause the logging for that receiver to be ignored 87 | GGKLogReceiver Logger::logReceiverAlways = nullptr; 88 | 89 | // The registered log receiver for TRACE logs - a nullptr will cause the logging for that receiver to be ignored 90 | GGKLogReceiver Logger::logReceiverTrace = nullptr; 91 | 92 | // 93 | // Registration 94 | // 95 | 96 | // Register logging receiver for DEBUG logging. To register a logging level, simply call with a delegate that performs the 97 | // appropriate logging action. To unregister, call with `nullptr` 98 | void Logger::registerDebugReceiver(GGKLogReceiver receiver) { Logger::logReceiverDebug = receiver; } 99 | 100 | // Register logging receiver for INFO logging. To register a logging level, simply call with a delegate that performs the 101 | // appropriate logging action. To unregister, call with `nullptr` 102 | void Logger::registerInfoReceiver(GGKLogReceiver receiver) { Logger::logReceiverInfo = receiver; } 103 | 104 | // Register logging receiver for STATUS logging. To register a logging level, simply call with a delegate that performs the 105 | // appropriate logging action. To unregister, call with `nullptr` 106 | void Logger::registerStatusReceiver(GGKLogReceiver receiver) { Logger::logReceiverStatus = receiver; } 107 | 108 | // Register logging receiver for WARN logging. To register a logging level, simply call with a delegate that performs the 109 | // appropriate logging action. To unregister, call with `nullptr` 110 | void Logger::registerWarnReceiver(GGKLogReceiver receiver) { Logger::logReceiverWarn = receiver; } 111 | 112 | // Register logging receiver for ERROR logging. To register a logging level, simply call with a delegate that performs the 113 | // appropriate logging action. To unregister, call with `nullptr` 114 | void Logger::registerErrorReceiver(GGKLogReceiver receiver) { Logger::logReceiverError = receiver; } 115 | 116 | // Register logging receiver for FATAL logging. To register a logging level, simply call with a delegate that performs the 117 | // appropriate logging action. To unregister, call with `nullptr` 118 | void Logger::registerFatalReceiver(GGKLogReceiver receiver) { Logger::logReceiverFatal = receiver; } 119 | 120 | // Register logging receiver for ALWAYS logging. To register a logging level, simply call with a delegate that performs the 121 | // appropriate logging action. To unregister, call with `nullptr` 122 | void Logger::registerAlwaysReceiver(GGKLogReceiver receiver) { Logger::logReceiverAlways = receiver; } 123 | 124 | // Register logging receiver for TRACE logging. To register a logging level, simply call with a delegate that performs the 125 | // appropriate logging action. To unregister, call with `nullptr` 126 | void Logger::registerTraceReceiver(GGKLogReceiver receiver) { Logger::logReceiverTrace = receiver; } 127 | 128 | // 129 | // Logging actions 130 | // 131 | 132 | // Log a DEBUG entry with a C string 133 | void Logger::debug(const char *pText) { if (nullptr != Logger::logReceiverDebug) { Logger::logReceiverDebug(pText); } } 134 | 135 | // Log a DEBUG entry with a string 136 | void Logger::debug(const std::string &text) { if (nullptr != Logger::logReceiverDebug) { debug(text.c_str()); } } 137 | 138 | // Log a DEBUG entry using a stream 139 | void Logger::debug(const std::ostream &text) { if (nullptr != Logger::logReceiverDebug) { debug(static_cast(text).str().c_str()); } } 140 | 141 | // Log a INFO entry with a C string 142 | void Logger::info(const char *pText) { if (nullptr != Logger::logReceiverInfo) { Logger::logReceiverInfo(pText); } } 143 | 144 | // Log a INFO entry with a string 145 | void Logger::info(const std::string &text) { if (nullptr != Logger::logReceiverInfo) { info(text.c_str()); } } 146 | 147 | // Log a INFO entry using a stream 148 | void Logger::info(const std::ostream &text) { if (nullptr != Logger::logReceiverInfo) { info(static_cast(text).str().c_str()); } } 149 | 150 | // Log a STATUS entry with a C string 151 | void Logger::status(const char *pText) { if (nullptr != Logger::logReceiverStatus) { Logger::logReceiverStatus(pText); } } 152 | 153 | // Log a STATUS entry with a string 154 | void Logger::status(const std::string &text) { if (nullptr != Logger::logReceiverStatus) { status(text.c_str()); } } 155 | 156 | // Log a STATUS entry using a stream 157 | void Logger::status(const std::ostream &text) { if (nullptr != Logger::logReceiverStatus) { status(static_cast(text).str().c_str()); } } 158 | 159 | // Log a WARN entry with a C string 160 | void Logger::warn(const char *pText) { if (nullptr != Logger::logReceiverWarn) { Logger::logReceiverWarn(pText); } } 161 | 162 | // Log a WARN entry with a string 163 | void Logger::warn(const std::string &text) { if (nullptr != Logger::logReceiverWarn) { warn(text.c_str()); } } 164 | 165 | // Log a WARN entry using a stream 166 | void Logger::warn(const std::ostream &text) { if (nullptr != Logger::logReceiverWarn) { warn(static_cast(text).str().c_str()); } } 167 | 168 | // Log a ERROR entry with a C string 169 | void Logger::error(const char *pText) { if (nullptr != Logger::logReceiverError) { Logger::logReceiverError(pText); } } 170 | 171 | // Log a ERROR entry with a string 172 | void Logger::error(const std::string &text) { if (nullptr != Logger::logReceiverError) { error(text.c_str()); } } 173 | 174 | // Log a ERROR entry using a stream 175 | void Logger::error(const std::ostream &text) { if (nullptr != Logger::logReceiverError) { error(static_cast(text).str().c_str()); } } 176 | 177 | // Log a FATAL entry with a C string 178 | void Logger::fatal(const char *pText) { if (nullptr != Logger::logReceiverFatal) { Logger::logReceiverFatal(pText); } } 179 | 180 | // Log a FATAL entry with a string 181 | void Logger::fatal(const std::string &text) { if (nullptr != Logger::logReceiverFatal) { fatal(text.c_str()); } } 182 | 183 | // Log a FATAL entry using a stream 184 | void Logger::fatal(const std::ostream &text) { if (nullptr != Logger::logReceiverFatal) { fatal(static_cast(text).str().c_str()); } } 185 | 186 | // Log a ALWAYS entry with a C string 187 | void Logger::always(const char *pText) { if (nullptr != Logger::logReceiverAlways) { Logger::logReceiverAlways(pText); } } 188 | 189 | // Log a ALWAYS entry with a string 190 | void Logger::always(const std::string &text) { if (nullptr != Logger::logReceiverAlways) { always(text.c_str()); } } 191 | 192 | // Log a ALWAYS entry using a stream 193 | void Logger::always(const std::ostream &text) { if (nullptr != Logger::logReceiverAlways) { always(static_cast(text).str().c_str()); } } 194 | 195 | // Log a TRACE entry with a C string 196 | void Logger::trace(const char *pText) { if (nullptr != Logger::logReceiverTrace) { Logger::logReceiverTrace(pText); } } 197 | 198 | // Log a TRACE entry with a string 199 | void Logger::trace(const std::string &text) { if (nullptr != Logger::logReceiverTrace) { trace(text.c_str()); } } 200 | 201 | // Log a TRACE entry using a stream 202 | void Logger::trace(const std::ostream &text) { if (nullptr != Logger::logReceiverTrace) { trace(static_cast(text).str().c_str()); } } 203 | 204 | }; // namespace ggk -------------------------------------------------------------------------------- /src/Logger.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is our Logger, which allows for applications to use their own logging mechanisms by registering log receivers for each of 25 | // the logging categories. 26 | // 27 | // >> 28 | // >>> DISCUSSION 29 | // >> 30 | // 31 | // See the discussion at the top of Logger.cpp 32 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 33 | 34 | #pragma once 35 | 36 | #include 37 | 38 | #include "../include/Gobbledegook.h" 39 | 40 | namespace ggk { 41 | 42 | // Our handy stringstream macro 43 | #define SSTR std::ostringstream().flush() 44 | 45 | class Logger 46 | { 47 | public: 48 | 49 | // 50 | // Registration 51 | // 52 | 53 | // Register logging receiver for DEBUG logging. To register a logging level, simply call with a delegate that performs the 54 | // appropriate logging action. To unregister, call with `nullptr` 55 | static void registerDebugReceiver(GGKLogReceiver receiver); 56 | 57 | // Register logging receiver for INFO logging. To register a logging level, simply call with a delegate that performs the 58 | // appropriate logging action. To unregister, call with `nullptr` 59 | static void registerInfoReceiver(GGKLogReceiver receiver); 60 | 61 | // Register logging receiver for STATUS logging. To register a logging level, simply call with a delegate that performs the 62 | // appropriate logging action. To unregister, call with `nullptr` 63 | static void registerStatusReceiver(GGKLogReceiver receiver); 64 | 65 | // Register logging receiver for WARN logging. To register a logging level, simply call with a delegate that performs the 66 | // appropriate logging action. To unregister, call with `nullptr` 67 | static void registerWarnReceiver(GGKLogReceiver receiver); 68 | 69 | // Register logging receiver for ERROR logging. To register a logging level, simply call with a delegate that performs the 70 | // appropriate logging action. To unregister, call with `nullptr` 71 | static void registerErrorReceiver(GGKLogReceiver receiver); 72 | 73 | // Register logging receiver for FATAL logging. To register a logging level, simply call with a delegate that performs the 74 | // appropriate logging action. To unregister, call with `nullptr` 75 | static void registerFatalReceiver(GGKLogReceiver receiver); 76 | 77 | // Register logging receiver for ALWAYS logging. To register a logging level, simply call with a delegate that performs the 78 | // appropriate logging action. To unregister, call with `nullptr` 79 | static void registerAlwaysReceiver(GGKLogReceiver receiver); 80 | 81 | // Register logging receiver for TRACE logging. To register a logging level, simply call with a delegate that performs the 82 | // appropriate logging action. To unregister, call with `nullptr` 83 | static void registerTraceReceiver(GGKLogReceiver receiver); 84 | 85 | 86 | // 87 | // Logging actions 88 | // 89 | 90 | // Log a DEBUG entry with a C string 91 | static void debug(const char *pText); 92 | 93 | // Log a DEBUG entry with a string 94 | static void debug(const std::string &text); 95 | 96 | // Log a DEBUG entry using a stream 97 | static void debug(const std::ostream &text); 98 | 99 | // Log a INFO entry with a C string 100 | static void info(const char *pText); 101 | 102 | // Log a INFO entry with a string 103 | static void info(const std::string &text); 104 | 105 | // Log a INFO entry using a stream 106 | static void info(const std::ostream &text); 107 | 108 | // Log a STATUS entry with a C string 109 | static void status(const char *pText); 110 | 111 | // Log a STATUS entry with a string 112 | static void status(const std::string &text); 113 | 114 | // Log a STATUS entry using a stream 115 | static void status(const std::ostream &text); 116 | 117 | // Log a WARN entry with a C string 118 | static void warn(const char *pText); 119 | 120 | // Log a WARN entry with a string 121 | static void warn(const std::string &text); 122 | 123 | // Log a WARN entry using a stream 124 | static void warn(const std::ostream &text); 125 | 126 | // Log a ERROR entry with a C string 127 | static void error(const char *pText); 128 | 129 | // Log a ERROR entry with a string 130 | static void error(const std::string &text); 131 | 132 | // Log a ERROR entry using a stream 133 | static void error(const std::ostream &text); 134 | 135 | // Log a FATAL entry with a C string 136 | static void fatal(const char *pText); 137 | 138 | // Log a FATAL entry with a string 139 | static void fatal(const std::string &text); 140 | 141 | // Log a FATAL entry using a stream 142 | static void fatal(const std::ostream &text); 143 | 144 | // Log a ALWAYS entry with a C string 145 | static void always(const char *pText); 146 | 147 | // Log a ALWAYS entry with a string 148 | static void always(const std::string &text); 149 | 150 | // Log a ALWAYS entry using a stream 151 | static void always(const std::ostream &text); 152 | 153 | // Log a TRACE entry with a C string 154 | static void trace(const char *pText); 155 | 156 | // Log a TRACE entry with a string 157 | static void trace(const std::string &text); 158 | 159 | // Log a TRACE entry using a stream 160 | static void trace(const std::ostream &text); 161 | 162 | private: 163 | 164 | // The registered log receiver for DEBUG logs - a nullptr will cause the logging for that receiver to be ignored 165 | static GGKLogReceiver logReceiverDebug; 166 | 167 | // The registered log receiver for INFO logs - a nullptr will cause the logging for that receiver to be ignored 168 | static GGKLogReceiver logReceiverInfo; 169 | 170 | // The registered log receiver for STATUS logs - a nullptr will cause the logging for that receiver to be ignored 171 | static GGKLogReceiver logReceiverStatus; 172 | 173 | // The registered log receiver for WARN logs - a nullptr will cause the logging for that receiver to be ignored 174 | static GGKLogReceiver logReceiverWarn; 175 | 176 | // The registered log receiver for ERROR logs - a nullptr will cause the logging for that receiver to be ignored 177 | static GGKLogReceiver logReceiverError; 178 | 179 | // The registered log receiver for FATAL logs - a nullptr will cause the logging for that receiver to be ignored 180 | static GGKLogReceiver logReceiverFatal; 181 | 182 | // The registered log receiver for ALWAYS logs - a nullptr will cause the logging for that receiver to be ignored 183 | static GGKLogReceiver logReceiverAlways; 184 | 185 | // The registered log receiver for TRACE logs - a nullptr will cause the logging for that receiver to be ignored 186 | static GGKLogReceiver logReceiverTrace; 187 | }; 188 | 189 | }; // namespace ggk -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # Build a static library (libggk.a) 2 | noinst_LIBRARIES = libggk.a 3 | libggk_a_CXXFLAGS = -fPIC -Wall -Wextra -std=c++11 $(GLIB_CFLAGS) $(GIO_CFLAGS) $(GOBJECT_CFLAGS) 4 | libggk_a_SOURCES = DBusInterface.cpp \ 5 | DBusInterface.h \ 6 | DBusMethod.cpp \ 7 | DBusMethod.h \ 8 | DBusObject.cpp \ 9 | DBusObject.h \ 10 | DBusObjectPath.h \ 11 | GattCharacteristic.cpp \ 12 | GattCharacteristic.h \ 13 | GattDescriptor.cpp \ 14 | GattDescriptor.h \ 15 | GattInterface.cpp \ 16 | GattInterface.h \ 17 | GattProperty.cpp \ 18 | GattProperty.h \ 19 | GattService.cpp \ 20 | GattService.h \ 21 | GattUuid.h \ 22 | Globals.h \ 23 | Gobbledegook.cpp \ 24 | ../include/Gobbledegook.h \ 25 | HciAdapter.cpp \ 26 | HciAdapter.h \ 27 | HciSocket.cpp \ 28 | HciSocket.h \ 29 | Init.cpp \ 30 | Init.h \ 31 | Logger.cpp \ 32 | Logger.h \ 33 | Mgmt.cpp \ 34 | Mgmt.h \ 35 | Server.cpp \ 36 | Server.h \ 37 | ServerUtils.cpp \ 38 | ServerUtils.h \ 39 | standalone.cpp \ 40 | TickEvent.h \ 41 | Utils.cpp \ 42 | Utils.h 43 | # Build our standalone server (linking statically with libggk.a, linking dynamically with GLib) 44 | standalone_CXXFLAGS = -fPIC -Wall -Wextra -std=c++11 45 | noinst_PROGRAMS = standalone 46 | standalone_SOURCES = standalone.cpp 47 | standalone_LDADD = libggk.a 48 | standalone_LDLIBS = $(GLIB_LIBS) $(GIO_LIBS) $(GOBJECT_LIBS) 49 | -------------------------------------------------------------------------------- /src/Mgmt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This file contains various functions for interacting with Bluetooth Management interface, which provides adapter configuration. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // We only cover the basics here. If there are configuration features you need that aren't supported (such as configuring BR/EDR), 31 | // then this would be a good place for them. 32 | // 33 | // Note that this class relies on the `HciAdapter`, which is a very primitive implementation. Use with caution. 34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | #include 37 | 38 | #include "Mgmt.h" 39 | #include "Logger.h" 40 | #include "Utils.h" 41 | 42 | namespace ggk { 43 | 44 | // Construct the Mgmt device 45 | // 46 | // Set `controllerIndex` to the zero-based index of the device as recognized by the OS. If this parameter is omitted, the index 47 | // of the first device (0) will be used. 48 | Mgmt::Mgmt(uint16_t controllerIndex) 49 | : controllerIndex(controllerIndex) 50 | { 51 | HciAdapter::getInstance().sync(controllerIndex); 52 | } 53 | 54 | // Set the adapter name and short name 55 | // 56 | // The inputs `name` and `shortName` may be truncated prior to setting them on the adapter. To ensure that `name` and 57 | // `shortName` conform to length specifications prior to calling this method, see the constants `kMaxAdvertisingNameLength` and 58 | // `kMaxAdvertisingShortNameLength`. In addition, the static methods `truncateName()` and `truncateShortName()` may be helpful. 59 | // 60 | // Returns true on success, otherwise false 61 | bool Mgmt::setName(std::string name, std::string shortName) 62 | { 63 | // Ensure their lengths are okay 64 | name = truncateName(name); 65 | shortName = truncateShortName(shortName); 66 | 67 | struct SRequest : HciAdapter::HciHeader 68 | { 69 | char name[249]; 70 | char shortName[11]; 71 | } __attribute__((packed)); 72 | 73 | SRequest request; 74 | request.code = Mgmt::ESetLocalNameCommand; 75 | request.controllerId = controllerIndex; 76 | request.dataSize = sizeof(SRequest) - sizeof(HciAdapter::HciHeader); 77 | 78 | memset(request.name, 0, sizeof(request.name)); 79 | snprintf(request.name, sizeof(request.name), "%s", name.c_str()); 80 | 81 | memset(request.shortName, 0, sizeof(request.shortName)); 82 | snprintf(request.shortName, sizeof(request.shortName), "%s", shortName.c_str()); 83 | 84 | if (!HciAdapter::getInstance().sendCommand(request)) 85 | { 86 | Logger::warn(SSTR << " + Failed to set name"); 87 | return false; 88 | } 89 | 90 | return true; 91 | } 92 | 93 | // Set a setting state to 'newState' 94 | // 95 | // Many settings are set the same way, this is just a convenience routine to handle them all 96 | // 97 | // Returns true on success, otherwise false 98 | bool Mgmt::setState(uint16_t commandCode, uint16_t controllerId, uint8_t newState) 99 | { 100 | struct SRequest : HciAdapter::HciHeader 101 | { 102 | uint8_t state; 103 | } __attribute__((packed)); 104 | 105 | SRequest request; 106 | request.code = commandCode; 107 | request.controllerId = controllerId; 108 | request.dataSize = sizeof(SRequest) - sizeof(HciAdapter::HciHeader); 109 | request.state = newState; 110 | 111 | if (!HciAdapter::getInstance().sendCommand(request)) 112 | { 113 | Logger::warn(SSTR << " + Failed to set " << HciAdapter::kCommandCodeNames[commandCode] << " state to: " << static_cast(newState)); 114 | return false; 115 | } 116 | 117 | return true; 118 | } 119 | 120 | // Set the powered state to `newState` (true = powered on, false = powered off) 121 | // 122 | // Returns true on success, otherwise false 123 | bool Mgmt::setPowered(bool newState) 124 | { 125 | return setState(Mgmt::ESetPoweredCommand, controllerIndex, newState ? 1 : 0); 126 | } 127 | 128 | // Set the BR/EDR state to `newState` (true = enabled, false = disabled) 129 | // 130 | // Returns true on success, otherwise false 131 | bool Mgmt::setBredr(bool newState) 132 | { 133 | return setState(Mgmt::ESetBREDRCommand, controllerIndex, newState ? 1 : 0); 134 | } 135 | 136 | // Set the Secure Connection state (0 = disabled, 1 = enabled, 2 = secure connections only mode) 137 | // 138 | // Returns true on success, otherwise false 139 | bool Mgmt::setSecureConnections(uint8_t newState) 140 | { 141 | return setState(Mgmt::ESetSecureConnectionsCommand, controllerIndex, newState); 142 | } 143 | 144 | // Set the bondable state to `newState` (true = enabled, false = disabled) 145 | // 146 | // Returns true on success, otherwise false 147 | bool Mgmt::setBondable(bool newState) 148 | { 149 | return setState(Mgmt::ESetBondableCommand, controllerIndex, newState ? 1 : 0); 150 | } 151 | 152 | // Set the connectable state to `newState` (true = enabled, false = disabled) 153 | // 154 | // Returns true on success, otherwise false 155 | bool Mgmt::setConnectable(bool newState) 156 | { 157 | return setState(Mgmt::ESetConnectableCommand, controllerIndex, newState ? 1 : 0); 158 | } 159 | 160 | // Set the LE state to `newState` (true = enabled, false = disabled) 161 | // 162 | // Returns true on success, otherwise false 163 | bool Mgmt::setLE(bool newState) 164 | { 165 | return setState(Mgmt::ESetLowEnergyCommand, controllerIndex, newState ? 1 : 0); 166 | } 167 | 168 | // Set the advertising state to `newState` (0 = disabled, 1 = enabled (with consideration towards the connectable setting), 169 | // 2 = enabled in connectable mode). 170 | // 171 | // Returns true on success, otherwise false 172 | bool Mgmt::setAdvertising(uint8_t newState) 173 | { 174 | return setState(Mgmt::ESetAdvertisingCommand, controllerIndex, newState); 175 | } 176 | 177 | // --------------------------------------------------------------------------------------------------------------------------------- 178 | // Utilitarian 179 | // --------------------------------------------------------------------------------------------------------------------------------- 180 | 181 | // Truncates the string `name` to the maximum allowed length for an adapter name. If `name` needs no truncation, a copy of 182 | // `name` is returned. 183 | std::string Mgmt::truncateName(const std::string &name) 184 | { 185 | if (name.length() <= kMaxAdvertisingNameLength) 186 | { 187 | return name; 188 | } 189 | 190 | return name.substr(0, kMaxAdvertisingNameLength); 191 | } 192 | 193 | // Truncates the string `name` to the maximum allowed length for an adapter short-name. If `name` needs no truncation, a copy 194 | // of `name` is returned. 195 | std::string Mgmt::truncateShortName(const std::string &name) 196 | { 197 | if (name.length() <= kMaxAdvertisingShortNameLength) 198 | { 199 | return name; 200 | } 201 | 202 | return name.substr(0, kMaxAdvertisingShortNameLength); 203 | } 204 | 205 | }; // namespace ggk -------------------------------------------------------------------------------- /src/Server.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This is the top-level interface for the server. There is only one of these stored in the global `TheServer`. Use this object 25 | // to configure your server's settings (there are surprisingly few of them.) It also contains the full server description and 26 | // implementation. 27 | // 28 | // >> 29 | // >>> DISCUSSION 30 | // >> 31 | // 32 | // See the discussion at the top of Server.cpp 33 | // 34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "../include/Gobbledegook.h" 45 | #include "DBusObject.h" 46 | 47 | namespace ggk { 48 | 49 | // 50 | // Forward declarations 51 | // 52 | 53 | struct GattProperty; 54 | struct GattCharacteristic; 55 | struct DBusInterface; 56 | struct DBusObjectPath; 57 | 58 | // 59 | // Implementation 60 | // 61 | 62 | struct Server 63 | { 64 | // 65 | // Types 66 | // 67 | 68 | // Our server is a collection of D-Bus objects 69 | typedef std::list Objects; 70 | 71 | // 72 | // Accessors 73 | // 74 | 75 | // Returns the set of objects that each represent the root of an object tree describing a group of services we are providing 76 | const Objects &getObjects() const { return objects; } 77 | 78 | // Returns the requested setting for BR/EDR (true = enabled, false = disabled) 79 | bool getEnableBREDR() const { return enableBREDR; } 80 | 81 | // Returns the requested setting for secure connections (true = enabled, false = disabled) 82 | bool getEnableSecureConnection() const { return enableSecureConnection; } 83 | 84 | // Returns the requested setting the connectable state (true = enabled, false = disabled) 85 | bool getEnableConnectable() const { return enableConnectable; } 86 | 87 | // Returns the requested setting the LE advertising state (true = enabled, false = disabled) 88 | bool getEnableAdvertising() const { return enableAdvertising; } 89 | 90 | // Returns the requested setting the bondable state (true = enabled, false = disabled) 91 | bool getEnableBondable() const { return enableBondable; } 92 | 93 | // Returns our registered data getter 94 | GGKServerDataGetter getDataGetter() const { return dataGetter; } 95 | 96 | // Returns our registered data setter 97 | GGKServerDataSetter getDataSetter() const { return dataSetter; } 98 | 99 | // advertisingName: The name for this controller, as advertised over LE 100 | // 101 | // This is set from the constructor. 102 | // 103 | // IMPORTANT: Setting the advertisingName will change the system-wide name of the device. If that's not what you want, set 104 | // BOTH advertisingName and advertisingShortName to as empty string ("") to prevent setting the advertising 105 | // name. 106 | const std::string &getAdvertisingName() const { return advertisingName; } 107 | 108 | // advertisingShortName: The short name for this controller, as advertised over LE 109 | // 110 | // According to the spec, the short name is used in case the full name doesn't fit within Extended Inquiry Response (EIR) or 111 | // Advertising Data (AD). 112 | // 113 | // This is set from the constructor. 114 | // 115 | // IMPORTANT: Setting the advertisingName will change the system-wide name of the device. If that's not what you want, set 116 | // BOTH advertisingName and advertisingShortName to as empty string ("") to prevent setting the advertising 117 | // name. 118 | const std::string &getAdvertisingShortName() const { return advertisingShortName; } 119 | 120 | // serviceName: The name of our server (collectino of services) 121 | // 122 | // This is set from the constructor. 123 | // 124 | // This is used to build the path for our Bluetooth services (and we'll go ahead and use it as the owned name as well for 125 | // consistency.) 126 | const std::string &getServiceName() const { return serviceName; } 127 | 128 | // Our owned name 129 | // 130 | // D-Bus uses owned names to locate servers on the bus. Think of this as a namespace within D-Bus. We building this with the 131 | // server name to keep things simple. 132 | std::string getOwnedName() const { return std::string("com.") + getServiceName(); } 133 | 134 | // 135 | // Initialization 136 | // 137 | 138 | // Our constructor builds our entire server description 139 | // 140 | // serviceName: The name of our server (collectino of services) 141 | // 142 | // This is used to build the path for our Bluetooth services. It also provides the base for the D-Bus owned name (see 143 | // getOwnedName.) 144 | // 145 | // This value will be stored as lower-case only. 146 | // 147 | // Retrieve this value using the `getName()` method. 148 | // 149 | // advertisingName: The name for this controller, as advertised over LE 150 | // 151 | // IMPORTANT: Setting the advertisingName will change the system-wide name of the device. If that's not what you want, set 152 | // BOTH advertisingName and advertisingShortName to as empty string ("") to prevent setting the advertising 153 | // name. 154 | // 155 | // Retrieve this value using the `getAdvertisingName()` method. 156 | // 157 | // advertisingShortName: The short name for this controller, as advertised over LE 158 | // 159 | // According to the spec, the short name is used in case the full name doesn't fit within Extended Inquiry Response (EIR) or 160 | // Advertising Data (AD). 161 | // 162 | // IMPORTANT: Setting the advertisingName will change the system-wide name of the device. If that's not what you want, set 163 | // BOTH advertisingName and advertisingShortName to as empty string ("") to prevent setting the advertising 164 | // name. 165 | // 166 | // Retrieve this value using the `getAdvertisingShortName()` method. 167 | // 168 | Server(const std::string &serviceName, const std::string &advertisingName, const std::string &advertisingShortName, 169 | GGKServerDataGetter getter, GGKServerDataSetter setter); 170 | 171 | // 172 | // Utilitarian 173 | // 174 | 175 | // Find and call a D-Bus method within the given D-Bus object on the given D-Bus interface 176 | // 177 | // If the method was called, this method returns true, otherwise false. There is no result from the method call itself. 178 | std::shared_ptr findInterface(const DBusObjectPath &objectPath, const std::string &interfaceName) const; 179 | 180 | // Find a D-Bus method within the given D-Bus object on the given D-Bus interface 181 | // 182 | // If the method was found, it is returned, otherwise nullptr is returned 183 | bool callMethod(const DBusObjectPath &objectPath, const std::string &interfaceName, const std::string &methodName, GDBusConnection *pConnection, GVariant *pParameters, GDBusMethodInvocation *pInvocation, gpointer pUserData) const; 184 | 185 | // Find a GATT Property within the given D-Bus object on the given D-Bus interface 186 | // 187 | // If the property was found, it is returned, otherwise nullptr is returned 188 | const GattProperty *findProperty(const DBusObjectPath &objectPath, const std::string &interfaceName, const std::string &propertyName) const; 189 | 190 | private: 191 | 192 | // Our server's objects 193 | Objects objects; 194 | 195 | // BR/EDR requested state 196 | bool enableBREDR; 197 | 198 | // Secure connection requested state 199 | bool enableSecureConnection; 200 | 201 | // Connectable requested state 202 | bool enableConnectable; 203 | 204 | // LE advertising requested state 205 | bool enableAdvertising; 206 | 207 | // Bondable requested state 208 | bool enableBondable; 209 | 210 | // The getter callback that is responsible for returning current server data that is shared over Bluetooth 211 | GGKServerDataGetter dataGetter; 212 | 213 | // The setter callback that is responsible for storing current server data that is shared over Bluetooth 214 | GGKServerDataSetter dataSetter; 215 | 216 | // advertisingName: The name for this controller, as advertised over LE 217 | // 218 | // This is set from the constructor. 219 | // 220 | // IMPORTANT: Setting the advertisingName will change the system-wide name of the device. If that's not what you want, set 221 | // BOTH advertisingName and advertisingShortName to as empty string ("") to prevent setting the advertising 222 | // name. 223 | std::string advertisingName; 224 | 225 | // advertisingShortName: The short name for this controller, as advertised over LE 226 | // 227 | // According to the spec, the short name is used in case the full name doesn't fit within Extended Inquiry Response (EIR) or 228 | // Advertising Data (AD). 229 | // 230 | // This is set from the constructor. 231 | // 232 | // IMPORTANT: Setting the advertisingName will change the system-wide name of the device. If that's not what you want, set 233 | // BOTH advertisingName and advertisingShortName to as empty string ("") to prevent setting the advertising 234 | // name. 235 | std::string advertisingShortName; 236 | 237 | // serviceName: The name of our server (collectino of services) 238 | // 239 | // This is set from the constructor. 240 | // 241 | // This is used to build the path for our Bluetooth services (and we'll go ahead and use it as the owned name as well for 242 | // consistency.) 243 | std::string serviceName; 244 | }; 245 | 246 | // Our one and only server. It's a global. 247 | extern std::shared_ptr TheServer; 248 | 249 | }; // namespace ggk 250 | -------------------------------------------------------------------------------- /src/ServerUtils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This file contains helper functions for our server's implementation. That is, methods that are used by the server itself (when 25 | // responding to D-Bus or BlueZ requests.) 26 | // 27 | // >> 28 | // >>> DISCUSSION 29 | // >> 30 | // 31 | // Generally speaking, these are blocks of code that are too big to comfortably fit as lambdas within the `Server::Server()` 32 | // constructor are here. 33 | // 34 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "ServerUtils.h" 42 | #include "DBusObject.h" 43 | #include "DBusInterface.h" 44 | #include "GattProperty.h" 45 | #include "GattService.h" 46 | #include "GattCharacteristic.h" 47 | #include "GattDescriptor.h" 48 | #include "Server.h" 49 | #include "Logger.h" 50 | #include "Utils.h" 51 | 52 | namespace ggk { 53 | 54 | // Adds an object to the tree of managed objects as returned from the `GetManagedObjects` method call from the D-Bus interface 55 | // `org.freedesktop.DBus.ObjectManager`. 56 | // 57 | // According to the spec: 58 | // 59 | // The return value of this method is a dict whose keys are object paths. 60 | // All returned object paths are children of the object path implementing this interface, 61 | // i.e. their object paths start with the ObjectManager's object path plus '/'. 62 | // 63 | // Each value is a dict whose keys are interfaces names. Each value in this inner dict 64 | // is the same dict that would be returned by the org.freedesktop.DBus.Properties.GetAll() 65 | // method for that combination of object path and interface. If an interface has no properties, 66 | // the empty dict is returned. 67 | // 68 | // (a{oa{sa{sv}}}) 69 | static void addManagedObjectsNode(const DBusObject &object, const DBusObjectPath &basePath, GVariantBuilder *pObjectArray) 70 | { 71 | if (!object.isPublished()) 72 | { 73 | return; 74 | } 75 | 76 | if (!object.getInterfaces().empty()) 77 | { 78 | DBusObjectPath path = basePath + object.getPathNode(); 79 | Logger::debug(SSTR << " Object: " << path); 80 | 81 | GVariantBuilder *pInterfaceArray = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); 82 | for (std::shared_ptr pInterface : object.getInterfaces()) 83 | { 84 | Logger::debug(SSTR << " + Interface (type: " << pInterface->getInterfaceType() << ")"); 85 | 86 | if (std::shared_ptr pService = TRY_GET_CONST_INTERFACE_OF_TYPE(pInterface, GattService)) 87 | { 88 | if (!pService->getProperties().empty()) 89 | { 90 | Logger::debug(SSTR << " GATT Service interface: " << pService->getName()); 91 | 92 | GVariantBuilder *pPropertyArray = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); 93 | for (const GattProperty &property : pService->getProperties()) 94 | { 95 | Logger::debug(SSTR << " Property " << property.getName()); 96 | g_variant_builder_add 97 | ( 98 | pPropertyArray, 99 | "{sv}", 100 | property.getName().c_str(), 101 | property.getValue() 102 | ); 103 | } 104 | 105 | g_variant_builder_add 106 | ( 107 | pInterfaceArray, 108 | "{sa{sv}}", 109 | pService->getName().c_str(), 110 | pPropertyArray 111 | ); 112 | } 113 | } 114 | else if (std::shared_ptr pCharacteristic = TRY_GET_CONST_INTERFACE_OF_TYPE(pInterface, GattCharacteristic)) 115 | { 116 | if (!pCharacteristic->getProperties().empty()) 117 | { 118 | Logger::debug(SSTR << " GATT Characteristic interface: " << pCharacteristic->getName()); 119 | 120 | GVariantBuilder *pPropertyArray = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); 121 | for (const GattProperty &property : pCharacteristic->getProperties()) 122 | { 123 | Logger::debug(SSTR << " Property " << property.getName()); 124 | g_variant_builder_add 125 | ( 126 | pPropertyArray, 127 | "{sv}", 128 | property.getName().c_str(), 129 | property.getValue() 130 | ); 131 | } 132 | 133 | g_variant_builder_add 134 | ( 135 | pInterfaceArray, 136 | "{sa{sv}}", 137 | pCharacteristic->getName().c_str(), 138 | pPropertyArray 139 | ); 140 | } 141 | } 142 | else if (std::shared_ptr pDescriptor = TRY_GET_CONST_INTERFACE_OF_TYPE(pInterface, GattDescriptor)) 143 | { 144 | if (!pDescriptor->getProperties().empty()) 145 | { 146 | Logger::debug(SSTR << " GATT Descriptor interface: " << pDescriptor->getName()); 147 | 148 | GVariantBuilder *pPropertyArray = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); 149 | for (const GattProperty &property : pDescriptor->getProperties()) 150 | { 151 | Logger::debug(SSTR << " Property " << property.getName()); 152 | g_variant_builder_add 153 | ( 154 | pPropertyArray, 155 | "{sv}", 156 | property.getName().c_str(), 157 | property.getValue() 158 | ); 159 | } 160 | 161 | g_variant_builder_add 162 | ( 163 | pInterfaceArray, 164 | "{sa{sv}}", 165 | pDescriptor->getName().c_str(), 166 | pPropertyArray 167 | ); 168 | } 169 | } 170 | else 171 | { 172 | Logger::error(SSTR << " Unknown interface type"); 173 | return; 174 | } 175 | } 176 | 177 | g_variant_builder_add 178 | ( 179 | pObjectArray, 180 | "{oa{sa{sv}}}", 181 | path.c_str(), 182 | pInterfaceArray 183 | ); 184 | } 185 | 186 | for (const DBusObject &child : object.getChildren()) 187 | { 188 | addManagedObjectsNode(child, basePath + object.getPathNode(), pObjectArray); 189 | } 190 | } 191 | 192 | // Builds the response to the method call `GetManagedObjects` from the D-Bus interface `org.freedesktop.DBus.ObjectManager` 193 | void ServerUtils::getManagedObjects(GDBusMethodInvocation *pInvocation) 194 | { 195 | Logger::debug(SSTR << "Reporting managed objects"); 196 | 197 | GVariantBuilder *pObjectArray = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); 198 | for (const DBusObject &object : TheServer->getObjects()) 199 | { 200 | addManagedObjectsNode(object, DBusObjectPath(""), pObjectArray); 201 | } 202 | 203 | GVariant *pParams = g_variant_new("(a{oa{sa{sv}}})", pObjectArray); 204 | g_dbus_method_invocation_return_value(pInvocation, pParams); 205 | } 206 | 207 | // WARNING: Hacky code - don't count on this working properly on all systems 208 | // 209 | // This routine will attempt to parse /proc/cpuinfo to return the CPU count/model. Results are cached on the first call, with 210 | // cached results returned on successive calls. 211 | // 212 | // If this routine fails, it will respond with something reasonable, if not _entirely_ accurate. 213 | std::string ServerUtils::getCpuInfo(int16_t &cpuCount) 214 | { 215 | static int16_t cachedCount = -1; 216 | static std::string cachedModel; 217 | static const std::string kCpuInfoFile = "/proc/cpuinfo"; 218 | 219 | // If we haven't cached a result, let's go get one 220 | if (cachedCount == -1) 221 | { 222 | // Reset our counter 223 | cachedCount = 0; 224 | 225 | // Open the cpuinfo file 226 | std::ifstream cpuInfo(kCpuInfoFile); 227 | if (cpuInfo.is_open()) 228 | { 229 | std::string line; 230 | while(getline(cpuInfo, line)) 231 | { 232 | // Count the processors 233 | std::regex processorRegex("^processor.*: [0-9].*$", std::regex::ECMAScript); 234 | std::smatch processorMatch; 235 | 236 | if (std::regex_search(line, processorMatch, processorRegex)) 237 | { 238 | cachedCount++; 239 | } 240 | 241 | // Extract the first model name we find 242 | if (cachedModel.empty()) 243 | { 244 | std::regex modelRegex("^model name.*: (.*)$", std::regex::ECMAScript); 245 | std::smatch modelMatch; 246 | if (std::regex_search(line, modelMatch, modelRegex)) 247 | { 248 | if (modelMatch.size() == 2) 249 | { 250 | cachedModel = Utils::trim(modelMatch[1].str()); 251 | } 252 | } 253 | } 254 | } 255 | 256 | cpuInfo.close(); 257 | } 258 | 259 | // If we never found one, provide a reasonable default 260 | if (cachedModel.empty()) { cachedModel = "Gooberfest Cyclemaster 3000 (v8)"; } 261 | if (cachedCount == 0) { cachedCount = 42; } 262 | } 263 | 264 | cpuCount = cachedCount; 265 | return cachedModel; 266 | } 267 | 268 | // Build a variant that meets the standard for the Current Time (0x2A2B) Bluetooth Characteristic standard 269 | // 270 | // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.current_time.xml 271 | GVariant *ServerUtils::gvariantCurrentTime() 272 | { 273 | time_t timeValue = time(nullptr); 274 | struct tm *pTimeStruct = localtime(&timeValue); 275 | guint16 year = pTimeStruct->tm_year + 1900; 276 | guint8 wday = guint8(pTimeStruct->tm_wday == 0 ? 7 : pTimeStruct->tm_wday); 277 | 278 | g_auto(GVariantBuilder) builder; 279 | g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); 280 | 281 | g_variant_builder_add(&builder, "y", (year >> 0) & 0xff); 282 | g_variant_builder_add(&builder, "y", (year >> 8) & 0xff); 283 | g_variant_builder_add(&builder, "y", guint8(pTimeStruct->tm_mon+1)); // month (1-12) 284 | g_variant_builder_add(&builder, "y", guint8(pTimeStruct->tm_mday)); // day (1-31) 285 | g_variant_builder_add(&builder, "y", guint8(pTimeStruct->tm_hour)); // hour (0-23) 286 | g_variant_builder_add(&builder, "y", guint8(pTimeStruct->tm_min)); // minute (0-59) 287 | g_variant_builder_add(&builder, "y", guint8(pTimeStruct->tm_sec)); // seconds (0-59) 288 | g_variant_builder_add(&builder, "y", wday); // weekday (1-7 where 1=Monday) 289 | g_variant_builder_add(&builder, "y", guint8(0)); // Fractions (1/256th of second) 290 | g_variant_builder_add(&builder, "y", guint8(0)); // Adjust reason bitmask (0 for testing) 291 | 292 | GVariant * const pVariant = g_variant_builder_end(&builder); 293 | return pVariant; 294 | } 295 | 296 | // Build a variant that meets the standard for the Local Time Information (0x2A0F) Bluetooth Characteristic standard 297 | // 298 | // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.local_time_information.xml 299 | GVariant *ServerUtils::gvariantLocalTime() 300 | { 301 | tzset(); 302 | time_t timeValue = time(nullptr); 303 | struct tm *pTimeStruct = localtime(&timeValue); 304 | 305 | gint8 utcOffset = -gint8(timezone / 60 / 15); // UTC time (uses 15-minute increments, 0 = UTC time) 306 | guint8 dstOffset = pTimeStruct->tm_isdst == 0 ? 0 : 4; // 0 = no DST offset, 4 = +1 hour for DST 307 | 308 | g_auto(GVariantBuilder) builder; 309 | g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); 310 | 311 | g_variant_builder_add(&builder, "y", utcOffset); 312 | g_variant_builder_add(&builder, "y", dstOffset); 313 | 314 | GVariant * const pVariant = g_variant_builder_end(&builder); 315 | return pVariant; 316 | } 317 | 318 | }; // namespace ggk -------------------------------------------------------------------------------- /src/ServerUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This file contains helper function declarations for our server's implementation. That is, methods that are used by the server 25 | // itself (when responding to D-Bus or BlueZ requests.) 26 | // 27 | // >> 28 | // >>> DISCUSSION 29 | // >> 30 | // 31 | // See ServerUtils.cpp for implementations 32 | // 33 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 34 | 35 | #pragma once 36 | 37 | #include 38 | #include 39 | 40 | namespace ggk { 41 | 42 | struct ServerUtils 43 | { 44 | // Builds the response to the method call `GetManagedObjects` from the D-Bus interface `org.freedesktop.DBus.ObjectManager` 45 | static void getManagedObjects(GDBusMethodInvocation *pInvocation); 46 | 47 | // WARNING: Hacky code - don't count on this working properly on all systems 48 | // 49 | // This routine will attempt to parse /proc/cpuinfo to return the CPU count/model. Results are cached on the first call, with 50 | // cached results returned on successive calls. 51 | // 52 | // If this routine fails, it will respond with something reasonable, if not _entirely_ accurate. 53 | static std::string getCpuInfo(int16_t &cpuCount); 54 | 55 | // Build a variant that meets the standard for the Current Time (0x2A2B) Bluetooth Characteristic standard 56 | // 57 | // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.current_time.xml 58 | static GVariant *gvariantCurrentTime(); 59 | 60 | // Build a variant that meets the standard for the Local Time Information (0x2A0F) Bluetooth Characteristic standard 61 | // 62 | // See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.local_time_information.xml 63 | static GVariant *gvariantLocalTime(); 64 | }; 65 | 66 | }; // namespace ggk -------------------------------------------------------------------------------- /src/TickEvent.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // A TIckEvent is an event that is triggered on a regular timer interval. 25 | // 26 | // >> 27 | // >>> DISCUSSION 28 | // >> 29 | // 30 | // Tick events are used within the Server's description in Server.cpp. As you might expect, they are useful for updating data on a 31 | // regular basis or performing other periodic tasks. One example usage might be checking the battery level every 60 seconds and if 32 | // it has changed since the last update, send out a notification to subscribers. 33 | // 34 | // The frequency at which events fire is dependent upon two values: the driving timer's tick frequency multiplied by the tick 35 | // freuency of the TickEvent itself. 36 | // 37 | // The tick event's frequency is set when a tick event is added via the `onEvent()` method to the server description. 38 | // 39 | // The driving timer's frequency is a one-second-resolution low-frequency timer with a default of one second. To modify this, see 40 | // `kPeriodicTimerFrequencySeconds` at the top of Init.cpp. Note that the periodic timer (which drives tick events) is intentionally 41 | // a low-frequency timer. Higher frequency timers would lend themselves to using more battery on both, the server and client. 42 | // 43 | // When using a TickEvent, be careful not to demand too much of your client. Notifiations that are too frequent may place undue 44 | // stress on their battery to receive and process the updates. 45 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 46 | 47 | #pragma once 48 | 49 | #include 50 | #include 51 | 52 | #include "DBusObjectPath.h" 53 | #include "Logger.h" 54 | 55 | namespace ggk { 56 | 57 | // --------------------------------------------------------------------------------------------------------------------------------- 58 | // Forward declarations 59 | // --------------------------------------------------------------------------------------------------------------------------------- 60 | 61 | struct DBusInterface; 62 | 63 | // --------------------------------------------------------------------------------------------------------------------------------- 64 | // Implementation 65 | // --------------------------------------------------------------------------------------------------------------------------------- 66 | 67 | struct TickEvent 68 | { 69 | // 70 | // Types 71 | // 72 | 73 | // A tick event callback, which is called whenever the TickEvent fires 74 | typedef void (*Callback)(const DBusInterface &self, const TickEvent &event, GDBusConnection *pConnection, void *pUserData); 75 | 76 | // Construct a TickEvent that will fire after a specified 'tickFrequency' number of ticks of the periodic timer. 77 | // 78 | // Note that the actual time between a callback's execution is the event's 'tickFrequency' multiplied by the time between each 79 | // periodic timer tick. 80 | TickEvent(const DBusInterface *pOwner, int tickFrequency, Callback callback, void *pUserData) 81 | : pOwner(pOwner), elapsedTicks(0), tickFrequency(tickFrequency), callback(callback), pUserData(pUserData) 82 | { 83 | } 84 | 85 | // 86 | // Accessors 87 | // 88 | 89 | // Returns the elapsed ticks since the last event firing 90 | int getElapsedTicks() const { return elapsedTicks; } 91 | 92 | // Sets the elapsed ticks since the last event firing 93 | void setElapsedTicks(int elapsed) { elapsedTicks = elapsed; } 94 | 95 | // Returns the tick frequency between schedule tick events 96 | int getTickFrequency() const { return tickFrequency; } 97 | 98 | // Sets the tick frequency between schedule tick events 99 | void setTickFrequency(int frequency) { tickFrequency = frequency; } 100 | 101 | // Returns the user data pointer associated to this TickEvent 102 | void *getUserData() { return pUserData; } 103 | 104 | // Sets the user data pointer associated to this TickEvent 105 | void setUserData(void *pUserData) { this->pUserData = pUserData; } 106 | 107 | // Gets the callback for the TickEvent 108 | Callback getCallback() const { return callback; } 109 | 110 | // Sets the callback for the TickEvent 111 | void setCallback(Callback callback) { this->callback = callback; } 112 | 113 | // 114 | // Tick management 115 | // 116 | 117 | // Perform a single tick of a TickEvent 118 | // 119 | // A TickEvent is ticked each time the periodic timer fires. The TickEvent only fires after `tickFrequency` ticks. As a result, 120 | // the `callback` is only called after a period of time equal to the time between firings of the periodic timer, multiplied by 121 | // `tickFrequency`. 122 | // 123 | // Returns true if event fires, false otherwise 124 | template 125 | void tick(const DBusObjectPath &path, GDBusConnection *pConnection, void *pUserData) const 126 | { 127 | elapsedTicks += 1; 128 | if (elapsedTicks >= tickFrequency) 129 | { 130 | if (nullptr != callback) 131 | { 132 | Logger::debug(SSTR << "Ticking at path '" << path << "'"); 133 | callback(*static_cast(pOwner), *this, pConnection, pUserData); 134 | } 135 | 136 | elapsedTicks = 0; 137 | } 138 | } 139 | 140 | private: 141 | 142 | // 143 | // Data members 144 | // 145 | 146 | const DBusInterface *pOwner; 147 | mutable int elapsedTicks; 148 | int tickFrequency; 149 | Callback callback; 150 | void *pUserData; 151 | }; 152 | 153 | }; // namespace ggk -------------------------------------------------------------------------------- /src/Utils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Paul Nettle. 2 | // 3 | // This file is part of Gobbledegook. 4 | // 5 | // Gobbledegook is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // Gobbledegook is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with Gobbledegook. If not, see . 17 | 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | // 20 | // >> 21 | // >>> INSIDE THIS FILE 22 | // >> 23 | // 24 | // This file contains various general utilitarian functions used throught. It is in some ways, the 'junk drawer' of the appliation, 25 | // though better organized than most physical junk drawers. 26 | // 27 | // >> 28 | // >>> DISCUSSION 29 | // >> 30 | // 31 | // This file contains: 32 | // 33 | // - String helper functions (trimming methods) 34 | // - Hexidecimal helper functions for 35 | // + Producing hex values of various types (8-bit, 16-bit, 32-bit) 36 | // + Standardied Hex/ASCII dumps to the log file of chunks of binary data 37 | // + Properly formatted Bluetooth addresses) 38 | // - GVariant helper funcions of various forms to convert values to/from GVariants 39 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 40 | 41 | #pragma once 42 | 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include "DBusObjectPath.h" 50 | 51 | namespace ggk { 52 | 53 | struct Utils 54 | { 55 | // ----------------------------------------------------------------------------------------------------------------------------- 56 | // Handy string functions 57 | // ----------------------------------------------------------------------------------------------------------------------------- 58 | 59 | // Trim from start (in place) 60 | static void trimBeginInPlace(std::string &str); 61 | 62 | // Trim from end (in place) 63 | static void trimEndInPlace(std::string &str); 64 | 65 | // Trim from both ends (in place) 66 | static void trimInPlace(std::string &str); 67 | 68 | // Trim from start (copying) 69 | static std::string trimBegin(const std::string &str); 70 | 71 | // Trim from end (copying) 72 | static std::string trimEnd(const std::string &str); 73 | 74 | // Trim from both ends (copying) 75 | static std::string trim(const std::string &str); 76 | 77 | // ----------------------------------------------------------------------------------------------------------------------------- 78 | // Hex output functions 79 | // ----------------------------------------------------------------------------------------------------------------------------- 80 | 81 | // Returns a zero-padded 8-bit hex value in the format: 0xA 82 | static std::string hex(uint8_t value); 83 | 84 | // Returns a zero-padded 8-bit hex value in the format: 0xAB 85 | static std::string hex(uint16_t value); 86 | 87 | // Returns a zero-padded 8-bit hex value in the format: 0xABCD 88 | static std::string hex(uint32_t value); 89 | 90 | // A full hex-dump of binary data (with accompanying ASCII output) 91 | static std::string hex(const uint8_t *pData, int count); 92 | 93 | // Returns a peoperly formatted Bluetooth address from a set of six octets stored at `pAddress` 94 | // 95 | // USE WITH CAUTION: It is expected that pAddress point to an array of 6 bytes. The length of the array cannot be validated and 96 | // incorrect lengths will produce undefined, likely unwanted and potentially fatal results. Or it will return the address of the 97 | // train at platform 9 3/4. You decide. 98 | // 99 | // This method returns a set of six zero-padded 8-bit hex values 8-bit in the format: 12:34:56:78:9A:BC 100 | static std::string bluetoothAddressString(uint8_t *pAddress); 101 | 102 | // ----------------------------------------------------------------------------------------------------------------------------- 103 | // A small collection of helper functions for generating various types of GVariants, which are needed when responding to BlueZ 104 | // method/property messages. Real services will likley need more of these to support various types of data passed to/from BlueZ, 105 | // or feel free to do away with them and use GLib directly. 106 | // ----------------------------------------------------------------------------------------------------------------------------- 107 | 108 | // Returns a GVariant containing a floating reference to a utf8 string 109 | static GVariant *gvariantFromString(const char *pStr); 110 | 111 | // Returns a GVariant containing a floating reference to a utf8 string 112 | static GVariant *gvariantFromString(const std::string &str); 113 | 114 | // Returns an array of strings ("as") with one string per variable argument. 115 | // 116 | // The array must be terminated with a nullptr. 117 | // 118 | // This is an extension method to the vararg version, which accepts pass-through variable arguments from other mthods. 119 | static GVariant *gvariantFromStringArray(const char *pStr, va_list args); 120 | 121 | // Returns an array of strings ("as") with one string per variable argument. 122 | // 123 | // The array must be terminated with a nullptr. 124 | static GVariant *gvariantFromStringArray(const char *pStr, ...); 125 | 126 | // Returns an array of strings ("as") from an array of strings 127 | static GVariant *gvariantFromStringArray(const std::vector &arr); 128 | 129 | // Returns an array of strings ("as") from an array of C strings 130 | static GVariant *gvariantFromStringArray(const std::vector &arr); 131 | 132 | // Returns an GVariant* containing an object path ("o") from an DBusObjectPath 133 | static GVariant *gvariantFromObject(const DBusObjectPath &path); 134 | 135 | // Returns an GVariant* containing a boolean 136 | static GVariant *gvariantFromBoolean(bool b); 137 | 138 | // Returns an GVariant* containing a 16-bit integer 139 | static GVariant *gvariantFromInt(gint16 value); 140 | 141 | // Returns an GVariant* containing a 32-bit integer 142 | static GVariant *gvariantFromInt(gint32 value); 143 | 144 | // Returns an array of bytes ("ay") with the contents of the input C string 145 | static GVariant *gvariantFromByteArray(const char *pStr); 146 | 147 | // Returns an array of bytes ("ay") with the contents of the input string 148 | static GVariant *gvariantFromByteArray(const std::string &str); 149 | 150 | // Returns an array of bytes ("ay") with the contents of the input array of unsigned 8-bit values 151 | static GVariant *gvariantFromByteArray(const guint8 *pBytes, int count); 152 | 153 | // Returns an array of bytes ("ay") with the contents of the input array of unsigned 8-bit values 154 | static GVariant *gvariantFromByteArray(const std::vector bytes); 155 | 156 | // Returns an array of bytes ("ay") containing a single unsigned 8-bit value 157 | static GVariant *gvariantFromByteArray(const guint8 data); 158 | 159 | // Returns an array of bytes ("ay") containing a single signed 8-bit value 160 | static GVariant *gvariantFromByteArray(const gint8 data); 161 | 162 | // Returns an array of bytes ("ay") containing a single unsigned 16-bit value 163 | static GVariant *gvariantFromByteArray(const guint16 data); 164 | 165 | // Returns an array of bytes ("ay") containing a single signed 16-bit value 166 | static GVariant *gvariantFromByteArray(const gint16 data); 167 | 168 | // Returns an array of bytes ("ay") containing a single unsigned 32-bit value 169 | static GVariant *gvariantFromByteArray(const guint32 data); 170 | 171 | // Returns an array of bytes ("ay") containing a single signed 32-bit value 172 | static GVariant *gvariantFromByteArray(const gint32 data); 173 | 174 | // Returns an array of bytes ("ay") containing a single unsigned 64-bit value 175 | static GVariant *gvariantFromByteArray(const guint64 data); 176 | 177 | // Returns an array of bytes ("ay") containing a single signed 64-bit value 178 | static GVariant *gvariantFromByteArray(const gint64 data); 179 | 180 | // Extracts a string from an array of bytes ("ay") 181 | static std::string stringFromGVariantByteArray(const GVariant *pVariant); 182 | 183 | // ----------------------------------------------------------------------------------------------------------------------------- 184 | // Endian conversion 185 | // 186 | // The Bluetooth Management API defines itself has using little-endian byte order. In the methods below, 'Hci' refers to this 187 | // format, while 'Host' refers to the endianness of the hardware we are running on. 188 | // 189 | // The `Utils::endianToHost()` method overloads perform endian byte-ordering conversions from the HCI to our endian format 190 | // The `Utils::endianToHci()` method overloads perform endian byte-ordering conversions from our endian format to that of the HCI 191 | // ----------------------------------------------------------------------------------------------------------------------------- 192 | 193 | // Convert a byte from HCI format to host format 194 | // 195 | // Since bytes are endian agnostic, this function simply returns the input value 196 | static uint8_t endianToHost(uint8_t value) {return value;} 197 | 198 | // Convert a byte from host format to HCI format 199 | // 200 | // Since bytes are endian agnostic, this function simply returns the input value 201 | static uint8_t endianToHci(uint8_t value) {return value;} 202 | 203 | // Convert a 16-bit value from HCI format to host format 204 | static uint16_t endianToHost(uint16_t value) {return le16toh(value);} 205 | 206 | // Convert a 16-bit value from host format to HCI format 207 | static uint16_t endianToHci(uint16_t value) {return htole16(value);} 208 | 209 | // Convert a 32-bit value from HCI format to host format 210 | static uint32_t endianToHost(uint32_t value) {return le32toh(value);} 211 | 212 | // Convert a 32-bit value from host format to HCI format 213 | static uint32_t endianToHci(uint32_t value) {return htole32(value);} 214 | }; 215 | 216 | }; // namespace ggk --------------------------------------------------------------------------------