diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7491b49 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,263 @@ +## +## CMakeLists.txt +## BluetoothLinux +## +## Builds `libbluetooth.so.3` — a drop-in replacement for the shared +## library BlueZ installs, implemented in Swift. +## +## SwiftPM drives development and the test suites; CMake exists because +## soname, symbol versioning and install name are not expressible in +## `Package.swift`. +## +## Phase 0's deliverable is a library that loads, resolves every one of +## the 218 symbols the reference exports, and aborts loudly on every +## call that is not implemented yet (see scripts/gen_stubs.py). Each +## subsequent phase moves names from `scripts/symbols.txt` into +## `scripts/implemented.txt` and the stubs disappear. +## +## Replacing this library cannot break the Bluetooth stack itself: +## nothing in the `bluez` package links it — bluetoothd, bluetoothctl, +## btmon, hciconfig and sdptool all statically link +## libbluetooth-internal.a. It exists solely for third-party consumers. +## The corollary is that there is no "run bluetoothctl and see" smoke +## test, which is why Conformance/ matters. +## +## The Ninja generator is required — CMake does not support the Swift +## language with Makefiles. +## +## Usage: +## cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ +## -DBLUETOOTH_PACKAGE_PATH=../Bluetooth +## cmake --build build +## cmake --build build --target check-exports +## cmake --install build --prefix /usr/local +## + +cmake_minimum_required(VERSION 3.26) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(FATAL_ERROR + "The Ninja generator is required for Swift targets; " + "re-run with -G Ninja.") +endif() + +project(BluetoothLinuxABI + VERSION 3.19.15 + DESCRIPTION "Swift implementation of the BlueZ libbluetooth ABI" + LANGUAGES C Swift) + +# Checked after project(), which is what sets CMAKE_SYSTEM_NAME. +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + message(FATAL_ERROR + "libbluetooth.so.3 is a Linux library; this build is only " + "supported on Linux.") +endif() + +# --------------------------------------------------------------------- +# Options +# --------------------------------------------------------------------- + +# Debian 5.85-4 ships libbluetooth.so.3.19.15 (libtool -version-info +# 22:15:19); master is heading to .3.19.16. The soname is what consumers +# actually bind to. +set(LIBBLUETOOTH_SOVERSION 3) +set(LIBBLUETOOTH_VERSION 3.19.15) + +option(BLUETOOTH_ABI_STATIC_STDLIB + "Statically link the Swift runtime into the shared library" OFF) + +option(BLUETOOTH_ABI_INSTALL_HEADERS + "Install the vendored BlueZ headers under /include/bluetooth" ON) + +# The Swift implementations of the non-socket half live in +# PureSwift/Bluetooth. Point this at a checkout of it. +set(BLUETOOTH_PACKAGE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../Bluetooth" + CACHE PATH "Path to a PureSwift/Bluetooth checkout") + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE) +endif() + +include(GNUInstallDirs) + +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +# --------------------------------------------------------------------- +# Generated sources +# --------------------------------------------------------------------- +# +# Both the stub table and the version script are derived from +# scripts/symbols.txt, so the export surface has exactly one source of +# truth. They are committed as well as generated, so that a plain +# `swift build` sees the same files. + +set(LIBBLUETOOTH_STUBS + "${CMAKE_CURRENT_SOURCE_DIR}/Sources/CBluetoothLinuxABI/gen/cbt_stubs.c") +set(LIBBLUETOOTH_VERSION_SCRIPT + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/libbluetooth.map") + +add_custom_command( + OUTPUT "${LIBBLUETOOTH_STUBS}" + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_stubs.py" + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_stubs.py" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/symbols.txt" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/implemented.txt" + COMMENT "Generating unimplemented-symbol stubs" + VERBATIM) + +add_custom_command( + OUTPUT "${LIBBLUETOOTH_VERSION_SCRIPT}" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/exported.txt" + COMMAND "${Python3_EXECUTABLE}" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_symbols.py" + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen_symbols.py" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/symbols.txt" + COMMENT "Generating the version script" + VERBATIM) + +# --------------------------------------------------------------------- +# CBluetoothLinuxABI — the C surface +# --------------------------------------------------------------------- +# +# `include/bluetooth/` holds the eleven public headers vendored verbatim +# from BlueZ; they are GPL-2.0-or-later, unlike the rest of this +# repository. See Sources/CBluetoothLinuxABI/README.md. + +add_library(CBluetoothLinuxABI STATIC ${LIBBLUETOOTH_STUBS}) + +target_include_directories(CBluetoothLinuxABI PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}/Sources/CBluetoothLinuxABI/include") + +# --------------------------------------------------------------------- +# BluetoothLinuxABI — the Swift implementations of the C entry points +# this repository owns (currently: the HCI string converter family) +# --------------------------------------------------------------------- + +file(GLOB BLUETOOTH_LINUX_ABI_SOURCES CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/Sources/BluetoothLinuxABI/*.swift" + "${CMAKE_CURRENT_SOURCE_DIR}/Sources/BluetoothLinuxABI/gen/*.swift") + +add_library(BluetoothLinuxABI STATIC ${BLUETOOTH_LINUX_ABI_SOURCES}) + +set_target_properties(BluetoothLinuxABI PROPERTIES + Swift_MODULE_NAME BluetoothLinuxABI) + +target_link_libraries(BluetoothLinuxABI PUBLIC CBluetoothLinuxABI) + +# --------------------------------------------------------------------- +# The non-socket half, from PureSwift/Bluetooth +# --------------------------------------------------------------------- + +if(NOT EXISTS "${BLUETOOTH_PACKAGE_PATH}/CMakeLists.txt") + message(FATAL_ERROR + "PureSwift/Bluetooth not found at ${BLUETOOTH_PACKAGE_PATH}. " + "Set -DBLUETOOTH_PACKAGE_PATH=.") +endif() + +# Its own shared library is not wanted here — this build links the +# static archives into libbluetooth.so.3 instead. +set(BLUETOOTH_ABI_SHARED OFF CACHE BOOL "" FORCE) +set(BLUETOOTH_ABI_INSTALL_HEADERS OFF CACHE BOOL "" FORCE) + +add_subdirectory("${BLUETOOTH_PACKAGE_PATH}" "${CMAKE_CURRENT_BINARY_DIR}/Bluetooth") + +# --------------------------------------------------------------------- +# libbluetooth.so.3 +# --------------------------------------------------------------------- + +add_library(bluetooth3 SHARED cmake/empty.c) + +set_target_properties(bluetooth3 PROPERTIES + OUTPUT_NAME bluetooth + SOVERSION ${LIBBLUETOOTH_SOVERSION} + VERSION ${LIBBLUETOOTH_VERSION} + # The link is driven by swiftc, and `bluetooth3` would collide with + # the `Bluetooth` module; name the vestigial module explicitly. + Swift_MODULE_NAME libbluetooth_abi) + +# Every entry point is referenced only from outside, so the archives +# holding them must be linked whole or the linker drops the lot. +# +# The archives are named explicitly through `LINKER:` (which expands to +# `-Xlinker` here) rather than via `$`: +# swiftc's driver silently discards bare `.a` arguments, so the result +# would be a shared library with no symbols in it. +target_link_options(bluetooth3 PRIVATE + "LINKER:--whole-archive" + "LINKER:$" + "LINKER:$" + "LINKER:$" + "LINKER:$" + "LINKER:$" + "LINKER:--no-whole-archive") + +add_dependencies(bluetooth3 BluetoothABI BluetoothSDP BluetoothLinuxABI CBluetooth CBluetoothLinuxABI) + +target_link_libraries(bluetooth3 PRIVATE Bluetooth) + +# Pin the export list. Upstream exports everything and carries no +# version script; see scripts/gen_symbols.py for why we do not, and for +# the audit that owes before this list is final. +target_link_options(bluetooth3 PRIVATE + "LINKER:--version-script=${LIBBLUETOOTH_VERSION_SCRIPT}") +set_target_properties(bluetooth3 PROPERTIES + LINK_DEPENDS "${LIBBLUETOOTH_VERSION_SCRIPT}") + +add_custom_target(generate-version-script + DEPENDS "${LIBBLUETOOTH_VERSION_SCRIPT}") +add_dependencies(bluetooth3 generate-version-script) + +if(BLUETOOTH_ABI_STATIC_STDLIB) + # Any process loading libbluetooth.so.3 otherwise pulls in + # libswiftCore. Measure the result against the 99 KB reference + # package before making this the default. + target_link_options(bluetooth3 PRIVATE "-static-stdlib") +endif() + +# --------------------------------------------------------------------- +# Install +# --------------------------------------------------------------------- + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/bluez.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/bluez.pc" + @ONLY) + +install(TARGETS bluetooth3 + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/bluez.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + +if(BLUETOOTH_ABI_INSTALL_HEADERS) + # The eleven headers the reference package installs. + file(GLOB LIBBLUETOOTH_HEADERS + "${CMAKE_CURRENT_SOURCE_DIR}/Sources/CBluetoothLinuxABI/include/bluetooth/*.h") + install(FILES ${LIBBLUETOOTH_HEADERS} + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/bluetooth") +endif() + +# --------------------------------------------------------------------- +# Export verification +# --------------------------------------------------------------------- +# +# Diffs the built library's dynamic symbol table against the generated +# list. Both a missing and an extra symbol fail, so the export surface +# cannot drift silently — this is the check that makes "the library +# still resolves everything the reference did" an assertion rather than +# a hope. + +add_custom_target(check-exports + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/check-exports.sh" + "$" + "${CMAKE_CURRENT_SOURCE_DIR}/scripts/exported.txt" + DEPENDS bluetooth3 generate-version-script + COMMENT "Checking exported symbols against scripts/exported.txt" + VERBATIM) diff --git a/Conformance/README.md b/Conformance/README.md new file mode 100644 index 0000000..2b6422b --- /dev/null +++ b/Conformance/README.md @@ -0,0 +1,52 @@ +# Conformance + +Differential conformance for `libbluetooth.so.3`: C programs compiled +twice — once against the reference library, once against ours — with the +outputs diffed and accepted deltas recorded. + +Because no BlueZ binary links the shared library (`bluetoothd`, +`bluetoothctl`, `btmon`, `hciconfig` and `sdptool` all statically link +`libbluetooth-internal.a`), "nothing crashed" tells us nothing. These +programs are the only signal that the replacement behaves like the +original. + +## Phase 1 — done, and it lives in PureSwift/Bluetooth + +The `bluetooth.c` and `bt_uuid_*` families are implemented there, so +their conformance harness is there too: `Conformance/compare.sh` in the +Bluetooth checkout, with `conformance_address.c` and +`conformance_uuid.c`. + +It can be pointed at the library this repository builds: + +``` +cmake --build .build/cmake +BLUEZ_SOURCE= \ + ../Bluetooth/Conformance/compare.sh .build/cmake/libbluetooth.so.3.19.15 +``` + +## Phases 2–4 — to be added here + +One driver per symbol family, following the same shape: + +| Driver | Covers | Phase | +|---|---|---| +| `conformance_hci_strings.c` | `hci_*tostr`, `lmp_*`, `pal_*` | 2 | +| `conformance_hci.c` | device and command wrappers | 3 | +| `conformance_sdp_codec.c` | `sdp_gen_pdu` / `sdp_extract_pdu` / `sdp_extract_attr` | 4a | +| `conformance_sdp_session.c` | session, registration, async requests | 4b | + +Two cheaper sources of coverage come first, though: + +- **BlueZ's own unit tests, reused directly.** `unit/test-uuid.c`, + `unit/test-lib.c` and `unit/test-sdp.c` link + `libbluetooth-internal.la` today; pointing them at our library instead + is free coverage for phases 1 and 4. +- **Fuzzing the parsers.** `bt_string_to_uuid`, `str2ba`, `bachk` and + `sdp_extract_pdu` against random and semi-structured input, comparing + both implementations byte for byte. `sdp_extract_pdu` is the one place + arbitrary remote bytes reach the library. + +Then an `LD_PRELOAD` substitution run against a real external consumer, +once one is identified — `bluez-cups` and the Python bindings are the +obvious candidates. diff --git a/Conformance/compare.sh b/Conformance/compare.sh new file mode 100755 index 0000000..236b512 --- /dev/null +++ b/Conformance/compare.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash +# +# Differential conformance: build each driver twice — once against the +# reference libbluetooth.so.3, once against ours — and diff the output. +# +# Drivers: +# conformance_hci_strings.c the hci_*tostr/hci_strto*, lmp_*, pal_* +# family (21 symbols). Exported directly +# by the system library, so this driver +# links against it as-is. +# +# The phase-1 (bluetooth.c + bt_uuid_*) and SDP drivers live in the +# PureSwift/Bluetooth checkout instead, alongside the symbols they +# cover — see Conformance/README.md. +# +# Both sides of each comparison compile against the *same* (vendored) +# headers, so the only variable is which implementation is linked. +# +# Usage: +# SWIFTPM_BLUETOOTH_CABI=1 swift build +# Conformance/compare.sh [path-to-libBluetoothLinuxABI.so] +# +# Environment: +# BT_REFERENCE_LIB path to the reference libbluetooth.so.3 +# +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BUILD="${ROOT}/.build/conformance" +TRIPLE="$(swift -print-target-info | sed -n 's/.*"unversionedTriple": "\([^"]*\)".*/\1/p' | head -1)" +OURS="${1:-${ROOT}/.build/${TRIPLE}/debug/libBluetoothLinuxABI.so}" + +find_reference() { + local found + found="$(/sbin/ldconfig -p 2>/dev/null \ + | sed -n 's/.*libbluetooth\.so\.3 (libc6[^)]*) => \(.*\)/\1/p' | head -1)" + if [[ -n "${found}" ]]; then + echo "${found}" + return + fi + local dir + for dir in /usr/lib/"$(uname -m)"-linux-gnu /usr/lib64 /usr/lib /lib; do + if [[ -f "${dir}/libbluetooth.so.3" ]]; then + echo "${dir}/libbluetooth.so.3" + return + fi + done +} + +REFERENCE="${BT_REFERENCE_LIB:-$(find_reference)}" + +if [[ ! -f "${OURS}" ]]; then + echo "error: ${OURS} not found; run 'SWIFTPM_BLUETOOTH_CABI=1 swift build' first" >&2 + exit 1 +fi + +if [[ -z "${REFERENCE}" || ! -f "${REFERENCE}" ]]; then + echo "error: reference libbluetooth.so.3 not found; install libbluetooth3 or set BT_REFERENCE_LIB" >&2 + exit 1 +fi + +mkdir -p "${BUILD}" + +# The drivers include , so stage the vendored headers +# under that prefix and use them for every build. +mkdir -p "${BUILD}/include/bluetooth" +cp "${ROOT}"/Sources/CBluetoothLinuxABI/include/bluetooth/*.h "${BUILD}/include/bluetooth/" + +CFLAGS=(-O0 -g -I "${BUILD}/include") +status=0 + +compare() { + local name="$1" reference="$2" ours="$3" + + "${reference}" > "${BUILD}/${name}.reference.txt" 2>&1 || true + "${ours}" > "${BUILD}/${name}.ours.txt" 2>&1 || true + + if diff -u "${BUILD}/${name}.reference.txt" "${BUILD}/${name}.ours.txt" \ + > "${BUILD}/${name}.diff.txt"; then + echo "conformance/${name}: identical output ($(wc -l < "${BUILD}/${name}.reference.txt") lines)" + return 0 + fi + + grep -E '^[+-]' "${BUILD}/${name}.diff.txt" | grep -vE '^(\+\+\+|---)' | sed 's/^[+-]//' \ + | sort -u > "${BUILD}/${name}.changed.txt" + + if [[ -f "${ROOT}/Conformance/known-differences.txt" ]]; then + grep -vE '^\s*(#|$)' "${ROOT}/Conformance/known-differences.txt" | sort -u \ + > "${BUILD}/known.txt" + else + : > "${BUILD}/known.txt" + fi + + comm -23 "${BUILD}/${name}.changed.txt" "${BUILD}/known.txt" \ + > "${BUILD}/${name}.unexpected.txt" + + if [[ -s "${BUILD}/${name}.unexpected.txt" ]]; then + echo "conformance/${name}: $(wc -l < "${BUILD}/${name}.unexpected.txt") unexpected differing line(s):" >&2 + head -50 "${BUILD}/${name}.unexpected.txt" >&2 + echo "(full diff: ${BUILD}/${name}.diff.txt)" >&2 + return 1 + fi + + echo "conformance/${name}: all $(wc -l < "${BUILD}/${name}.changed.txt") differing lines are known-accepted" +} + +# --- HCI string converters ---------------------------------------------- + +cc "${CFLAGS[@]}" -o "${BUILD}/hci_strings.reference" \ + "${ROOT}/Conformance/conformance_hci_strings.c" "${REFERENCE}" +cc "${CFLAGS[@]}" -o "${BUILD}/hci_strings.ours" \ + "${ROOT}/Conformance/conformance_hci_strings.c" "${OURS}" \ + -Wl,-rpath,"$(dirname "${OURS}")" + +compare hci_strings "${BUILD}/hci_strings.reference" "${BUILD}/hci_strings.ours" || status=1 + +exit "${status}" diff --git a/Conformance/conformance_hci_strings.c b/Conformance/conformance_hci_strings.c new file mode 100644 index 0000000..caf2293 --- /dev/null +++ b/Conformance/conformance_hci_strings.c @@ -0,0 +1,217 @@ +/* + * Differential conformance driver for the HCI string converter family + * (21 symbols: hci_*tostr/hci_strto*, lmp_*, pal_*). + * + * All 21 are exported by the system libbluetooth.so.3 (verified + * against nm -D), so this driver links against it directly — no + * BlueZ source tree needed. + */ + +#include +#include +#include + +#include +#include +#include + +static void dump_bus(void) +{ + int buses[] = { + HCI_VIRTUAL, HCI_USB, HCI_PCCARD, HCI_UART, HCI_RS232, HCI_PCI, + HCI_SDIO, HCI_SPI, HCI_I2C, HCI_SMD, HCI_VIRTIO, HCI_IPC, 99 + }; + size_t i; + + for (i = 0; i < sizeof(buses) / sizeof(*buses); i++) { + printf("bustostr(%d) = \"%s\"\n", buses[i], hci_bustostr(buses[i])); + printf("dtypetostr(%d) = \"%s\"\n", buses[i], hci_dtypetostr(buses[i])); + } + + printf("typetostr(%d) = \"%s\"\n", HCI_PRIMARY, hci_typetostr(HCI_PRIMARY)); + printf("typetostr(%d) = \"%s\"\n", HCI_AMP, hci_typetostr(HCI_AMP)); + printf("typetostr(99) = \"%s\"\n", hci_typetostr(99)); +} + +static void dump_dflags(void) +{ + uint32_t flags[] = { + 0, + 1 << HCI_UP, + 1 << HCI_INIT, + (1 << HCI_UP) | (1 << HCI_RUNNING) | (1 << HCI_PSCAN), + 0xFFFFFFFF + }; + size_t i; + + for (i = 0; i < sizeof(flags) / sizeof(*flags); i++) { + char *s = hci_dflagstostr(flags[i]); + printf("dflagstostr(0x%08x) = \"%s\"\n", flags[i], s ? s : "(null)"); + free(s); + } +} + +static void dump_ptype(void) +{ + unsigned int values[] = { + 0, HCI_DM1, HCI_DM1 | HCI_DH1, HCI_DM1 | HCI_DM3 | HCI_DM5 | + HCI_DH1 | HCI_DH3 | HCI_DH5, 0xFFFF + }; + size_t i; + static const char *strs[] = { + "DM1", "DM1,DH1", "DM1,DH1,BOGUS", "bogus", NULL + }; + + for (i = 0; i < sizeof(values) / sizeof(*values); i++) { + char *s = hci_ptypetostr(values[i]); + printf("ptypetostr(0x%04x) = \"%s\"\n", values[i], s ? s : "(null)"); + free(s); + + s = hci_scoptypetostr(values[i]); + printf("scoptypetostr(0x%04x) = \"%s\"\n", values[i], s ? s : "(null)"); + free(s); + } + + for (i = 0; strs[i]; i++) { + unsigned int val = 0xdeadbeef; + int rc = hci_strtoptype((char *) strs[i], &val); + printf("strtoptype(\"%s\") = %d val=0x%x\n", strs[i], rc, val); + + val = 0xdeadbeef; + rc = hci_strtoscoptype((char *) strs[i], &val); + printf("strtoscoptype(\"%s\") = %d val=0x%x\n", strs[i], rc, val); + } +} + +static void dump_lp_lm(void) +{ + unsigned int values[] = { 0, HCI_LP_RSWITCH, HCI_LP_SNIFF | HCI_LP_PARK, 0xFF }; + size_t i; + static const char *lp_strs[] = { "RSWITCH", "SNIFF,PARK", "bogus", NULL }; + static const char *lm_strs[] = { "ACCEPT", "MASTER", "CENTRAL,AUTH", "bogus", NULL }; + + for (i = 0; i < sizeof(values) / sizeof(*values); i++) { + char *s = hci_lptostr(values[i]); + printf("lptostr(0x%02x) = \"%s\"\n", values[i], s ? s : "(null)"); + free(s); + + s = hci_lmtostr(values[i]); + printf("lmtostr(0x%02x) = \"%s\"\n", values[i], s ? s : "(null)"); + free(s); + } + + for (i = 0; lp_strs[i]; i++) { + unsigned int val = 0xdeadbeef; + int rc = hci_strtolp((char *) lp_strs[i], &val); + printf("strtolp(\"%s\") = %d val=0x%x\n", lp_strs[i], rc, val); + } + + for (i = 0; lm_strs[i]; i++) { + unsigned int val = 0xdeadbeef; + int rc = hci_strtolm((char *) lm_strs[i], &val); + printf("strtolm(\"%s\") = %d val=0x%x\n", lm_strs[i], rc, val); + } +} + +static void dump_commands(void) +{ + unsigned int cmds[] = { 0, 1, 227, 231, 9999 }; + size_t i; + uint8_t bitmap[64]; + + for (i = 0; i < sizeof(cmds) / sizeof(*cmds); i++) { + char *s = hci_cmdtostr(cmds[i]); + printf("cmdtostr(%u) = \"%s\"\n", cmds[i], s ? s : "(null)"); + free(s); + } + + memset(bitmap, 0, sizeof(bitmap)); + bitmap[0] = 0x01; /* bit 0: Inquiry */ + bitmap[0] |= 0x02; /* bit 1: Inquiry Cancel */ + bitmap[28] |= 0x08; /* bit 227: LE Read Supported States */ + { + char *s = hci_commandstostr(bitmap, " ", 60); + printf("commandstostr(narrow) = \"%s\"\n", s ? s : "(null)"); + free(s); + + s = hci_commandstostr(bitmap, NULL, 200); + printf("commandstostr(wide, no pref) = \"%s\"\n", s ? s : "(null)"); + free(s); + } +} + +static void dump_versions(void) +{ + unsigned int values[] = { 0x00, 0x09, 0x0d, 0xff }; + size_t i; + static const char *strs[] = { "5.0", "1.0b", "bogus", NULL }; + + for (i = 0; i < sizeof(values) / sizeof(*values); i++) { + char *s = hci_vertostr(values[i]); + printf("vertostr(0x%02x) = \"%s\"\n", values[i], s ? s : "(null)"); + free(s); + + s = lmp_vertostr(values[i]); + printf("lmp_vertostr(0x%02x) = \"%s\"\n", values[i], s ? s : "(null)"); + free(s); + } + + { + unsigned int palValues[] = { 0x00, 0x01, 0xff }; + for (i = 0; i < sizeof(palValues) / sizeof(*palValues); i++) { + char *s = pal_vertostr(palValues[i]); + printf("pal_vertostr(0x%02x) = \"%s\"\n", palValues[i], s ? s : "(null)"); + free(s); + } + } + + for (i = 0; strs[i]; i++) { + unsigned int ver = 0xdeadbeef; + int rc = hci_strtover((char *) strs[i], &ver); + printf("strtover(\"%s\") = %d ver=0x%x\n", strs[i], rc, ver); + + ver = 0xdeadbeef; + rc = lmp_strtover((char *) strs[i], &ver); + printf("lmp_strtover(\"%s\") = %d ver=0x%x\n", strs[i], rc, ver); + + ver = 0xdeadbeef; + rc = pal_strtover((char *) strs[i], &ver); + printf("pal_strtover(\"%s\") = %d ver=0x%x\n", strs[i], rc, ver); + } +} + +static void dump_features(void) +{ + uint8_t features[8]; + char *s; + + memset(features, 0, sizeof(features)); + features[0] = LMP_3SLOT | LMP_5SLOT | LMP_ENCRYPT; + features[4] = LMP_LE; + features[6] = LMP_SIMPLE_PAIR; + + s = lmp_featurestostr(features, NULL, 200); + printf("lmp_featurestostr(wide) = \"%s\"\n", s ? s : "(null)"); + free(s); + + s = lmp_featurestostr(features, "\t", 30); + printf("lmp_featurestostr(narrow) = \"%s\"\n", s ? s : "(null)"); + free(s); + + memset(features, 0xff, sizeof(features)); + s = lmp_featurestostr(features, NULL, 200); + printf("lmp_featurestostr(all) = \"%s\"\n", s ? s : "(null)"); + free(s); +} + +int main(void) +{ + dump_bus(); + dump_dflags(); + dump_ptype(); + dump_lp_lm(); + dump_commands(); + dump_versions(); + dump_features(); + return 0; +} diff --git a/Package.swift b/Package.swift index 4e4b463..a5f1a6e 100644 --- a/Package.swift +++ b/Package.swift @@ -182,3 +182,51 @@ if buildDocs { ] } #endif + +// C ABI (libbluetooth.so.3 replacement) +// +// Off by default so the ordinary Swift build stays unaffected. Enable +// with `SWIFTPM_BLUETOOTH_CABI=1` to build the C surface — the vendored +// BlueZ headers plus the generated stub table for every symbol not +// implemented yet (see scripts/gen_stubs.py). +// +// SwiftPM builds the sources; the installable `libbluetooth.so.3` +// itself is built by CMake, which is the only one of the two that can +// set a soname, a version and an export list. +if ProcessInfo.processInfo.environment["SWIFTPM_BLUETOOTH_CABI"] == "1" { + package.products += [ + .library( + name: "BluetoothLinuxABI", + type: .dynamic, + targets: ["BluetoothLinuxABI"] + ) + ] + package.targets += [ + .target( + name: "CBluetoothLinuxABI", + exclude: [ + "README.md", + "include/bluetooth/LICENSE" + ] + ), + .target( + name: "BluetoothLinuxABI", + dependencies: [ + "CBluetoothLinuxABI", + // For bt_malloc/bt_malloc0/bt_free — CBluetoothLinuxABI + // declares them (bluetooth.h) but does not define them; + // BluetoothABI's CBluetooth does. Also gated behind + // SWIFTPM_BLUETOOTH_CABI=1, which SwiftPM propagates to + // this dependency's own manifest evaluation. + .product(name: "BluetoothABI", package: "Bluetooth") + ] + ), + .testTarget( + name: "BluetoothLinuxABITests", + dependencies: [ + "BluetoothLinuxABI", + "CBluetoothLinuxABI" + ] + ) + ] +} diff --git a/Sources/BluetoothLinuxABI/HCICommandsConfig.swift b/Sources/BluetoothLinuxABI/HCICommandsConfig.swift new file mode 100644 index 0000000..7015c04 --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCICommandsConfig.swift @@ -0,0 +1,468 @@ +// +// HCICommandsConfig.swift +// BluetoothLinux +// +// Controller configuration and status-parameter wrappers: class of +// device, voice setting, inquiry access codes, stored link keys, +// inquiry/AFH/inquiry-mode toggles, extended inquiry response, simple +// pairing, OOB data, transmit power, link policy/supervision timeout, +// AFH classification, and per-connection link quality/RSSI/AFH +// map/clock queries. +// + +import CBluetoothLinuxABI +import Glibc + +extension read_class_of_dev_rp: HCIStatusResponse {} +extension read_voice_setting_rp: HCIStatusResponse {} +extension read_current_iac_lap_rp: HCIStatusResponse {} +extension read_inquiry_scan_type_rp: HCIStatusResponse {} +extension write_inquiry_scan_type_rp: HCIStatusResponse {} +extension read_inquiry_mode_rp: HCIStatusResponse {} +extension write_inquiry_mode_rp: HCIStatusResponse {} +extension read_afh_mode_rp: HCIStatusResponse {} +extension write_afh_mode_rp: HCIStatusResponse {} +extension read_ext_inquiry_response_rp: HCIStatusResponse {} +extension write_ext_inquiry_response_rp: HCIStatusResponse {} +extension read_simple_pairing_mode_rp: HCIStatusResponse {} +extension write_simple_pairing_mode_rp: HCIStatusResponse {} +extension read_local_oob_data_rp: HCIStatusResponse {} +extension read_inq_response_tx_power_level_rp: HCIStatusResponse {} +extension write_inquiry_transmit_power_level_rp: HCIStatusResponse {} +extension read_transmit_power_level_rp: HCIStatusResponse {} +extension read_link_policy_rp: HCIStatusResponse {} +extension write_link_policy_rp: HCIStatusResponse {} +extension read_link_supervision_timeout_rp: HCIStatusResponse {} +extension write_link_supervision_timeout_rp: HCIStatusResponse {} +extension set_afh_classification_rp: HCIStatusResponse {} +extension read_link_quality_rp: HCIStatusResponse {} +extension read_rssi_rp: HCIStatusResponse {} +extension read_afh_map_rp: HCIStatusResponse {} +extension read_clock_rp: HCIStatusResponse {} + +// MARK: - Class of device + +/// `int hci_read_class_of_dev(int dd, uint8_t *cls, int to)` +@c(hci_read_class_of_dev) +public func hci_read_class_of_dev(_ dd: Int32, _ deviceClass: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard var response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_CLASS_OF_DEV), + response: read_class_of_dev_rp.self, timeout: timeout + ) else { return -1 } + guard let deviceClass else { errno = EINVAL; return -1 } + withUnsafeMutableBytes(of: &response.dev_class) { deviceClass.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 3) } + return 0 +} + +/// `int hci_write_class_of_dev(int dd, uint32_t cls, int to)` +@c(hci_write_class_of_dev) +public func hci_write_class_of_dev(_ dd: Int32, _ deviceClass: UInt32, _ timeout: Int32) -> Int32 { + var command = write_class_of_dev_cp() + command.dev_class.0 = UInt8(deviceClass & 0xff) + command.dev_class.1 = UInt8((deviceClass >> 8) & 0xff) + command.dev_class.2 = UInt8((deviceClass >> 16) & 0xff) + return hciCommand(dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_CLASS_OF_DEV), command: command, timeout: timeout) +} + +// MARK: - Voice setting + +/// `int hci_read_voice_setting(int dd, uint16_t *vs, int to)` +@c(hci_read_voice_setting) +public func hci_read_voice_setting(_ dd: Int32, _ voiceSetting: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_VOICE_SETTING), + response: read_voice_setting_rp.self, timeout: timeout + ) else { return -1 } + voiceSetting?.pointee = response.voice_setting + return 0 +} + +/// `int hci_write_voice_setting(int dd, uint16_t vs, int to)` +@c(hci_write_voice_setting) +public func hci_write_voice_setting(_ dd: Int32, _ voiceSetting: UInt16, _ timeout: Int32) -> Int32 { + var command = write_voice_setting_cp() + command.voice_setting = voiceSetting + return hciCommand(dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_VOICE_SETTING), command: command, timeout: timeout) +} + +// MARK: - Current IAC LAP + +/// `int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to)` +@c(hci_read_current_iac_lap) +public func hci_read_current_iac_lap(_ dd: Int32, _ numIAC: UnsafeMutablePointer?, _ lap: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard var response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_CURRENT_IAC_LAP), + response: read_current_iac_lap_rp.self, timeout: timeout + ) else { return -1 } + guard let numIAC else { errno = EINVAL; return -1 } + numIAC.pointee = response.num_current_iac + if let lap { + withUnsafeMutableBytes(of: &response.lap) { lap.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: Int(response.num_current_iac) * 3) } + } + return 0 +} + +/// `int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to)` +@c(hci_write_current_iac_lap) +public func hci_write_current_iac_lap(_ dd: Int32, _ numIAC: UInt8, _ lap: UnsafePointer?, _ timeout: Int32) -> Int32 { + guard let lap else { errno = EINVAL; return -1 } + var command = write_current_iac_lap_cp() + command.num_current_iac = numIAC + withUnsafeMutableBytes(of: &command.lap) { $0.copyMemory(from: UnsafeRawBufferPointer(start: lap, count: Int(numIAC) * 3)) } + return hciCommand( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_CURRENT_IAC_LAP), + commandBytes: withUnsafeBytes(of: &command) { Array($0.prefix(Int(numIAC) * 3 + 1)) }, timeout: timeout + ) +} + +// MARK: - Stored link keys + +/// `int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)` +@c(hci_read_stored_link_key) +public func hci_read_stored_link_key(_ dd: Int32, _ bdaddr: UnsafeMutablePointer?, _ all: UInt8, _ timeout: Int32) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = read_stored_link_key_cp() + command.bdaddr = bdaddr.pointee + command.read_all = all + return hciCommand(dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_STORED_LINK_KEY), command: command, timeout: timeout) +} + +/// `int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to)` +@c(hci_write_stored_link_key) +public func hci_write_stored_link_key(_ dd: Int32, _ bdaddr: UnsafeMutablePointer?, _ key: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let bdaddr, let key else { errno = EINVAL; return -1 } + var bytes = [UInt8](repeating: 0, count: 1 + 6 + 16) + bytes[0] = 1 + let address = bdaddr.pointee.b + bytes[1] = address.0 + bytes[2] = address.1 + bytes[3] = address.2 + bytes[4] = address.3 + bytes[5] = address.4 + bytes[6] = address.5 + for index in 0 ..< 16 { bytes[7 + index] = key[index] } + return hciCommand(dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_STORED_LINK_KEY), commandBytes: bytes, timeout: timeout) +} + +/// `int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to)` +@c(hci_delete_stored_link_key) +public func hci_delete_stored_link_key(_ dd: Int32, _ bdaddr: UnsafeMutablePointer?, _ all: UInt8, _ timeout: Int32) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = delete_stored_link_key_cp() + command.bdaddr = bdaddr.pointee + command.delete_all = all + return hciCommand(dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_DELETE_STORED_LINK_KEY), command: command, timeout: timeout) +} + +// MARK: - Inquiry scan type + +/// `int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to)` +@c(hci_read_inquiry_scan_type) +public func hci_read_inquiry_scan_type(_ dd: Int32, _ type: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_INQUIRY_SCAN_TYPE), + response: read_inquiry_scan_type_rp.self, timeout: timeout + ) else { return -1 } + type?.pointee = response.type + return 0 +} + +/// `int hci_write_inquiry_scan_type(int dd, uint8_t type, int to)` +@c(hci_write_inquiry_scan_type) +public func hci_write_inquiry_scan_type(_ dd: Int32, _ type: UInt8, _ timeout: Int32) -> Int32 { + var command = write_inquiry_scan_type_cp() + command.type = type + guard hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_INQUIRY_SCAN_TYPE), + command: command, response: write_inquiry_scan_type_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +// MARK: - Inquiry mode + +/// `int hci_read_inquiry_mode(int dd, uint8_t *mode, int to)` +@c(hci_read_inquiry_mode) +public func hci_read_inquiry_mode(_ dd: Int32, _ mode: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_INQUIRY_MODE), + response: read_inquiry_mode_rp.self, timeout: timeout + ) else { return -1 } + mode?.pointee = response.mode + return 0 +} + +/// `int hci_write_inquiry_mode(int dd, uint8_t mode, int to)` +@c(hci_write_inquiry_mode) +public func hci_write_inquiry_mode(_ dd: Int32, _ mode: UInt8, _ timeout: Int32) -> Int32 { + var command = write_inquiry_mode_cp() + command.mode = mode + guard hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_INQUIRY_MODE), + command: command, response: write_inquiry_mode_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +// MARK: - AFH mode + +/// `int hci_read_afh_mode(int dd, uint8_t *mode, int to)` +@c(hci_read_afh_mode) +public func hci_read_afh_mode(_ dd: Int32, _ mode: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_AFH_MODE), + response: read_afh_mode_rp.self, timeout: timeout + ) else { return -1 } + mode?.pointee = response.mode + return 0 +} + +/// `int hci_write_afh_mode(int dd, uint8_t mode, int to)` +@c(hci_write_afh_mode) +public func hci_write_afh_mode(_ dd: Int32, _ mode: UInt8, _ timeout: Int32) -> Int32 { + var command = write_afh_mode_cp() + command.mode = mode + guard hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_AFH_MODE), + command: command, response: write_afh_mode_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +// MARK: - Extended inquiry response + +/// `int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to)` +@c(hci_read_ext_inquiry_response) +public func hci_read_ext_inquiry_response(_ dd: Int32, _ fec: UnsafeMutablePointer?, _ data: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard var response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_EXT_INQUIRY_RESPONSE), + response: read_ext_inquiry_response_rp.self, timeout: timeout + ) else { return -1 } + guard let fec, let data else { errno = EINVAL; return -1 } + fec.pointee = response.fec + withUnsafeMutableBytes(of: &response.data) { data.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: Int(HCI_MAX_EIR_LENGTH)) } + return 0 +} + +/// `int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to)` +@c(hci_write_ext_inquiry_response) +public func hci_write_ext_inquiry_response(_ dd: Int32, _ fec: UInt8, _ data: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let data else { errno = EINVAL; return -1 } + var command = write_ext_inquiry_response_cp() + command.fec = fec + withUnsafeMutableBytes(of: &command.data) { $0.copyMemory(from: UnsafeRawBufferPointer(start: data, count: Int(HCI_MAX_EIR_LENGTH))) } + guard hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_EXT_INQUIRY_RESPONSE), + command: command, response: write_ext_inquiry_response_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +// MARK: - Simple pairing mode + +/// `int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to)` +@c(hci_read_simple_pairing_mode) +public func hci_read_simple_pairing_mode(_ dd: Int32, _ mode: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_SIMPLE_PAIRING_MODE), + response: read_simple_pairing_mode_rp.self, timeout: timeout + ) else { return -1 } + mode?.pointee = response.mode + return 0 +} + +/// `int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to)` +@c(hci_write_simple_pairing_mode) +public func hci_write_simple_pairing_mode(_ dd: Int32, _ mode: UInt8, _ timeout: Int32) -> Int32 { + var command = write_simple_pairing_mode_cp() + command.mode = mode + guard hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_SIMPLE_PAIRING_MODE), + command: command, response: write_simple_pairing_mode_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +// MARK: - Out-of-band data + +/// `int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to)` +@c(hci_read_local_oob_data) +public func hci_read_local_oob_data(_ dd: Int32, _ hash: UnsafeMutablePointer?, _ randomizer: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard var response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_LOCAL_OOB_DATA), + response: read_local_oob_data_rp.self, timeout: timeout + ) else { return -1 } + guard let hash, let randomizer else { errno = EINVAL; return -1 } + withUnsafeMutableBytes(of: &response.hash) { hash.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 16) } + withUnsafeMutableBytes(of: &response.randomizer) { randomizer.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 16) } + return 0 +} + +// MARK: - Transmit power + +/// `int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to)` +@c(hci_read_inq_response_tx_power_level) +public func hci_read_inq_response_tx_power_level(_ dd: Int32, _ level: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL), + response: read_inq_response_tx_power_level_rp.self, timeout: timeout + ) else { return -1 } + level?.pointee = response.level + return 0 +} + +/// `int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to)` +@c(hci_read_inquiry_transmit_power_level) +public func hci_read_inquiry_transmit_power_level(_ dd: Int32, _ level: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + hci_read_inq_response_tx_power_level(dd, level, timeout) +} + +/// `int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to)` +@c(hci_write_inquiry_transmit_power_level) +public func hci_write_inquiry_transmit_power_level(_ dd: Int32, _ level: Int8, _ timeout: Int32) -> Int32 { + var command = write_inquiry_transmit_power_level_cp() + command.level = level + guard hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL), + command: command, response: write_inquiry_transmit_power_level_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to)` +@c(hci_read_transmit_power_level) +public func hci_read_transmit_power_level(_ dd: Int32, _ handle: UInt16, _ type: UInt8, _ level: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + var command = read_transmit_power_level_cp() + command.handle = handle + command.type = type + guard let response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_TRANSMIT_POWER_LEVEL), + command: command, response: read_transmit_power_level_rp.self, timeout: timeout + ) else { return -1 } + level?.pointee = response.level + return 0 +} + +// MARK: - Link policy / supervision timeout + +/// `int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to)` +@c(hci_read_link_policy) +public func hci_read_link_policy(_ dd: Int32, _ handle: UInt16, _ policy: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_LINK_POLICY), ocf: Int32(OCF_READ_LINK_POLICY), + command: handle, response: read_link_policy_rp.self, timeout: timeout + ) else { return -1 } + policy?.pointee = response.policy + return 0 +} + +/// `int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to)` +@c(hci_write_link_policy) +public func hci_write_link_policy(_ dd: Int32, _ handle: UInt16, _ policy: UInt16, _ timeout: Int32) -> Int32 { + var command = write_link_policy_cp() + command.handle = handle + command.policy = policy + guard hciRequest( + dd, ogf: Int32(OGF_LINK_POLICY), ocf: Int32(OCF_WRITE_LINK_POLICY), + command: command, response: write_link_policy_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to)` +@c(hci_read_link_supervision_timeout) +public func hci_read_link_supervision_timeout(_ dd: Int32, _ handle: UInt16, _ linkTimeout: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_LINK_SUPERVISION_TIMEOUT), + command: handle, response: read_link_supervision_timeout_rp.self, timeout: timeout + ) else { return -1 } + linkTimeout?.pointee = response.timeout + return 0 +} + +/// `int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to)` +@c(hci_write_link_supervision_timeout) +public func hci_write_link_supervision_timeout(_ dd: Int32, _ handle: UInt16, _ linkTimeout: UInt16, _ timeout: Int32) -> Int32 { + var command = write_link_supervision_timeout_cp() + command.handle = handle + command.timeout = linkTimeout + guard hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_WRITE_LINK_SUPERVISION_TIMEOUT), + command: command, response: write_link_supervision_timeout_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +// MARK: - AFH classification + +/// `int hci_set_afh_classification(int dd, uint8_t *map, int to)` +@c(hci_set_afh_classification) +public func hci_set_afh_classification(_ dd: Int32, _ map: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let map else { errno = EINVAL; return -1 } + var command = set_afh_classification_cp() + withUnsafeMutableBytes(of: &command.map) { $0.copyMemory(from: UnsafeRawBufferPointer(start: map, count: 10)) } + guard hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_SET_AFH_CLASSIFICATION), + command: command, response: set_afh_classification_rp.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +// MARK: - Status parameters (link quality / RSSI / AFH map / clock) + +/// `int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to)` +@c(hci_read_link_quality) +public func hci_read_link_quality(_ dd: Int32, _ handle: UInt16, _ linkQuality: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_STATUS_PARAM), ocf: Int32(OCF_READ_LINK_QUALITY), + command: handle, response: read_link_quality_rp.self, timeout: timeout + ) else { return -1 } + linkQuality?.pointee = response.link_quality + return 0 +} + +/// `int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to)` +@c(hci_read_rssi) +public func hci_read_rssi(_ dd: Int32, _ handle: UInt16, _ rssi: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_STATUS_PARAM), ocf: Int32(OCF_READ_RSSI), + command: handle, response: read_rssi_rp.self, timeout: timeout + ) else { return -1 } + rssi?.pointee = response.rssi + return 0 +} + +/// `int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to)` +@c(hci_read_afh_map) +public func hci_read_afh_map(_ dd: Int32, _ handle: UInt16, _ mode: UnsafeMutablePointer?, _ map: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard var response = hciRequest( + dd, ogf: Int32(OGF_STATUS_PARAM), ocf: Int32(OCF_READ_AFH_MAP), + command: handle, response: read_afh_map_rp.self, timeout: timeout + ) else { return -1 } + guard let mode, let map else { errno = EINVAL; return -1 } + mode.pointee = response.mode + withUnsafeMutableBytes(of: &response.map) { map.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 10) } + return 0 +} + +/// `int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to)` +@c(hci_read_clock) +public func hci_read_clock( + _ dd: Int32, + _ handle: UInt16, + _ which: UInt8, + _ clock: UnsafeMutablePointer?, + _ accuracy: UnsafeMutablePointer?, + _ timeout: Int32 +) -> Int32 { + var command = read_clock_cp() + command.handle = handle + command.which_clock = which + guard let response = hciRequest( + dd, ogf: Int32(OGF_STATUS_PARAM), ocf: Int32(OCF_READ_CLOCK), + command: command, response: read_clock_rp.self, timeout: timeout + ) else { return -1 } + clock?.pointee = response.clock + accuracy?.pointee = response.accuracy + return 0 +} diff --git a/Sources/BluetoothLinuxABI/HCICommandsConnection.swift b/Sources/BluetoothLinuxABI/HCICommandsConnection.swift new file mode 100644 index 0000000..ab19e97 --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCICommandsConnection.swift @@ -0,0 +1,138 @@ +// +// HCICommandsConnection.swift +// BluetoothLinux +// +// Connection- and link-management HCI command wrappers: establishing +// and tearing down ACL links, and the link-policy state changes that +// wait for their own completion event rather than an immediate +// command-complete (authentication, encryption, role switch, park +// mode, link key changes). +// + +import CBluetoothLinuxABI +import Glibc + +extension evt_conn_complete: HCIStatusResponse {} +extension evt_disconn_complete: HCIStatusResponse {} +extension evt_auth_complete: HCIStatusResponse {} +extension evt_encrypt_change: HCIStatusResponse {} +extension evt_change_conn_link_key_complete: HCIStatusResponse {} +extension evt_role_change: HCIStatusResponse {} +extension evt_mode_change: HCIStatusResponse {} + +/// `int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to)` +@c(hci_create_connection) +public func hci_create_connection( + _ dd: Int32, + _ bdaddr: UnsafePointer?, + _ ptype: UInt16, + _ clockOffset: UInt16, + _ roleSwitch: UInt8, + _ handle: UnsafeMutablePointer?, + _ timeout: Int32 +) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = create_conn_cp() + command.bdaddr = bdaddr.pointee + command.pkt_type = ptype + command.pscan_rep_mode = 0x02 + command.clock_offset = clockOffset + command.role_switch = roleSwitch + + guard let response = hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_CREATE_CONN), event: Int32(EVT_CONN_COMPLETE), + command: command, response: evt_conn_complete.self, timeout: timeout + ) else { return -1 } + handle?.pointee = response.handle + return 0 +} + +/// `int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to)` +@c(hci_disconnect) +public func hci_disconnect(_ dd: Int32, _ handle: UInt16, _ reason: UInt8, _ timeout: Int32) -> Int32 { + var command = disconnect_cp() + command.handle = handle + command.reason = reason + guard hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_DISCONNECT), event: Int32(EVT_DISCONN_COMPLETE), + command: command, response: evt_disconn_complete.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_authenticate_link(int dd, uint16_t handle, int to)` +@c(hci_authenticate_link) +public func hci_authenticate_link(_ dd: Int32, _ handle: UInt16, _ timeout: Int32) -> Int32 { + var command = auth_requested_cp() + command.handle = handle + guard hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_AUTH_REQUESTED), event: Int32(EVT_AUTH_COMPLETE), + command: command, response: evt_auth_complete.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to)` +@c(hci_encrypt_link) +public func hci_encrypt_link(_ dd: Int32, _ handle: UInt16, _ encrypt: UInt8, _ timeout: Int32) -> Int32 { + var command = set_conn_encrypt_cp() + command.handle = handle + command.encrypt = encrypt + guard hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_SET_CONN_ENCRYPT), event: Int32(EVT_ENCRYPT_CHANGE), + command: command, response: evt_encrypt_change.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_change_link_key(int dd, uint16_t handle, int to)` +@c(hci_change_link_key) +public func hci_change_link_key(_ dd: Int32, _ handle: UInt16, _ timeout: Int32) -> Int32 { + var command = change_conn_link_key_cp() + command.handle = handle + guard hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_CHANGE_CONN_LINK_KEY), event: Int32(EVT_CHANGE_CONN_LINK_KEY_COMPLETE), + command: command, response: evt_change_conn_link_key_complete.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to)` +@c(hci_switch_role) +public func hci_switch_role(_ dd: Int32, _ bdaddr: UnsafeMutablePointer?, _ role: UInt8, _ timeout: Int32) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = switch_role_cp() + command.bdaddr = bdaddr.pointee + command.role = role + guard hciRequest( + dd, ogf: Int32(OGF_LINK_POLICY), ocf: Int32(OCF_SWITCH_ROLE), event: Int32(EVT_ROLE_CHANGE), + command: command, response: evt_role_change.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to)` +@c(hci_park_mode) +public func hci_park_mode(_ dd: Int32, _ handle: UInt16, _ maxInterval: UInt16, _ minInterval: UInt16, _ timeout: Int32) -> Int32 { + var command = park_mode_cp() + command.handle = handle + command.max_interval = maxInterval + command.min_interval = minInterval + guard hciRequest( + dd, ogf: Int32(OGF_LINK_POLICY), ocf: Int32(OCF_PARK_MODE), event: Int32(EVT_MODE_CHANGE), + command: command, response: evt_mode_change.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_exit_park_mode(int dd, uint16_t handle, int to)` +@c(hci_exit_park_mode) +public func hci_exit_park_mode(_ dd: Int32, _ handle: UInt16, _ timeout: Int32) -> Int32 { + var command = exit_park_mode_cp() + command.handle = handle + guard hciRequest( + dd, ogf: Int32(OGF_LINK_POLICY), ocf: Int32(OCF_EXIT_PARK_MODE), event: Int32(EVT_MODE_CHANGE), + command: command, response: evt_mode_change.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} diff --git a/Sources/BluetoothLinuxABI/HCICommandsInfo.swift b/Sources/BluetoothLinuxABI/HCICommandsInfo.swift new file mode 100644 index 0000000..c77762d --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCICommandsInfo.swift @@ -0,0 +1,118 @@ +// +// HCICommandsInfo.swift +// BluetoothLinux +// +// Local controller information/identity: version, supported +// commands/features, address, and the local device name. +// + +import CBluetoothLinuxABI +import Glibc + +extension read_local_version_rp: HCIStatusResponse {} +extension read_local_commands_rp: HCIStatusResponse {} +extension read_local_features_rp: HCIStatusResponse {} +extension read_local_ext_features_rp: HCIStatusResponse {} +extension read_bd_addr_rp: HCIStatusResponse {} +extension read_local_name_rp: HCIStatusResponse {} + +/// `int hci_read_local_version(int dd, struct hci_version *ver, int to)` +@c(hci_read_local_version) +public func hci_read_local_version(_ dd: Int32, _ version: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let version else { errno = EINVAL; return -1 } + guard let response = hciRequest( + dd, ogf: Int32(OGF_INFO_PARAM), ocf: Int32(OCF_READ_LOCAL_VERSION), + response: read_local_version_rp.self, timeout: timeout + ) else { return -1 } + version.pointee.manufacturer = response.manufacturer + version.pointee.hci_ver = response.hci_ver + version.pointee.hci_rev = response.hci_rev + version.pointee.lmp_ver = response.lmp_ver + version.pointee.lmp_subver = response.lmp_subver + return 0 +} + +/// `int hci_read_local_commands(int dd, uint8_t *commands, int to)` +@c(hci_read_local_commands) +public func hci_read_local_commands(_ dd: Int32, _ commands: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard var response = hciRequest( + dd, ogf: Int32(OGF_INFO_PARAM), ocf: Int32(OCF_READ_LOCAL_COMMANDS), + response: read_local_commands_rp.self, timeout: timeout + ) else { return -1 } + if let commands { + withUnsafeMutableBytes(of: &response.commands) { commands.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 64) } + } + return 0 +} + +/// `int hci_read_local_features(int dd, uint8_t *features, int to)` +@c(hci_read_local_features) +public func hci_read_local_features(_ dd: Int32, _ features: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard var response = hciRequest( + dd, ogf: Int32(OGF_INFO_PARAM), ocf: Int32(OCF_READ_LOCAL_FEATURES), + response: read_local_features_rp.self, timeout: timeout + ) else { return -1 } + if let features { + withUnsafeMutableBytes(of: &response.features) { features.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 8) } + } + return 0 +} + +/// `int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to)` +@c(hci_read_local_ext_features) +public func hci_read_local_ext_features( + _ dd: Int32, + _ page: UInt8, + _ maxPage: UnsafeMutablePointer?, + _ features: UnsafeMutablePointer?, + _ timeout: Int32 +) -> Int32 { + var command = read_local_ext_features_cp() + command.page_num = page + guard var response = hciRequest( + dd, ogf: Int32(OGF_INFO_PARAM), ocf: Int32(OCF_READ_LOCAL_EXT_FEATURES), + command: command, response: read_local_ext_features_rp.self, timeout: timeout + ) else { return -1 } + maxPage?.pointee = response.max_page_num + if let features { + withUnsafeMutableBytes(of: &response.features) { features.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 8) } + } + return 0 +} + +/// `int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to)` +@c(hci_read_bd_addr) +public func hci_read_bd_addr(_ dd: Int32, _ bdaddr: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_INFO_PARAM), ocf: Int32(OCF_READ_BD_ADDR), + response: read_bd_addr_rp.self, timeout: timeout + ) else { return -1 } + bdaddr?.pointee = response.bdaddr + return 0 +} + +/// `int hci_read_local_name(int dd, int len, char *name, int to)` +@c(hci_read_local_name) +public func hci_read_local_name(_ dd: Int32, _ len: Int32, _ name: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let name, len > 0 else { errno = EINVAL; return -1 } + guard var response = hciRequest( + dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_READ_LOCAL_NAME), + response: read_local_name_rp.self, timeout: timeout + ) else { return -1 } + withUnsafeMutableBytes(of: &response.name) { buffer in + buffer[247] = 0 + _ = strncpy(name, buffer.bindMemory(to: CChar.self).baseAddress!, Int(len)) + } + return 0 +} + +/// `int hci_write_local_name(int dd, const char *name, int to)` +@c(hci_write_local_name) +public func hci_write_local_name(_ dd: Int32, _ name: UnsafePointer?, _ timeout: Int32) -> Int32 { + guard let name else { errno = EINVAL; return -1 } + var command = change_local_name_cp() + withUnsafeMutableBytes(of: &command.name) { buffer in + _ = strncpy(buffer.bindMemory(to: CChar.self).baseAddress!, name, buffer.count - 1) + } + return hciCommand(dd, ogf: Int32(OGF_HOST_CTL), ocf: Int32(OCF_CHANGE_LOCAL_NAME), command: command, timeout: timeout) +} diff --git a/Sources/BluetoothLinuxABI/HCICommandsLE.swift b/Sources/BluetoothLinuxABI/HCICommandsLE.swift new file mode 100644 index 0000000..73256cf --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCICommandsLE.swift @@ -0,0 +1,232 @@ +// +// HCICommandsLE.swift +// BluetoothLinux +// +// LE controller commands: the white list and resolving list (both +// used for LE connection/scan filtering), scan and advertising +// control, and LE connection establishment/update. +// + +import CBluetoothLinuxABI +import Glibc + +extension evt_le_connection_complete: HCIStatusResponse {} +extension evt_le_connection_update_complete: HCIStatusResponse {} +extension evt_le_read_remote_used_features_complete: HCIStatusResponse {} +extension le_read_white_list_size_rp: HCIStatusResponse {} +extension le_read_resolv_list_size_rp: HCIStatusResponse {} + +/// `int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)` +@c(hci_le_add_white_list) +public func hci_le_add_white_list(_ dd: Int32, _ bdaddr: UnsafePointer?, _ type: UInt8, _ timeout: Int32) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = le_add_device_to_white_list_cp() + command.bdaddr_type = type + command.bdaddr = bdaddr.pointee + return hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_ADD_DEVICE_TO_WHITE_LIST), command: command, timeout: timeout) +} + +/// `int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)` +@c(hci_le_rm_white_list) +public func hci_le_rm_white_list(_ dd: Int32, _ bdaddr: UnsafePointer?, _ type: UInt8, _ timeout: Int32) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = le_remove_device_from_white_list_cp() + command.bdaddr_type = type + command.bdaddr = bdaddr.pointee + return hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST), command: command, timeout: timeout) +} + +/// `int hci_le_read_white_list_size(int dd, uint8_t *size, int to)` +@c(hci_le_read_white_list_size) +public func hci_le_read_white_list_size(_ dd: Int32, _ size: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_READ_WHITE_LIST_SIZE), + response: le_read_white_list_size_rp.self, timeout: timeout + ) else { return -1 } + size?.pointee = response.size + return 0 +} + +/// `int hci_le_clear_white_list(int dd, int to)` +@c(hci_le_clear_white_list) +public func hci_le_clear_white_list(_ dd: Int32, _ timeout: Int32) -> Int32 { + hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_CLEAR_WHITE_LIST), timeout: timeout) +} + +/// `int hci_le_add_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, uint8_t *peer_irk, uint8_t *local_irk, int to)` +@c(hci_le_add_resolving_list) +public func hci_le_add_resolving_list( + _ dd: Int32, + _ bdaddr: UnsafePointer?, + _ type: UInt8, + _ peerIRK: UnsafePointer?, + _ localIRK: UnsafePointer?, + _ timeout: Int32 +) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = le_add_device_to_resolv_list_cp() + command.bdaddr_type = type + command.bdaddr = bdaddr.pointee + if let peerIRK { + withUnsafeMutableBytes(of: &command.peer_irk) { $0.copyMemory(from: UnsafeRawBufferPointer(start: peerIRK, count: 16)) } + } + if let localIRK { + withUnsafeMutableBytes(of: &command.local_irk) { $0.copyMemory(from: UnsafeRawBufferPointer(start: localIRK, count: 16)) } + } + return hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_ADD_DEVICE_TO_RESOLV_LIST), command: command, timeout: timeout) +} + +/// `int hci_le_rm_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to)` +@c(hci_le_rm_resolving_list) +public func hci_le_rm_resolving_list(_ dd: Int32, _ bdaddr: UnsafePointer?, _ type: UInt8, _ timeout: Int32) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = le_remove_device_from_resolv_list_cp() + command.bdaddr_type = type + command.bdaddr = bdaddr.pointee + return hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_REMOVE_DEVICE_FROM_RESOLV_LIST), command: command, timeout: timeout) +} + +/// `int hci_le_clear_resolving_list(int dd, int to)` +@c(hci_le_clear_resolving_list) +public func hci_le_clear_resolving_list(_ dd: Int32, _ timeout: Int32) -> Int32 { + hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_CLEAR_RESOLV_LIST), timeout: timeout) +} + +/// `int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to)` +@c(hci_le_read_resolving_list_size) +public func hci_le_read_resolving_list_size(_ dd: Int32, _ size: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let response = hciRequest( + dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_READ_RESOLV_LIST_SIZE), + response: le_read_resolv_list_size_rp.self, timeout: timeout + ) else { return -1 } + size?.pointee = response.size + return 0 +} + +/// `int hci_le_set_address_resolution_enable(int dd, uint8_t enable, int to)` +@c(hci_le_set_address_resolution_enable) +public func hci_le_set_address_resolution_enable(_ dd: Int32, _ enable: UInt8, _ timeout: Int32) -> Int32 { + var command = le_set_address_resolution_enable_cp() + command.enable = enable + return hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_SET_ADDRESS_RESOLUTION_ENABLE), command: command, timeout: timeout) +} + +/// `int hci_le_set_scan_enable(int dd, uint8_t enable, uint8_t filter_dup, int to)` +@c(hci_le_set_scan_enable) +public func hci_le_set_scan_enable(_ dd: Int32, _ enable: UInt8, _ filterDuplicates: UInt8, _ timeout: Int32) -> Int32 { + var command = le_set_scan_enable_cp() + command.enable = enable + command.filter_dup = filterDuplicates + return hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_SET_SCAN_ENABLE), command: command, timeout: timeout) +} + +/// `int hci_le_set_scan_parameters(int dd, uint8_t type, uint16_t interval, uint16_t window, uint8_t own_type, uint8_t filter, int to)` +@c(hci_le_set_scan_parameters) +public func hci_le_set_scan_parameters( + _ dd: Int32, + _ type: UInt8, + _ interval: UInt16, + _ window: UInt16, + _ ownType: UInt8, + _ filter: UInt8, + _ timeout: Int32 +) -> Int32 { + var command = le_set_scan_parameters_cp() + command.type = type + command.interval = interval + command.window = window + command.own_bdaddr_type = ownType + command.filter = filter + return hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_SET_SCAN_PARAMETERS), command: command, timeout: timeout) +} + +/// `int hci_le_set_advertise_enable(int dd, uint8_t enable, int to)` +@c(hci_le_set_advertise_enable) +public func hci_le_set_advertise_enable(_ dd: Int32, _ enable: UInt8, _ timeout: Int32) -> Int32 { + var command = le_set_advertise_enable_cp() + command.enable = enable + return hciStatus(dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_SET_ADVERTISE_ENABLE), command: command, timeout: timeout) +} + +/// `int hci_le_create_conn(int dd, uint16_t interval, uint16_t window, uint8_t initiator_filter, uint8_t peer_bdaddr_type, bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t supervision_timeout, uint16_t min_ce_length, uint16_t max_ce_length, uint16_t *handle, int to)` +@c(hci_le_create_conn) +public func hci_le_create_conn( + _ dd: Int32, + _ interval: UInt16, + _ window: UInt16, + _ initiatorFilter: UInt8, + _ peerAddressType: UInt8, + _ peerAddress: bdaddr_t, + _ ownAddressType: UInt8, + _ minInterval: UInt16, + _ maxInterval: UInt16, + _ latency: UInt16, + _ supervisionTimeout: UInt16, + _ minCELength: UInt16, + _ maxCELength: UInt16, + _ handle: UnsafeMutablePointer?, + _ timeout: Int32 +) -> Int32 { + var command = le_create_connection_cp() + command.interval = interval + command.window = window + command.initiator_filter = initiatorFilter + command.peer_bdaddr_type = peerAddressType + command.peer_bdaddr = peerAddress + command.own_bdaddr_type = ownAddressType + command.min_interval = minInterval + command.max_interval = maxInterval + command.latency = latency + command.supervision_timeout = supervisionTimeout + command.min_ce_length = minCELength + command.max_ce_length = maxCELength + + guard let response = hciRequest( + dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_CREATE_CONN), event: Int32(EVT_LE_CONN_COMPLETE), + command: command, response: evt_le_connection_complete.self, timeout: timeout + ) else { return -1 } + handle?.pointee = response.handle + return 0 +} + +/// `int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval, uint16_t max_interval, uint16_t latency, uint16_t supervision_timeout, int to)` +@c(hci_le_conn_update) +public func hci_le_conn_update( + _ dd: Int32, + _ handle: UInt16, + _ minInterval: UInt16, + _ maxInterval: UInt16, + _ latency: UInt16, + _ supervisionTimeout: UInt16, + _ timeout: Int32 +) -> Int32 { + var command = le_connection_update_cp() + command.handle = handle + command.min_interval = minInterval + command.max_interval = maxInterval + command.latency = latency + command.supervision_timeout = supervisionTimeout + command.min_ce_length = 0x0001 + command.max_ce_length = 0x0001 + + guard hciRequest( + dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_CONN_UPDATE), event: Int32(EVT_LE_CONN_UPDATE_COMPLETE), + command: command, response: evt_le_connection_update_complete.self, timeout: timeout + ) != nil else { return -1 } + return 0 +} + +/// `int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to)` +@c(hci_le_read_remote_features) +public func hci_le_read_remote_features(_ dd: Int32, _ handle: UInt16, _ features: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + var command = le_read_remote_used_features_cp() + command.handle = handle + guard var response = hciRequest( + dd, ogf: Int32(OGF_LE_CTL), ocf: Int32(OCF_LE_READ_REMOTE_USED_FEATURES), event: Int32(EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE), + command: command, response: evt_le_read_remote_used_features_complete.self, timeout: timeout + ) else { return -1 } + if let features { + withUnsafeMutableBytes(of: &response.features) { features.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 8) } + } + return 0 +} diff --git a/Sources/BluetoothLinuxABI/HCICommandsRemote.swift b/Sources/BluetoothLinuxABI/HCICommandsRemote.swift new file mode 100644 index 0000000..1d04596 --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCICommandsRemote.swift @@ -0,0 +1,137 @@ +// +// HCICommandsRemote.swift +// BluetoothLinux +// +// Wrappers that query a *remote* device over an existing link: name, +// version, features, and clock offset. These wait for the matching +// completion event rather than an immediate command-complete. +// + +import CBluetoothLinuxABI +import Glibc + +extension evt_remote_name_req_complete: HCIStatusResponse {} +extension evt_read_remote_version_complete: HCIStatusResponse {} +extension evt_read_remote_features_complete: HCIStatusResponse {} +extension evt_read_remote_ext_features_complete: HCIStatusResponse {} +extension evt_read_clock_offset_complete: HCIStatusResponse {} + +/// `int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to)` +@c(hci_read_remote_name_with_clock_offset) +public func hci_read_remote_name_with_clock_offset( + _ dd: Int32, + _ bdaddr: UnsafePointer?, + _ pscanRepMode: UInt8, + _ clockOffset: UInt16, + _ len: Int32, + _ name: UnsafeMutablePointer?, + _ timeout: Int32 +) -> Int32 { + guard let bdaddr, let name, len > 0 else { errno = EINVAL; return -1 } + var command = remote_name_req_cp() + command.bdaddr = bdaddr.pointee + command.pscan_rep_mode = pscanRepMode + command.clock_offset = clockOffset + + guard let response = hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_REMOTE_NAME_REQ), event: Int32(EVT_REMOTE_NAME_REQ_COMPLETE), + command: command, response: evt_remote_name_req_complete.self, timeout: timeout + ) else { return -1 } + + var responseName = response.name + withUnsafeMutableBytes(of: &responseName) { buffer in + buffer[247] = 0 + let source = buffer.bindMemory(to: CChar.self) + _ = strncpy(name, source.baseAddress!, Int(len)) + } + return 0 +} + +/// `int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to)` +@c(hci_read_remote_name) +public func hci_read_remote_name( + _ dd: Int32, + _ bdaddr: UnsafePointer?, + _ len: Int32, + _ name: UnsafeMutablePointer?, + _ timeout: Int32 +) -> Int32 { + hci_read_remote_name_with_clock_offset(dd, bdaddr, 0x02, 0x0000, len, name, timeout) +} + +/// `int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to)` +@c(hci_read_remote_name_cancel) +public func hci_read_remote_name_cancel(_ dd: Int32, _ bdaddr: UnsafePointer?, _ timeout: Int32) -> Int32 { + guard let bdaddr else { errno = EINVAL; return -1 } + var command = remote_name_req_cancel_cp() + command.bdaddr = bdaddr.pointee + return hciCommand(dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_REMOTE_NAME_REQ_CANCEL), command: command, timeout: timeout) +} + +/// `int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to)` +@c(hci_read_remote_version) +public func hci_read_remote_version(_ dd: Int32, _ handle: UInt16, _ version: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let version else { errno = EINVAL; return -1 } + var command = read_remote_version_cp() + command.handle = handle + guard let response = hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_READ_REMOTE_VERSION), event: Int32(EVT_READ_REMOTE_VERSION_COMPLETE), + command: command, response: evt_read_remote_version_complete.self, timeout: timeout + ) else { return -1 } + version.pointee.manufacturer = response.manufacturer + version.pointee.lmp_ver = response.lmp_ver + version.pointee.lmp_subver = response.lmp_subver + return 0 +} + +/// `int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to)` +@c(hci_read_remote_features) +public func hci_read_remote_features(_ dd: Int32, _ handle: UInt16, _ features: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + var command = read_remote_features_cp() + command.handle = handle + guard var response = hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_READ_REMOTE_FEATURES), event: Int32(EVT_READ_REMOTE_FEATURES_COMPLETE), + command: command, response: evt_read_remote_features_complete.self, timeout: timeout + ) else { return -1 } + if let features { + withUnsafeMutableBytes(of: &response.features) { features.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 8) } + } + return 0 +} + +/// `int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to)` +@c(hci_read_remote_ext_features) +public func hci_read_remote_ext_features( + _ dd: Int32, + _ handle: UInt16, + _ page: UInt8, + _ maxPage: UnsafeMutablePointer?, + _ features: UnsafeMutablePointer?, + _ timeout: Int32 +) -> Int32 { + var command = read_remote_ext_features_cp() + command.handle = handle + command.page_num = page + guard var response = hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_READ_REMOTE_EXT_FEATURES), event: Int32(EVT_READ_REMOTE_EXT_FEATURES_COMPLETE), + command: command, response: evt_read_remote_ext_features_complete.self, timeout: timeout + ) else { return -1 } + maxPage?.pointee = response.max_page_num + if let features { + withUnsafeMutableBytes(of: &response.features) { features.update(from: $0.bindMemory(to: UInt8.self).baseAddress!, count: 8) } + } + return 0 +} + +/// `int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to)` +@c(hci_read_clock_offset) +public func hci_read_clock_offset(_ dd: Int32, _ handle: UInt16, _ clockOffset: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + var command = read_clock_offset_cp() + command.handle = handle + guard let response = hciRequest( + dd, ogf: Int32(OGF_LINK_CTL), ocf: Int32(OCF_READ_CLOCK_OFFSET), event: Int32(EVT_READ_CLOCK_OFFSET_COMPLETE), + command: command, response: evt_read_clock_offset_complete.self, timeout: timeout + ) else { return -1 } + clockOffset?.pointee = response.clock_offset + return 0 +} diff --git a/Sources/BluetoothLinuxABI/HCIDevice.swift b/Sources/BluetoothLinuxABI/HCIDevice.swift new file mode 100644 index 0000000..433eee5 --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCIDevice.swift @@ -0,0 +1,432 @@ +// +// HCIDevice.swift +// BluetoothLinux +// +// Swift implementations of the HCI device management family: opening +// and closing a raw HCI socket, looking up device info/address/id, +// enumerating devices, routing, and sending raw commands/requests. +// +// These talk to the kernel directly via AF_BLUETOOTH/BTPROTO_HCI raw +// sockets and the HCIGETDEVINFO/HCIGETDEVLIST ioctls, mirroring +// lib/hci.c exactly, rather than routing through BluetoothLinux's own +// (async, higher-level) HostController/Socket infrastructure — the +// ABI surface has to be synchronous and match the reference's wire +// layout precisely, which a bridge into the async engine would not +// make any simpler. +// +// hci_send_cmd/hci_send_req have not been exercised against a live +// or virtual HCI device (no differential conformance harness for +// this family yet); they're a direct, careful translation of +// lib/hci.c's hci_send_cmd/hci_send_req. +// + +import CBluetoothLinuxABI +import Glibc + +// MARK: - Opcode packing (cmd_opcode_pack is a function-like macro, not imported) + +private func hciOpcode(ogf: UInt16, ocf: UInt16) -> UInt16 { + (ocf & 0x03ff) | (ogf << 10) +} + +// MARK: - ioctl request codes (_IOR('H', nr, int) macros; not imported — +// ClangImporter can't evaluate function-like macros that use sizeof). +// Values confirmed against the vendored header with a small C program. + +private let hciGetDeviceList: UInt = 0x800448d2 +private let hciGetDeviceInfo: UInt = 0x800448d3 + +// MARK: - atoi-equivalent (BlueZ's hci_devid uses atoi(str + 3)) + +private func atoiPrefix(_ string: Substring) -> Int32 { + var characters = string + while let first = characters.first, first == " " || first == "\t" || first == "\n" { + characters.removeFirst() + } + var sign: Int32 = 1 + if let first = characters.first, first == "+" || first == "-" { + if first == "-" { sign = -1 } + characters.removeFirst() + } + var value: Int32 = 0 + while let first = characters.first, let digit = first.wholeNumberValue, first.isASCII, digit <= 9 { + value = value &* 10 &+ Int32(digit) + characters.removeFirst() + } + return sign &* value +} + +// MARK: - Open / close + +/// `int hci_open_dev(int dev_id)` +@c(hci_open_dev) +public func hci_open_dev(_ devID: Int32) -> Int32 { + guard devID >= 0 else { + errno = ENODEV + return -1 + } + let dd = socket(Int32(AF_BLUETOOTH), Int32(SOCK_RAW.rawValue) | Int32(SOCK_CLOEXEC.rawValue), Int32(BTPROTO_HCI)) + guard dd >= 0 else { return dd } + + var address = sockaddr_hci(hci_family: sa_family_t(AF_BLUETOOTH), hci_dev: UInt16(devID), hci_channel: 0) + let result = withUnsafeMutablePointer(to: &address) { pointer -> Int32 in + pointer.withMemoryRebound(to: sockaddr.self, capacity: 1) { sockaddrPointer in + bind(dd, sockaddrPointer, socklen_t(MemoryLayout.size)) + } + } + guard result >= 0 else { + let savedError = errno + close(dd) + errno = savedError + return -1 + } + return dd +} + +/// `int hci_close_dev(int dd)` +@c(hci_close_dev) +public func hci_close_dev(_ dd: Int32) -> Int32 { + close(dd) +} + +// MARK: - Device info / address / id + +/// `int hci_devinfo(int dev_id, struct hci_dev_info *di)` +@c(hci_devinfo) +public func hci_devinfo(_ devID: Int32, _ info: UnsafeMutablePointer?) -> Int32 { + guard let info else { return -1 } + let dd = socket(Int32(AF_BLUETOOTH), Int32(SOCK_RAW.rawValue) | Int32(SOCK_CLOEXEC.rawValue), Int32(BTPROTO_HCI)) + guard dd >= 0 else { return dd } + + memset(info, 0, MemoryLayout.size) + info.pointee.dev_id = UInt16(devID) + let result = ioctl(dd, numericCast(hciGetDeviceInfo), info) + + let savedError = errno + close(dd) + errno = savedError + return result +} + +/// `int hci_devba(int dev_id, bdaddr_t *bdaddr)` +@c(hci_devba) +public func hci_devba(_ devID: Int32, _ bdaddr: UnsafeMutablePointer?) -> Int32 { + var info = hci_dev_info() + guard hci_devinfo(devID, &info) >= 0 else { return -1 } + guard hci_test_bit(Int32(HCI_UP), &info.flags) != 0 else { + errno = ENETDOWN + return -1 + } + bdaddr?.pointee = info.bdaddr + return 0 +} + +/// `int hci_devid(const char *str)` +@c(hci_devid) +public func hci_devid(_ str: UnsafePointer?) -> Int32 { + guard let str else { return -1 } + let string = String(cString: str) + var id: Int32 = -1 + if string.hasPrefix("hci"), string.utf8.count >= 4 { + id = atoiPrefix(string.dropFirst(3)) + var address = bdaddr_t(b: (0, 0, 0, 0, 0, 0)) + guard hci_devba(id, &address) >= 0 else { return -1 } + } else { + errno = ENODEV + var address = bdaddr_t(b: (0, 0, 0, 0, 0, 0)) + str2ba(str, &address) + id = withUnsafeMutablePointer(to: &address) { + hci_for_each_dev(Int32(HCI_UP), sameBDAddress, Int(bitPattern: $0)) + } + } + return id +} + +// MARK: - Enumeration / routing + +private func sameBDAddress(_ dd: Int32, _ devID: Int32, _ arg: Int) -> Int32 { + var info = hci_dev_info() + info.dev_id = UInt16(devID) + guard ioctl(dd, numericCast(hciGetDeviceInfo), &info) == 0 else { return 0 } + guard let target = UnsafeRawPointer(bitPattern: arg)?.assumingMemoryBound(to: bdaddr_t.self) else { return 0 } + return bacmp(target, &info.bdaddr) == 0 ? 1 : 0 +} + +private func otherBDAddress(_ dd: Int32, _ devID: Int32, _ arg: Int) -> Int32 { + var info = hci_dev_info() + info.dev_id = UInt16(devID) + guard ioctl(dd, numericCast(hciGetDeviceInfo), &info) == 0 else { return 0 } + guard hci_test_bit(Int32(HCI_RAW), &info.flags) == 0 else { return 0 } + guard let target = UnsafeRawPointer(bitPattern: arg)?.assumingMemoryBound(to: bdaddr_t.self) else { return 0 } + return Int32(bacmp(target, &info.bdaddr)) +} + +/// `int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg)` +@c(hci_for_each_dev) +public func hci_for_each_dev( + _ flag: Int32, + _ callback: (@convention(c) (Int32, Int32, Int) -> Int32)?, + _ arg: Int +) -> Int32 { + let sk = socket(Int32(AF_BLUETOOTH), Int32(SOCK_RAW.rawValue) | Int32(SOCK_CLOEXEC.rawValue), Int32(BTPROTO_HCI)) + guard sk >= 0 else { return -1 } + + let maxDevices = Int(HCI_MAX_DEV) + let headerSize = MemoryLayout.size + let entrySize = MemoryLayout.size + let byteCount = headerSize + maxDevices * entrySize + let buffer = UnsafeMutableRawPointer.allocate( + byteCount: byteCount, + alignment: MemoryLayout.alignment + ) + defer { buffer.deallocate() } + buffer.initializeMemory(as: UInt8.self, repeating: 0, count: byteCount) + + let listRequest = buffer.assumingMemoryBound(to: hci_dev_list_req.self) + listRequest.pointee.dev_num = UInt16(maxDevices) + + var deviceID: Int32 = -1 + var savedError: Int32 = 0 + + if ioctl(sk, numericCast(hciGetDeviceList), buffer) < 0 { + savedError = errno + } else { + let deviceRequests = buffer.advanced(by: headerSize).assumingMemoryBound(to: hci_dev_req.self) + let count = Int(listRequest.pointee.dev_num) + for i in 0 ..< count { + var options = deviceRequests[i].dev_opt + guard hci_test_bit(flag, &options) != 0 else { continue } + let id = Int32(deviceRequests[i].dev_id) + if callback == nil || callback!(sk, id, arg) != 0 { + deviceID = id + break + } + } + if deviceID < 0 { + savedError = Int32(ENODEV) + } + } + + close(sk) + errno = savedError + return deviceID +} + +/// `int hci_get_route(bdaddr_t *bdaddr)` +@c(hci_get_route) +public func hci_get_route(_ bdaddr: UnsafeMutablePointer?) -> Int32 { + func route(with target: UnsafeMutablePointer) -> Int32 { + let arg = Int(bitPattern: target) + var deviceID = hci_for_each_dev(Int32(HCI_UP), otherBDAddress, arg) + if deviceID < 0 { + deviceID = hci_for_each_dev(Int32(HCI_UP), sameBDAddress, arg) + } + return deviceID + } + if let bdaddr { + return route(with: bdaddr) + } + var any = bdaddr_t(b: (0, 0, 0, 0, 0, 0)) + return withUnsafeMutablePointer(to: &any) { route(with: $0) } +} + +// MARK: - Sending commands / requests + +/// `int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)` +@c(hci_send_cmd) +public func hci_send_cmd(_ dd: Int32, _ ogf: UInt16, _ ocf: UInt16, _ plen: UInt8, _ param: UnsafeMutableRawPointer?) -> Int32 { + var packetType = UInt8(HCI_COMMAND_PKT) + var header = hci_command_hdr(opcode: hciOpcode(ogf: ogf, ocf: ocf), plen: plen) + + return withUnsafeMutableBytes(of: &packetType) { typeBuffer -> Int32 in + withUnsafeMutableBytes(of: &header) { headerBuffer -> Int32 in + var vectors = [ + iovec(iov_base: typeBuffer.baseAddress, iov_len: 1), + iovec(iov_base: headerBuffer.baseAddress, iov_len: headerBuffer.count) + ] + if plen > 0 { + vectors.append(iovec(iov_base: param, iov_len: Int(plen))) + } + while true { + let written = writev(dd, &vectors, Int32(vectors.count)) + if written < 0 { + if errno == EAGAIN || errno == EINTR { continue } + return -1 + } + return 0 + } + } + } +} + +/// `int hci_send_req(int dd, struct hci_request *req, int timeout)` +@c(hci_send_req) +public func hci_send_req(_ dd: Int32, _ request: UnsafeMutablePointer?, _ timeout: Int32) -> Int32 { + guard let request else { + errno = EINVAL + return -1 + } + let opcode = hciOpcode(ogf: request.pointee.ogf, ocf: request.pointee.ocf) + + var oldFilter = hci_filter() + var oldFilterLength = socklen_t(MemoryLayout.size) + guard getsockopt(dd, Int32(SOL_HCI), Int32(HCI_FILTER), &oldFilter, &oldFilterLength) >= 0 else { + return -1 + } + + var newFilter = hci_filter() + hci_filter_clear(&newFilter) + hci_filter_set_ptype(Int32(HCI_EVENT_PKT), &newFilter) + hci_filter_set_event(Int32(EVT_CMD_STATUS), &newFilter) + hci_filter_set_event(Int32(EVT_CMD_COMPLETE), &newFilter) + hci_filter_set_event(Int32(EVT_LE_META_EVENT), &newFilter) + hci_filter_set_event(request.pointee.event, &newFilter) + hci_filter_set_opcode(Int32(opcode), &newFilter) + guard setsockopt(dd, Int32(SOL_HCI), Int32(HCI_FILTER), &newFilter, socklen_t(MemoryLayout.size)) >= 0 else { + return -1 + } + + func restoreFilterAndFail() -> Int32 { + let savedError = errno + var restore = oldFilter + var finalError = savedError + if setsockopt(dd, Int32(SOL_HCI), Int32(HCI_FILTER), &restore, socklen_t(MemoryLayout.size)) < 0 { + finalError = errno + } + errno = finalError + return -1 + } + + func restoreFilterAndSucceed() -> Int32 { + var restore = oldFilter + guard setsockopt(dd, Int32(SOL_HCI), Int32(HCI_FILTER), &restore, socklen_t(MemoryLayout.size)) >= 0 else { + return -1 + } + return 0 + } + + guard hci_send_cmd( + dd, + request.pointee.ogf, + request.pointee.ocf, + UInt8(truncatingIfNeeded: request.pointee.clen), + request.pointee.cparam + ) >= 0 else { + return restoreFilterAndFail() + } + + var buffer = [UInt8](repeating: 0, count: Int(HCI_MAX_EVENT_SIZE)) + var remainingTimeout = timeout + var attempts = 10 + + while attempts > 0 { + attempts -= 1 + + if remainingTimeout != 0 { + var pfd = pollfd(fd: dd, events: Int16(POLLIN), revents: 0) + var pollResult: Int32 + repeat { + pollResult = poll(&pfd, 1, remainingTimeout) + } while pollResult < 0 && (errno == EAGAIN || errno == EINTR) + + if pollResult < 0 { + return restoreFilterAndFail() + } + if pollResult == 0 { + errno = ETIMEDOUT + return restoreFilterAndFail() + } + + remainingTimeout -= 10 + if remainingTimeout < 0 { remainingTimeout = 0 } + } + + var bytesRead: Int + repeat { + bytesRead = buffer.withUnsafeMutableBytes { read(dd, $0.baseAddress, $0.count) } + } while bytesRead < 0 && (errno == EAGAIN || errno == EINTR) + + if bytesRead < 0 { + return restoreFilterAndFail() + } + + let dataOffset = 1 + Int(HCI_EVENT_HDR_SIZE) + guard bytesRead >= dataOffset else { continue } + + let eventCode = Int32(buffer[1]) + let length = bytesRead - dataOffset + + switch eventCode { + case Int32(EVT_CMD_STATUS): + guard length >= MemoryLayout.size else { continue } + let status: evt_cmd_status = buffer.withUnsafeBytes { $0.loadUnaligned(fromByteOffset: dataOffset, as: evt_cmd_status.self) } + guard status.opcode == opcode else { continue } + if request.pointee.event != Int32(EVT_CMD_STATUS) { + if status.status != 0 { + errno = EIO + return restoreFilterAndFail() + } + } else { + let resultLength = min(length, Int(request.pointee.rlen)) + request.pointee.rlen = Int32(resultLength) + if let rparam = request.pointee.rparam { + _ = buffer.withUnsafeBytes { memcpy(rparam, $0.baseAddress!.advanced(by: dataOffset), resultLength) } + } + return restoreFilterAndSucceed() + } + + case Int32(EVT_CMD_COMPLETE): + guard length >= MemoryLayout.size else { continue } + let complete: evt_cmd_complete = buffer.withUnsafeBytes { $0.loadUnaligned(fromByteOffset: dataOffset, as: evt_cmd_complete.self) } + guard complete.opcode == opcode else { continue } + let paramOffset = dataOffset + Int(EVT_CMD_COMPLETE_SIZE) + let paramLength = length - Int(EVT_CMD_COMPLETE_SIZE) + let resultLength = min(paramLength, Int(request.pointee.rlen)) + request.pointee.rlen = Int32(resultLength) + if let rparam = request.pointee.rparam, resultLength > 0 { + _ = buffer.withUnsafeBytes { memcpy(rparam, $0.baseAddress!.advanced(by: paramOffset), resultLength) } + } + return restoreFilterAndSucceed() + + case Int32(EVT_REMOTE_NAME_REQ_COMPLETE): + guard eventCode == request.pointee.event else { break } + guard length >= MemoryLayout.size else { continue } + let addressOffset = dataOffset + 1 + var remoteAddress: bdaddr_t = buffer.withUnsafeBytes { $0.loadUnaligned(fromByteOffset: addressOffset, as: bdaddr_t.self) } + if let cparam = request.pointee.cparam { + var commandAddress = cparam.loadUnaligned(as: bdaddr_t.self) + guard bacmp(&remoteAddress, &commandAddress) == 0 else { continue } + } + let resultLength = min(length, Int(request.pointee.rlen)) + request.pointee.rlen = Int32(resultLength) + if let rparam = request.pointee.rparam { + _ = buffer.withUnsafeBytes { memcpy(rparam, $0.baseAddress!.advanced(by: dataOffset), resultLength) } + } + return restoreFilterAndSucceed() + + case Int32(EVT_LE_META_EVENT): + guard length >= 1 else { continue } + let subevent = Int32(buffer[dataOffset]) + guard subevent == request.pointee.event else { continue } + let metaLength = length - 1 + let resultLength = min(metaLength, Int(request.pointee.rlen)) + request.pointee.rlen = Int32(resultLength) + if let rparam = request.pointee.rparam { + _ = buffer.withUnsafeBytes { memcpy(rparam, $0.baseAddress!.advanced(by: dataOffset + 1), resultLength) } + } + return restoreFilterAndSucceed() + + default: + guard eventCode == request.pointee.event else { break } + let resultLength = min(length, Int(request.pointee.rlen)) + request.pointee.rlen = Int32(resultLength) + if let rparam = request.pointee.rparam { + _ = buffer.withUnsafeBytes { memcpy(rparam, $0.baseAddress!.advanced(by: dataOffset), resultLength) } + } + return restoreFilterAndSucceed() + } + } + + errno = ETIMEDOUT + return restoreFilterAndFail() +} diff --git a/Sources/BluetoothLinuxABI/HCIInquiry.swift b/Sources/BluetoothLinuxABI/HCIInquiry.swift new file mode 100644 index 0000000..cc4d452 --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCIInquiry.swift @@ -0,0 +1,101 @@ +// +// HCIInquiry.swift +// BluetoothLinux +// +// hci_inquiry doesn't go through hci_send_req at all — it's a single +// ioctl(HCIINQUIRY) call whose input (the inquiry parameters) and +// output (the resulting inquiry_info records) share one kernel +// buffer, the same "fixed header immediately followed by a run of +// fixed-size records" shape as HCIGETDEVLIST in HCIDevice.swift. +// + +import CBluetoothLinuxABI +import Glibc + +// _IOR('H', 240, int) — see HCIDevice.swift's hciGetDeviceInfo/hciGetDeviceList +// for why this can't be imported directly. +private let hciInquiryRequest: UInt = 0x800448f0 + +/// `int hci_inquiry(int dev_id, int len, int nrsp, const uint8_t *lap, inquiry_info **ii, long flags)` +@c(hci_inquiry) +public func hci_inquiry( + _ deviceID: Int32, + _ len: Int32, + _ numberOfResponses: Int32, + _ lap: UnsafePointer?, + _ inquiryInfo: UnsafeMutablePointer?>?, + _ flags: Int +) -> Int32 { + guard let inquiryInfo else { errno = EINVAL; return -1 } + + var requestedResponses = numberOfResponses + let numberOfResponsesField: UInt8 = numberOfResponses > 0 ? UInt8(numberOfResponses) : 0 + if numberOfResponses <= 0 { + requestedResponses = 255 + } + + var deviceID = deviceID + if deviceID < 0 { + deviceID = hci_get_route(nil) + guard deviceID >= 0 else { + errno = ENODEV + return -1 + } + } + + let dd = socket(Int32(AF_BLUETOOTH), Int32(SOCK_RAW.rawValue) | Int32(SOCK_CLOEXEC.rawValue), Int32(BTPROTO_HCI)) + guard dd >= 0 else { return dd } + + let headerSize = MemoryLayout.size + let entrySize = MemoryLayout.size + let byteCount = headerSize + entrySize * Int(requestedResponses) + let buffer = UnsafeMutableRawPointer.allocate( + byteCount: byteCount, + alignment: MemoryLayout.alignment + ) + defer { buffer.deallocate() } + buffer.initializeMemory(as: UInt8.self, repeating: 0, count: byteCount) + + let request = buffer.assumingMemoryBound(to: hci_inquiry_req.self) + request.pointee.dev_id = UInt16(deviceID) + request.pointee.num_rsp = numberOfResponsesField + request.pointee.length = UInt8(truncatingIfNeeded: len) + request.pointee.flags = UInt16(truncatingIfNeeded: flags) + if let lap { + request.pointee.lap.0 = lap[0] + request.pointee.lap.1 = lap[1] + request.pointee.lap.2 = lap[2] + } else { + request.pointee.lap.0 = 0x33 + request.pointee.lap.1 = 0x8b + request.pointee.lap.2 = 0x9e + } + + let ioctlResult = ioctl(dd, numericCast(hciInquiryRequest), buffer) + guard ioctlResult >= 0 else { + let savedError = errno + close(dd) + errno = savedError + return -1 + } + + let responseCount = Int(request.pointee.num_rsp) + let resultByteCount = entrySize * responseCount + + var result: Int32 + if inquiryInfo.pointee == nil { + inquiryInfo.pointee = UnsafeMutablePointer.allocate(capacity: responseCount) + } + if let destination = inquiryInfo.pointee { + let source = buffer.advanced(by: headerSize) + UnsafeMutableRawPointer(destination).copyMemory(from: source, byteCount: resultByteCount) + result = Int32(responseCount) + } else { + result = -1 + } + + let savedError = errno + close(dd) + errno = savedError + return result +} diff --git a/Sources/BluetoothLinuxABI/HCIRequest.swift b/Sources/BluetoothLinuxABI/HCIRequest.swift new file mode 100644 index 0000000..8af46da --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCIRequest.swift @@ -0,0 +1,153 @@ +// +// HCIRequest.swift +// BluetoothLinux +// +// Shared plumbing for the HCI command wrapper family: every wrapper in +// lib/hci.c follows one of a small number of shapes — build a command +// struct, call hci_send_req, check a status byte, optionally copy +// fields out of the response. These helpers capture those shapes once +// so each wrapper function is a short, direct transcription of its +// reference instead of repeating the send/check boilerplate. +// +// None of this has been exercised against a real or virtual HCI +// device; see HCIDevice.swift's note on hci_send_cmd/hci_send_req. +// + +import CBluetoothLinuxABI +import Glibc + +/// A response struct whose first field is the standard one-byte HCI +/// command status (0 = success). Nearly every `*_rp`/`evt_*` struct in +/// hci.h has this shape; conforming a type only asserts that its first +/// byte is the status, which the C struct layout already guarantees. +internal protocol HCIStatusResponse { + init() + var status: UInt8 { get } +} + +/// Placeholder for commands that take no parameters. +internal struct HCIEmptyCommand {} + +/// Sends `command` and returns `hci_send_req`'s raw result, with no +/// response payload (`rparam`/`rlen` left at zero) — mirrors the +/// wrappers that call `hci_send_req` directly and return its result +/// (or check nothing at all). +@discardableResult +internal func hciCommand( + _ dd: Int32, + ogf: Int32, + ocf: Int32, + event: Int32 = 0, + command: Command, + timeout: Int32 +) -> Int32 { + var command = command + var request = hci_request() + request.ogf = UInt16(ogf) + request.ocf = UInt16(ocf) + request.event = event + request.clen = Int32(MemoryLayout.size) + return withUnsafeMutableBytes(of: &command) { commandBuffer -> Int32 in + request.cparam = MemoryLayout.size > 0 ? commandBuffer.baseAddress : nil + return withUnsafeMutablePointer(to: &request) { hci_send_req(dd, $0, timeout) } + } +} + +@discardableResult +internal func hciCommand(_ dd: Int32, ogf: Int32, ocf: Int32, event: Int32 = 0, timeout: Int32) -> Int32 { + hciCommand(dd, ogf: ogf, ocf: ocf, event: event, command: HCIEmptyCommand(), timeout: timeout) +} + +/// Sends `commandBytes` verbatim as the command parameter — for the +/// handful of wrappers whose parameter is a hand-assembled, variable +/// length byte buffer rather than a fixed C struct. +@discardableResult +internal func hciCommand(_ dd: Int32, ogf: Int32, ocf: Int32, event: Int32 = 0, commandBytes: [UInt8], timeout: Int32) -> Int32 { + var bytes = commandBytes + var request = hci_request() + request.ogf = UInt16(ogf) + request.ocf = UInt16(ocf) + request.event = event + request.clen = Int32(bytes.count) + return bytes.withUnsafeMutableBytes { buffer -> Int32 in + request.cparam = buffer.baseAddress + return withUnsafeMutablePointer(to: &request) { hci_send_req(dd, $0, timeout) } + } +} + +/// Sends `command` and reads back a single status byte — the pattern +/// used by every `hci_le_*` wrapper that doesn't decode a response +/// struct (BlueZ declares a bare `uint8_t status;` local for these +/// rather than a named `_rp` type). +internal func hciStatus(_ dd: Int32, ogf: Int32, ocf: Int32, command: Command, timeout: Int32) -> Int32 { + var command = command + var status: UInt8 = 0 + var request = hci_request() + request.ogf = UInt16(ogf) + request.ocf = UInt16(ocf) + request.clen = Int32(MemoryLayout.size) + request.rlen = 1 + let result: Int32 = withUnsafeMutableBytes(of: &command) { commandBuffer in + withUnsafeMutableBytes(of: &status) { statusBuffer -> Int32 in + request.cparam = MemoryLayout.size > 0 ? commandBuffer.baseAddress : nil + request.rparam = statusBuffer.baseAddress + return withUnsafeMutablePointer(to: &request) { hci_send_req(dd, $0, timeout) } + } + } + guard result >= 0 else { return -1 } + guard status == 0 else { + errno = EIO + return -1 + } + return 0 +} + +internal func hciStatus(_ dd: Int32, ogf: Int32, ocf: Int32, timeout: Int32) -> Int32 { + hciStatus(dd, ogf: ogf, ocf: ocf, command: HCIEmptyCommand(), timeout: timeout) +} + +/// Sends `command`, decodes the response into `Response`, and checks +/// its `status` field. Returns `nil` (with `errno` set) on failure — +/// the shape used by nearly every `hci_read_*`/`hci_write_*` wrapper. +internal func hciRequest( + _ dd: Int32, + ogf: Int32, + ocf: Int32, + event: Int32 = 0, + command: Command, + response: Response.Type, + timeout: Int32 +) -> Response? { + var command = command + var response = Response() + var request = hci_request() + request.ogf = UInt16(ogf) + request.ocf = UInt16(ocf) + request.event = event + request.clen = Int32(MemoryLayout.size) + request.rlen = Int32(MemoryLayout.size) + let result: Int32 = withUnsafeMutableBytes(of: &command) { commandBuffer in + withUnsafeMutableBytes(of: &response) { responseBuffer -> Int32 in + request.cparam = MemoryLayout.size > 0 ? commandBuffer.baseAddress : nil + request.rparam = responseBuffer.baseAddress + return withUnsafeMutablePointer(to: &request) { hci_send_req(dd, $0, timeout) } + } + } + guard result >= 0 else { return nil } + guard response.status == 0 else { + errno = EIO + return nil + } + return response +} + +internal func hciRequest( + _ dd: Int32, + ogf: Int32, + ocf: Int32, + event: Int32 = 0, + response: Response.Type, + timeout: Int32 +) -> Response? { + hciRequest(dd, ogf: ogf, ocf: ocf, event: event, command: HCIEmptyCommand(), response: response, timeout: timeout) +} diff --git a/Sources/BluetoothLinuxABI/HCIStrings.swift b/Sources/BluetoothLinuxABI/HCIStrings.swift new file mode 100644 index 0000000..4a492bb --- /dev/null +++ b/Sources/BluetoothLinuxABI/HCIStrings.swift @@ -0,0 +1,371 @@ +// +// HCIStrings.swift +// BluetoothLinux +// +// Swift implementations of the `hci_*tostr`/`hci_strto*` (and +// `lmp_*`/`pal_*`) string converter family, bound to the declarations +// in the vendored `hci_lib.h`. The lookup tables themselves are +// generated (see `gen/HCITables.swift`) from BlueZ's own `hci_map` +// arrays, to avoid transcription errors across the largest of them +// (232 HCI command names). +// +// One deliberate deviation from the reference: `hci_lmtostr` mallocs +// a fixed 50-byte buffer for a "PERIPHERAL " prefix concatenated with +// `hci_bit2str`'s (up to 120-byte) result — with enough link-mode +// bits set simultaneously, the reference overflows its own buffer. +// This is a real bug, not a documented contract; this implementation +// allocates enough space for the actual output instead of reproducing +// the overflow. +// + +import CBluetoothLinuxABI + +// MARK: - Table lookup helpers (BlueZ's hci_bit2str/hci_str2bit/hci_uint2str/hci_str2uint) + +/// `static char *hci_bit2str(const hci_map *m, unsigned int val)` +/// +/// Every set bit's name, space-separated, in table order. Returns an +/// empty (not NULL) string when no bits match, matching the reference. +private func bit2str(_ table: [(name: String, value: UInt32)], _ val: UInt32) -> UnsafeMutablePointer { + var result = "" + for entry in table where entry.value & val != 0 { + result += entry.name + result += " " + } + return strdup(result) +} + +/// `static int hci_str2bit(const hci_map *map, char *str, unsigned int *val)` +/// +/// Comma-separated, case-insensitive; ORs every matching entry's bits +/// into `*val`. Returns whether anything matched. +private func str2bit(_ table: [(name: String, value: UInt32)], _ str: UnsafePointer?) -> (matched: Bool, value: UInt32) { + guard let str else { return (false, 0) } + var value: UInt32 = 0 + var matched = false + for token in String(cString: str).split(separator: ",") { + let token = trimmed(token) + for entry in table where entry.name.lowercased() == token.lowercased() { + value |= entry.value + matched = true + } + } + return (matched, value) +} + +/// `static char *hci_uint2str(const hci_map *m, unsigned int val)` +/// +/// Exact match; empty (not NULL) string when nothing matches. +private func uint2str(_ table: [(name: String, value: UInt32)], _ val: UInt32) -> UnsafeMutablePointer { + for entry in table where entry.value == val { + return strdup(entry.name) + } + return strdup("") +} + +/// `static int hci_str2uint(const hci_map *map, char *str, unsigned int *val)` +private func str2uint(_ table: [(name: String, value: UInt32)], _ str: UnsafePointer?) -> (matched: Bool, value: UInt32) { + guard let str else { return (false, 0) } + let token = String(cString: str) + for candidate in token.split(separator: ",") { + let candidate = trimmed(candidate) + for entry in table where entry.name.lowercased() == candidate.lowercased() { + return (true, entry.value) + } + } + return (false, 0) +} + +/// Strips leading/trailing spaces and tabs — a Foundation-free stand-in +/// for `CharacterSet.whitespaces`-based trimming. +private func trimmed(_ substring: Substring) -> Substring { + var result = substring + while let first = result.first, first == " " || first == "\t" { result.removeFirst() } + while let last = result.last, last == " " || last == "\t" { result.removeLast() } + return result +} + +// MARK: - Static-lifetime strings (const char *, never freed) + +private nonisolated(unsafe) let staticStrings: [String: UnsafePointer] = [ + "Virtual": staticCString("Virtual"), + "USB": staticCString("USB"), + "PCCARD": staticCString("PCCARD"), + "UART": staticCString("UART"), + "RS232": staticCString("RS232"), + "PCI": staticCString("PCI"), + "SDIO": staticCString("SDIO"), + "SPI": staticCString("SPI"), + "I2C": staticCString("I2C"), + "SMD": staticCString("SMD"), + "VIRTIO": staticCString("VIRTIO"), + "IPC": staticCString("IPC"), + "Unknown": staticCString("Unknown"), + "Primary": staticCString("Primary"), + "AMP": staticCString("AMP") +] + +private func staticCString(_ string: String) -> UnsafePointer { + UnsafePointer(strdup(string)!) +} + +// MARK: - Bus / controller type + +/// `const char *hci_bustostr(int bus)` +@c(hci_bustostr) +public func hci_bustostr(_ bus: Int32) -> UnsafePointer? { + let name: String + switch bus { + case Int32(HCI_VIRTUAL): name = "Virtual" + case Int32(HCI_USB): name = "USB" + case Int32(HCI_PCCARD): name = "PCCARD" + case Int32(HCI_UART): name = "UART" + case Int32(HCI_RS232): name = "RS232" + case Int32(HCI_PCI): name = "PCI" + case Int32(HCI_SDIO): name = "SDIO" + case Int32(HCI_SPI): name = "SPI" + case Int32(HCI_I2C): name = "I2C" + case Int32(HCI_SMD): name = "SMD" + case Int32(HCI_VIRTIO): name = "VIRTIO" + case Int32(HCI_IPC): name = "IPC" + default: name = "Unknown" + } + return staticStrings[name] +} + +/// `const char *hci_dtypetostr(int type)` +@c(hci_dtypetostr) +public func hci_dtypetostr(_ type: Int32) -> UnsafePointer? { + hci_bustostr(type & 0x0f) +} + +/// `char *hci_typetostr(int type)` +@c(hci_typetostr) +public func hci_typetostr(_ type: Int32) -> UnsafeMutablePointer? { + let name: String + switch type { + case Int32(HCI_PRIMARY): name = "Primary" + case Int32(HCI_AMP): name = "AMP" + default: name = "Unknown" + } + return UnsafeMutablePointer(mutating: staticStrings[name]) +} + +// MARK: - Device flags + +/// `char *hci_dflagstostr(uint32_t flags)` +@c(hci_dflagstostr) +public func hci_dflagstostr(_ flags: UInt32) -> UnsafeMutablePointer? { + var result = "" + if flags & (1 << UInt32(HCI_UP)) == 0 { + result += "DOWN " + } + for entry in hciDeviceFlagsMap where flags & (1 << entry.value) != 0 { + result += entry.name + result += " " + } + return strdup(result) +} + +// MARK: - Packet types + +/// `char *hci_ptypetostr(unsigned int ptype)` +@c(hci_ptypetostr) +public func hci_ptypetostr(_ ptype: UInt32) -> UnsafeMutablePointer? { + bit2str(hciPacketTypeMap, ptype) +} + +/// `int hci_strtoptype(char *str, unsigned int *val)` +@c(hci_strtoptype) +public func hci_strtoptype(_ str: UnsafeMutablePointer?, _ val: UnsafeMutablePointer?) -> Int32 { + let (matched, value) = str2bit(hciPacketTypeMap, str) + val?.pointee = value + return matched ? 1 : 0 +} + +/// `char *hci_scoptypetostr(unsigned int ptype)` +@c(hci_scoptypetostr) +public func hci_scoptypetostr(_ ptype: UInt32) -> UnsafeMutablePointer? { + bit2str(hciSCOPacketTypeMap, ptype) +} + +/// `int hci_strtoscoptype(char *str, unsigned int *val)` +@c(hci_strtoscoptype) +public func hci_strtoscoptype(_ str: UnsafeMutablePointer?, _ val: UnsafeMutablePointer?) -> Int32 { + let (matched, value) = str2bit(hciSCOPacketTypeMap, str) + val?.pointee = value + return matched ? 1 : 0 +} + +// MARK: - Link policy + +/// `char *hci_lptostr(unsigned int lp)` +@c(hci_lptostr) +public func hci_lptostr(_ lp: UInt32) -> UnsafeMutablePointer? { + bit2str(hciLinkPolicyMap, lp) +} + +/// `int hci_strtolp(char *str, unsigned int *val)` +@c(hci_strtolp) +public func hci_strtolp(_ str: UnsafeMutablePointer?, _ val: UnsafeMutablePointer?) -> Int32 { + let (matched, value) = str2bit(hciLinkPolicyMap, str) + val?.pointee = value + return matched ? 1 : 0 +} + +// MARK: - Link mode + +/// `char *hci_lmtostr(unsigned int lm)` +/// +/// See the file-level note: allocates enough space for the real +/// output instead of reproducing the reference's fixed-buffer overflow. +@c(hci_lmtostr) +public func hci_lmtostr(_ lm: UInt32) -> UnsafeMutablePointer? { + var result = "" + if lm & UInt32(HCI_LM_MASTER) == 0 { + result += "PERIPHERAL " + } + for entry in hciLinkModeMap where entry.value & lm != 0 { + result += entry.name + result += " " + } + return strdup(result) +} + +/// `int hci_strtolm(char *str, unsigned int *val)` +@c(hci_strtolm) +public func hci_strtolm(_ str: UnsafeMutablePointer?, _ val: UnsafeMutablePointer?) -> Int32 { + var (matched, value) = str2bit(hciLinkModeMap, str) + // Deprecated name, kept for compatibility. + if let str, String(cString: str).lowercased().contains("master") { + matched = true + value |= UInt32(HCI_LM_MASTER) + } + val?.pointee = value + return matched ? 1 : 0 +} + +// MARK: - Commands + +/// `char *hci_cmdtostr(unsigned int cmd)` +@c(hci_cmdtostr) +public func hci_cmdtostr(_ cmd: UInt32) -> UnsafeMutablePointer? { + uint2str(hciCommandsMap, cmd) +} + +/// `char *hci_commandstostr(const uint8_t *commands, const char *pref, int width)` +/// +/// Every supported-commands bit set in `commands` (a 64-byte bitmap, +/// per the HCI spec), quoted and space-separated, wrapped to `width` +/// columns with `pref` repeated at the start of each line. +/// +/// BlueZ's `lib/bluetooth/hci.c` (as of 5.85, the version these +/// declarations are vendored from) trims the final trailing space +/// unconditionally (`ptr[-1] = '\0'` once anything was written). The +/// installed reference on this system is BlueZ 5.82, whose compiled +/// behavior — confirmed by differential conformance — keeps it. Since +/// conformance means matching the actual deployed library, not a +/// specific source revision, the trailing space is kept here too. +@c(hci_commandstostr) +public func hci_commandstostr( + _ commands: UnsafePointer?, + _ pref: UnsafePointer?, + _ width: Int32 +) -> UnsafeMutablePointer? { + guard let commands else { return nil } + let prefix = pref.map { String(cString: $0) } ?? "" + let maxWidth = Int(width) - 3 + + var result = prefix + var lineStart = result.count + + for entry in hciCommandsMap { + let bit = Int(entry.value) + guard commands[bit / 8] & (1 << (bit % 8)) != 0 else { continue } + let piece = "'\(entry.name)' " + if !result.isEmpty && (result.count - lineStart) + entry.name.count > maxWidth { + result.removeLast() + result += "\n" + result += prefix + lineStart = result.count + } + result += piece + } + return strdup(result) +} + +// MARK: - Versions + +/// `char *hci_vertostr(unsigned int ver)` +@c(hci_vertostr) +public func hci_vertostr(_ ver: UInt32) -> UnsafeMutablePointer? { + uint2str(hciVersionMap, ver) +} + +/// `int hci_strtover(char *str, unsigned int *ver)` +@c(hci_strtover) +public func hci_strtover(_ str: UnsafeMutablePointer?, _ ver: UnsafeMutablePointer?) -> Int32 { + let (matched, value) = str2uint(hciVersionMap, str) + if matched { ver?.pointee = value } + return matched ? 1 : 0 +} + +/// `char *lmp_vertostr(unsigned int ver)` +@c(lmp_vertostr) +public func lmp_vertostr(_ ver: UInt32) -> UnsafeMutablePointer? { + uint2str(hciVersionMap, ver) +} + +/// `int lmp_strtover(char *str, unsigned int *ver)` +@c(lmp_strtover) +public func lmp_strtover(_ str: UnsafeMutablePointer?, _ ver: UnsafeMutablePointer?) -> Int32 { + let (matched, value) = str2uint(hciVersionMap, str) + if matched { ver?.pointee = value } + return matched ? 1 : 0 +} + +/// `char *pal_vertostr(unsigned int ver)` +@c(pal_vertostr) +public func pal_vertostr(_ ver: UInt32) -> UnsafeMutablePointer? { + uint2str(hciPALVersionMap, ver) +} + +/// `int pal_strtover(char *str, unsigned int *ver)` +@c(pal_strtover) +public func pal_strtover(_ str: UnsafeMutablePointer?, _ ver: UnsafeMutablePointer?) -> Int32 { + let (matched, value) = str2uint(hciPALVersionMap, str) + if matched { ver?.pointee = value } + return matched ? 1 : 0 +} + +// MARK: - LMP features + +/// `char *lmp_featurestostr(uint8_t *features, char *pref, int width)` +@c(lmp_featurestostr) +public func lmp_featurestostr( + _ features: UnsafeMutablePointer?, + _ pref: UnsafeMutablePointer?, + _ width: Int32 +) -> UnsafeMutablePointer? { + guard let features else { return nil } + let prefix = pref.map { String(cString: $0) } + let maxWidth = Int(width) - 1 + + var result = prefix ?? "" + var lineStart = result.count + + for (byteIndex, row) in lmpFeaturesMap.enumerated() { + let byte = features[byteIndex] + for entry in row where entry.value & byte != 0 { + if (result.count - lineStart) + entry.name.count > maxWidth { + result += "\n" + result += prefix ?? "" + lineStart = result.count + } + result += entry.name + result += " " + } + } + + return strdup(result) +} diff --git a/Sources/BluetoothLinuxABI/gen/HCITables.swift b/Sources/BluetoothLinuxABI/gen/HCITables.swift new file mode 100644 index 0000000..e843ab6 --- /dev/null +++ b/Sources/BluetoothLinuxABI/gen/HCITables.swift @@ -0,0 +1,416 @@ +// +// HCITables.swift +// BluetoothLinux +// +// Generated by scripts/generate-hci-tables.py — do not edit. +// +// Source: BlueZ lib/bluetooth/hci.c's `hci_map` tables, with `#define` +// values resolved against lib/bluetooth/hci.h. +// + +/// HCI device flags (bit tests). +internal let hciDeviceFlagsMap: [(name: String, value: UInt32)] = [ + ("UP", 0x0), + ("INIT", 0x1), + ("RUNNING", 0x2), + ("RAW", 0x8), + ("PSCAN", 0x3), + ("ISCAN", 0x4), + ("INQUIRY", 0x7), + ("AUTH", 0x5), + ("ENCRYPT", 0x6), +] + +/// ACL packet types (bitmask). +internal let hciPacketTypeMap: [(name: String, value: UInt32)] = [ + ("DM1", 0x8), + ("DM3", 0x400), + ("DM5", 0x4000), + ("DH1", 0x10), + ("DH3", 0x800), + ("DH5", 0x8000), + ("HV1", 0x20), + ("HV2", 0x40), + ("HV3", 0x80), + ("2-DH1", 0x2), + ("2-DH3", 0x100), + ("2-DH5", 0x1000), + ("3-DH1", 0x4), + ("3-DH3", 0x200), + ("3-DH5", 0x2000), +] + +/// SCO packet types (bitmask). +internal let hciSCOPacketTypeMap: [(name: String, value: UInt32)] = [ + ("HV1", 0x1), + ("HV2", 0x2), + ("HV3", 0x4), + ("EV3", 0x8), + ("EV4", 0x10), + ("EV5", 0x20), + ("2-EV3", 0x40), + ("2-EV5", 0x100), + ("3-EV3", 0x80), + ("3-EV5", 0x200), +] + +/// Link policy settings (bitmask). +internal let hciLinkPolicyMap: [(name: String, value: UInt32)] = [ + ("NONE", 0x0), + ("RSWITCH", 0x1), + ("HOLD", 0x2), + ("SNIFF", 0x4), + ("PARK", 0x8), +] + +/// Link mode settings (bitmask). +internal let hciLinkModeMap: [(name: String, value: UInt32)] = [ + ("NONE", 0x0), + ("ACCEPT", 0x8000), + ("CENTRAL", 0x1), + ("AUTH", 0x2), + ("ENCRYPT", 0x4), + ("TRUSTED", 0x8), + ("RELIABLE", 0x10), + ("SECURE", 0x20), +] + +/// Core specification version (exact match). +internal let hciVersionMap: [(name: String, value: UInt32)] = [ + ("1.0b", 0x0), + ("1.1", 0x1), + ("1.2", 0x2), + ("2.0", 0x3), + ("2.1", 0x4), + ("3.0", 0x5), + ("4.0", 0x6), + ("4.1", 0x7), + ("4.2", 0x8), + ("5.0", 0x9), + ("5.1", 0xa), + ("5.2", 0xb), + ("5.3", 0xc), + ("5.4", 0xd), +] + +/// 802.11 PAL version (exact match). +internal let hciPALVersionMap: [(name: String, value: UInt32)] = [ + ("3.0", 0x1), +] + +/// Supported Commands bit -> name (232 entries). +internal let hciCommandsMap: [(name: String, value: UInt32)] = [ + ("Inquiry", 0x0), + ("Inquiry Cancel", 0x1), + ("Periodic Inquiry Mode", 0x2), + ("Exit Periodic Inquiry Mode", 0x3), + ("Create Connection", 0x4), + ("Disconnect", 0x5), + ("Add SCO Connection", 0x6), + ("Cancel Create Connection", 0x7), + ("Accept Connection Request", 0x8), + ("Reject Connection Request", 0x9), + ("Link Key Request Reply", 0xa), + ("Link Key Request Negative Reply", 0xb), + ("PIN Code Request Reply", 0xc), + ("PIN Code Request Negative Reply", 0xd), + ("Change Connection Packet Type", 0xe), + ("Authentication Requested", 0xf), + ("Set Connection Encryption", 0x10), + ("Change Connection Link Key", 0x11), + ("Temporary Link Key", 0x12), + ("Remote Name Request", 0x13), + ("Cancel Remote Name Request", 0x14), + ("Read Remote Supported Features", 0x15), + ("Read Remote Extended Features", 0x16), + ("Read Remote Version Information", 0x17), + ("Read Clock Offset", 0x18), + ("Read LMP Handle", 0x19), + ("Reserved", 0x1a), + ("Reserved", 0x1b), + ("Reserved", 0x1c), + ("Reserved", 0x1d), + ("Reserved", 0x1e), + ("Reserved", 0x1f), + ("Reserved", 0x20), + ("Hold Mode", 0x21), + ("Sniff Mode", 0x22), + ("Exit Sniff Mode", 0x23), + ("Park State", 0x24), + ("Exit Park State", 0x25), + ("QoS Setup", 0x26), + ("Role Discovery", 0x27), + ("Switch Role", 0x28), + ("Read Link Policy Settings", 0x29), + ("Write Link Policy Settings", 0x2a), + ("Read Default Link Policy Settings", 0x2b), + ("Write Default Link Policy Settings", 0x2c), + ("Flow Specification", 0x2d), + ("Set Event Mask", 0x2e), + ("Reset", 0x2f), + ("Set Event Filter", 0x30), + ("Flush", 0x31), + ("Read PIN Type", 0x32), + ("Write PIN Type", 0x33), + ("Create New Unit Key", 0x34), + ("Read Stored Link Key", 0x35), + ("Write Stored Link Key", 0x36), + ("Delete Stored Link Key", 0x37), + ("Write Local Name", 0x38), + ("Read Local Name", 0x39), + ("Read Connection Accept Timeout", 0x3a), + ("Write Connection Accept Timeout", 0x3b), + ("Read Page Timeout", 0x3c), + ("Write Page Timeout", 0x3d), + ("Read Scan Enable", 0x3e), + ("Write Scan Enable", 0x3f), + ("Read Page Scan Activity", 0x40), + ("Write Page Scan Activity", 0x41), + ("Read Inquiry Scan Activity", 0x42), + ("Write Inquiry Scan Activity", 0x43), + ("Read Authentication Enable", 0x44), + ("Write Authentication Enable", 0x45), + ("Read Encryption Mode", 0x46), + ("Write Encryption Mode", 0x47), + ("Read Class Of Device", 0x48), + ("Write Class Of Device", 0x49), + ("Read Voice Setting", 0x4a), + ("Write Voice Setting", 0x4b), + ("Read Automatic Flush Timeout", 0x4c), + ("Write Automatic Flush Timeout", 0x4d), + ("Read Num Broadcast Retransmissions", 0x4e), + ("Write Num Broadcast Retransmissions", 0x4f), + ("Read Hold Mode Activity", 0x50), + ("Write Hold Mode Activity", 0x51), + ("Read Transmit Power Level", 0x52), + ("Read Synchronous Flow Control Enable", 0x53), + ("Write Synchronous Flow Control Enable", 0x54), + ("Set Host Controller To Host Flow Control", 0x55), + ("Host Buffer Size", 0x56), + ("Host Number Of Completed Packets", 0x57), + ("Read Link Supervision Timeout", 0x58), + ("Write Link Supervision Timeout", 0x59), + ("Read Number of Supported IAC", 0x5a), + ("Read Current IAC LAP", 0x5b), + ("Write Current IAC LAP", 0x5c), + ("Read Page Scan Period Mode", 0x5d), + ("Write Page Scan Period Mode", 0x5e), + ("Read Page Scan Mode", 0x5f), + ("Write Page Scan Mode", 0x60), + ("Set AFH Channel Classification", 0x61), + ("Reserved", 0x62), + ("Reserved", 0x63), + ("Read Inquiry Scan Type", 0x64), + ("Write Inquiry Scan Type", 0x65), + ("Read Inquiry Mode", 0x66), + ("Write Inquiry Mode", 0x67), + ("Read Page Scan Type", 0x68), + ("Write Page Scan Type", 0x69), + ("Read AFH Channel Assessment Mode", 0x6a), + ("Write AFH Channel Assessment Mode", 0x6b), + ("Reserved", 0x6c), + ("Reserved", 0x6d), + ("Reserved", 0x6e), + ("Reserved", 0x6f), + ("Reserved", 0x70), + ("Reserved", 0x71), + ("Reserved", 0x72), + ("Read Local Version Information", 0x73), + ("Read Local Supported Commands", 0x74), + ("Read Local Supported Features", 0x75), + ("Read Local Extended Features", 0x76), + ("Read Buffer Size", 0x77), + ("Read Country Code", 0x78), + ("Read BD ADDR", 0x79), + ("Read Failed Contact Counter", 0x7a), + ("Reset Failed Contact Counter", 0x7b), + ("Get Link Quality", 0x7c), + ("Read RSSI", 0x7d), + ("Read AFH Channel Map", 0x7e), + ("Read BD Clock", 0x7f), + ("Read Loopback Mode", 0x80), + ("Write Loopback Mode", 0x81), + ("Enable Device Under Test Mode", 0x82), + ("Setup Synchronous Connection", 0x83), + ("Accept Synchronous Connection", 0x84), + ("Reject Synchronous Connection", 0x85), + ("Reserved", 0x86), + ("Reserved", 0x87), + ("Read Extended Inquiry Response", 0x88), + ("Write Extended Inquiry Response", 0x89), + ("Refresh Encryption Key", 0x8a), + ("Reserved", 0x8b), + ("Sniff Subrating", 0x8c), + ("Read Simple Pairing Mode", 0x8d), + ("Write Simple Pairing Mode", 0x8e), + ("Read Local OOB Data", 0x8f), + ("Read Inquiry Response Transmit Power Level", 0x90), + ("Write Inquiry Transmit Power Level", 0x91), + ("Read Default Erroneous Data Reporting", 0x92), + ("Write Default Erroneous Data Reporting", 0x93), + ("Reserved", 0x94), + ("Reserved", 0x95), + ("Reserved", 0x96), + ("IO Capability Request Reply", 0x97), + ("User Confirmation Request Reply", 0x98), + ("User Confirmation Request Negative Reply", 0x99), + ("User Passkey Request Reply", 0x9a), + ("User Passkey Request Negative Reply", 0x9b), + ("Remote OOB Data Request Reply", 0x9c), + ("Write Simple Pairing Debug Mode", 0x9d), + ("Enhanced Flush", 0x9e), + ("Remote OOB Data Request Negative Reply", 0x9f), + ("Reserved", 0xa0), + ("Reserved", 0xa1), + ("Send Keypress Notification", 0xa2), + ("IO Capability Request Negative Reply", 0xa3), + ("Read Encryption Key Size", 0xa4), + ("Reserved", 0xa5), + ("Reserved", 0xa6), + ("Reserved", 0xa7), + ("Create Physical Link", 0xa8), + ("Accept Physical Link", 0xa9), + ("Disconnect Physical Link", 0xaa), + ("Create Logical Link", 0xab), + ("Accept Logical Link", 0xac), + ("Disconnect Logical Link", 0xad), + ("Logical Link Cancel", 0xae), + ("Flow Specification Modify", 0xaf), + ("Read Logical Link Accept Timeout", 0xb0), + ("Write Logical Link Accept Timeout", 0xb1), + ("Set Event Mask Page 2", 0xb2), + ("Read Location Data", 0xb3), + ("Write Location Data", 0xb4), + ("Read Local AMP Info", 0xb5), + ("Read Local AMP_ASSOC", 0xb6), + ("Write Remote AMP_ASSOC", 0xb7), + ("Read Flow Control Mode", 0xb8), + ("Write Flow Control Mode", 0xb9), + ("Read Data Block Size", 0xba), + ("Reserved", 0xbb), + ("Reserved", 0xbc), + ("Enable AMP Receiver Reports", 0xbd), + ("AMP Test End", 0xbe), + ("AMP Test Command", 0xbf), + ("Read Enhanced Transmit Power Level", 0xc0), + ("Reserved", 0xc1), + ("Read Best Effort Flush Timeout", 0xc2), + ("Write Best Effort Flush Timeout", 0xc3), + ("Short Range Mode", 0xc4), + ("Read LE Host Support", 0xc5), + ("Write LE Host Support", 0xc6), + ("Reserved", 0xc7), + ("LE Set Event Mask", 0xc8), + ("LE Read Buffer Size", 0xc9), + ("LE Read Local Supported Features", 0xca), + ("Reserved", 0xcb), + ("LE Set Random Address", 0xcc), + ("LE Set Advertising Parameters", 0xcd), + ("LE Read Advertising Channel TX Power", 0xce), + ("LE Set Advertising Data", 0xcf), + ("LE Set Scan Response Data", 0xd0), + ("LE Set Advertise Enable", 0xd1), + ("LE Set Scan Parameters", 0xd2), + ("LE Set Scan Enable", 0xd3), + ("LE Create Connection", 0xd4), + ("LE Create Connection Cancel", 0xd5), + ("LE Read Accept List Size", 0xd6), + ("LE Clear Accept List", 0xd7), + ("LE Add Device To Accept List", 0xd8), + ("LE Remove Device From Accept List", 0xd9), + ("LE Connection Update", 0xda), + ("LE Set Host Channel Classification", 0xdb), + ("LE Read Channel Map", 0xdc), + ("LE Read Remote Used Features", 0xdd), + ("LE Encrypt", 0xde), + ("LE Rand", 0xdf), + ("LE Start Encryption", 0xe0), + ("LE Long Term Key Request Reply", 0xe1), + ("LE Long Term Key Request Negative Reply", 0xe2), + ("LE Read Supported States", 0xe3), + ("LE Receiver Test", 0xe4), + ("LE Transmitter Test", 0xe5), + ("LE Test End", 0xe6), + ("Reserved", 0xe7), +] + +/// LMP Features page 0, one array per byte (8 bytes x up to 9 bits). +internal let lmpFeaturesMap: [[(name: String, value: UInt8)]] = [ + [ + ("<3-slot packets>", 0x01), + ("<5-slot packets>", 0x02), + ("", 0x04), + ("", 0x08), + ("", 0x10), + ("", 0x20), + ("", 0x40), + ("", 0x80), + ], + [ + ("", 0x01), + ("", 0x02), + ("", 0x04), + ("", 0x08), + ("", 0x10), + ("", 0x20), + ("", 0x40), + ("", 0x80), + ], + [ + ("", 0x01), + ("", 0x02), + ("", 0x04), + ("", 0x08), + ("", 0x80), + ], + [ + ("", 0x01), + ("", 0x02), + ("", 0x04), + ("", 0x08), + ("", 0x10), + ("", 0x20), + ("", 0x40), + ("", 0x80), + ], + [ + ("", 0x01), + ("", 0x02), + ("", 0x04), + ("", 0x08), + ("", 0x10), + ("
", 0x20), + ("", 0x40), + ("<3-slot EDR ACL>", 0x80), + ], + [ + ("<5-slot EDR ACL>", 0x01), + ("", 0x02), + ("", 0x04), + ("", 0x08), + ("", 0x10), + ("", 0x20), + ("", 0x40), + ("<3-slot EDR eSCO>", 0x80), + ], + [ + ("", 0x01), + ("", 0x02), + ("", 0x04), + ("", 0x08), + ("", 0x10), + ("", 0x20), + ("", 0x40), + ("", 0x80), + ], + [ + ("", 0x01), + ("", 0x02), + ("", 0x04), + ("", 0x08), + ("", 0x10), + ("", 0x20), + ("", 0x40), + ("", 0x80), + ], +] diff --git a/Sources/CBluetoothLinuxABI/README.md b/Sources/CBluetoothLinuxABI/README.md new file mode 100644 index 0000000..464225a --- /dev/null +++ b/Sources/CBluetoothLinuxABI/README.md @@ -0,0 +1,55 @@ +# CBluetoothLinuxABI + +The C surface of `libbluetooth.so.3`. + +## Licensing + +This directory is licensed differently from the rest of this repository. + +- `include/bluetooth/*.h` are the eleven public headers **vendored + verbatim from [BlueZ](https://github.com/bluez/bluez) 5.85** (`bluetooth.h`, + `hci.h`, `hci_lib.h`, `sdp.h`, `sdp_lib.h`, `l2cap.h`, `rfcomm.h`, + `sco.h`, `bnep.h`, `cmtp.h`, `hidp.h`, plus `uuid.h`) and are licensed + **GPL-2.0-or-later** — see `include/bluetooth/LICENSE`. They are kept + byte-identical so that every implementation, Swift or C, is compiled + against the exact declarations the reference library exports, and so + that installing them satisfies consumers unchanged. Do not edit them; + to update, re-vendor from a newer BlueZ tag and record the tag here. +- `gen/cbt_stubs.c` is generated by `scripts/gen_stubs.py` and is + original work under this repository's MIT license. + +Binaries produced from the combination of these headers and the Swift +targets are subject to the GPL-2.0-or-later terms of the vendored +headers. Resolving the exact licensing of the shipped +`libbluetooth.so.3` is an open question that should be settled before +the port goes much further — it is the one material difference from the +swift-png precedent this work otherwise follows, where libpng's +permissive license made vendoring `png.h` free. + +## Contents + +| Path | Purpose | +|---|---| +| `include/bluetooth/` | The eleven vendored BlueZ public headers | +| `gen/cbt_stubs.c` | Generated: one loudly-failing stub per not-yet-implemented symbol | + +## The stub table + +`scripts/gen_stubs.py` emits a stub for every name in +`scripts/symbols.txt` that is not in `scripts/implemented.txt`. That is +what makes the port incremental: from the first commit the library +exports all 218 symbols the reference does, so it loads and resolves +against any consumer, and every call that is not implemented yet aborts +with a message naming the symbol rather than silently returning garbage. + +``` +$ ./stubtest +libbluetooth (PureSwift): hci_open_dev is not implemented yet. +Aborted +``` + +Each phase moves names from `symbols.txt` into `implemented.txt` and +watches the conformance diff shrink. Getting that bookkeeping wrong is a +build failure rather than a subtle bug: a name listed as implemented +that is not produces an undefined symbol at link time, and one left +listed after being implemented produces a duplicate symbol. diff --git a/Sources/CBluetoothLinuxABI/gen/cbt_stubs.c b/Sources/CBluetoothLinuxABI/gen/cbt_stubs.c new file mode 100644 index 0000000..ab9acc2 --- /dev/null +++ b/Sources/CBluetoothLinuxABI/gen/cbt_stubs.c @@ -0,0 +1,157 @@ +/* + * Generated by scripts/gen_stubs.py — do not edit. + * + * One stub per exported symbol that PureSwift has not implemented yet. + * Each aborts with the symbol name, so an unported call site is + * immediately identifiable rather than silently wrong. + * + * Regenerate after editing scripts/implemented.txt: + * scripts/gen_stubs.py + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +__attribute__((noreturn)) +static void cbt_unimplemented(const char *symbol) +{ + fprintf(stderr, + "libbluetooth (PureSwift): %s is not implemented yet.\n", + symbol); + abort(); +} + +int sdp_close(sdp_session_t *session) +{ + cbt_unimplemented("sdp_close"); +} + +sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags) +{ + cbt_unimplemented("sdp_connect"); +} + +sdp_session_t *sdp_create(int sk, uint32_t flags) +{ + cbt_unimplemented("sdp_create"); +} + +int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags) +{ + cbt_unimplemented("sdp_device_record_register"); +} + +int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle) +{ + cbt_unimplemented("sdp_device_record_register_binary"); +} + +int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec) +{ + cbt_unimplemented("sdp_device_record_unregister"); +} + +int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle) +{ + cbt_unimplemented("sdp_device_record_unregister_binary"); +} + +int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec) +{ + cbt_unimplemented("sdp_device_record_update"); +} + +int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size) +{ + cbt_unimplemented("sdp_device_record_update_binary"); +} + +uint16_t sdp_gen_tid(sdp_session_t *session) +{ + cbt_unimplemented("sdp_gen_tid"); +} + +int sdp_general_inquiry(inquiry_info *ii, int dev_num, int duration, uint8_t *found) +{ + cbt_unimplemented("sdp_general_inquiry"); +} + +int sdp_get_error(sdp_session_t *session) +{ + cbt_unimplemented("sdp_get_error"); +} + +int sdp_get_socket(const sdp_session_t *session) +{ + cbt_unimplemented("sdp_get_socket"); +} + +int sdp_process(sdp_session_t *session) +{ + cbt_unimplemented("sdp_process"); +} + +int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags) +{ + cbt_unimplemented("sdp_record_register"); +} + +int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec) +{ + cbt_unimplemented("sdp_record_unregister"); +} + +int sdp_record_update(sdp_session_t *sess, const sdp_record_t *rec) +{ + cbt_unimplemented("sdp_record_update"); +} + +int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *req, uint8_t *rsp, uint32_t reqsize, uint32_t *rspsize) +{ + cbt_unimplemented("sdp_send_req_w4_rsp"); +} + +int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list) +{ + cbt_unimplemented("sdp_service_attr_async"); +} + +sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list) +{ + cbt_unimplemented("sdp_service_attr_req"); +} + +int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num) +{ + cbt_unimplemented("sdp_service_search_async"); +} + +int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list) +{ + cbt_unimplemented("sdp_service_search_attr_async"); +} + +int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list) +{ + cbt_unimplemented("sdp_service_search_attr_req"); +} + +int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num, sdp_list_t **rsp_list) +{ + cbt_unimplemented("sdp_service_search_req"); +} + +int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata) +{ + cbt_unimplemented("sdp_set_notify"); +} + diff --git a/Sources/CBluetoothLinuxABI/include/CBluetoothLinuxABI.h b/Sources/CBluetoothLinuxABI/include/CBluetoothLinuxABI.h new file mode 100644 index 0000000..2e80a8a --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/CBluetoothLinuxABI.h @@ -0,0 +1,29 @@ +// +// CBluetoothLinuxABI.h +// BluetoothLinux +// +// Umbrella header for the CBluetoothLinuxABI module. +// +// Everything under `bluetooth/` is vendored verbatim from BlueZ 5.85 +// (GPL-2.0-or-later, see bluetooth/LICENSE). The implementations — +// Swift in BluetoothLinuxABI, generated C stubs in gen/ — are +// type-checked against these declarations. +// + +#ifndef CBLUETOOTHLINUXABI_H +#define CBLUETOOTHLINUXABI_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* CBLUETOOTHLINUXABI_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/LICENSE b/Sources/CBluetoothLinuxABI/include/bluetooth/LICENSE new file mode 100644 index 0000000..6d45519 --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/bluetooth.h b/Sources/CBluetoothLinuxABI/include/bluetooth/bluetooth.h new file mode 100644 index 0000000..88a5d8b --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/bluetooth.h @@ -0,0 +1,531 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * Copyright 2023 NXP + * + * + */ + +#ifndef __BLUETOOTH_H +#define __BLUETOOTH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef AF_BLUETOOTH +#define AF_BLUETOOTH 31 +#define PF_BLUETOOTH AF_BLUETOOTH +#endif + +#define BTPROTO_L2CAP 0 +#define BTPROTO_HCI 1 +#define BTPROTO_SCO 2 +#define BTPROTO_RFCOMM 3 +#define BTPROTO_BNEP 4 +#define BTPROTO_CMTP 5 +#define BTPROTO_HIDP 6 +#define BTPROTO_AVDTP 7 +#define BTPROTO_ISO 8 + +#define SOL_HCI 0 +#define SOL_L2CAP 6 +#define SOL_SCO 17 +#define SOL_RFCOMM 18 + +#ifndef SOL_BLUETOOTH +#define SOL_BLUETOOTH 274 +#endif + +#define BT_SECURITY 4 +struct bt_security { + uint8_t level; + uint8_t key_size; +}; +#define BT_SECURITY_SDP 0 +#define BT_SECURITY_LOW 1 +#define BT_SECURITY_MEDIUM 2 +#define BT_SECURITY_HIGH 3 +#define BT_SECURITY_FIPS 4 + +#define BT_DEFER_SETUP 7 + +#define BT_FLUSHABLE 8 + +#define BT_FLUSHABLE_OFF 0 +#define BT_FLUSHABLE_ON 1 + +#define BT_POWER 9 +struct bt_power { + uint8_t force_active; +}; +#define BT_POWER_FORCE_ACTIVE_OFF 0 +#define BT_POWER_FORCE_ACTIVE_ON 1 + +#define BT_CHANNEL_POLICY 10 + +/* BR/EDR only (default policy) + * AMP controllers cannot be used. + * Channel move requests from the remote device are denied. + * If the L2CAP channel is currently using AMP, move the channel to BR/EDR. + */ +#define BT_CHANNEL_POLICY_BREDR_ONLY 0 + +/* BR/EDR Preferred + * Allow use of AMP controllers. + * If the L2CAP channel is currently on AMP, move it to BR/EDR. + * Channel move requests from the remote device are allowed. + */ +#define BT_CHANNEL_POLICY_BREDR_PREFERRED 1 + +/* AMP Preferred + * Allow use of AMP controllers + * If the L2CAP channel is currently on BR/EDR and AMP controller + * resources are available, initiate a channel move to AMP. + * Channel move requests from the remote device are allowed. + * If the L2CAP socket has not been connected yet, try to create + * and configure the channel directly on an AMP controller rather + * than BR/EDR. + */ +#define BT_CHANNEL_POLICY_AMP_PREFERRED 2 + +#define BT_VOICE 11 +struct bt_voice { + uint16_t setting; +}; + +#define BT_SNDMTU 12 +#define BT_RCVMTU 13 + +#define BT_VOICE_TRANSPARENT 0x0003 +#define BT_VOICE_CVSD_16BIT 0x0060 +#define BT_VOICE_TRANSPARENT_16BIT 0x0063 + +#define BT_PHY 14 + +#define BT_PHY_BR_1M_1SLOT 0x00000001 +#define BT_PHY_BR_1M_3SLOT 0x00000002 +#define BT_PHY_BR_1M_5SLOT 0x00000004 +#define BT_PHY_EDR_2M_1SLOT 0x00000008 +#define BT_PHY_EDR_2M_3SLOT 0x00000010 +#define BT_PHY_EDR_2M_5SLOT 0x00000020 +#define BT_PHY_EDR_3M_1SLOT 0x00000040 +#define BT_PHY_EDR_3M_3SLOT 0x00000080 +#define BT_PHY_EDR_3M_5SLOT 0x00000100 +#define BT_PHY_LE_1M_TX 0x00000200 +#define BT_PHY_LE_1M_RX 0x00000400 +#define BT_PHY_LE_2M_TX 0x00000800 +#define BT_PHY_LE_2M_RX 0x00001000 +#define BT_PHY_LE_CODED_TX 0x00002000 +#define BT_PHY_LE_CODED_RX 0x00004000 + +#define BT_MODE 15 + +#define BT_MODE_BASIC 0x00 +#define BT_MODE_ERTM 0x01 +#define BT_MODE_STREAMING 0x02 +#define BT_MODE_LE_FLOWCTL 0x03 +#define BT_MODE_EXT_FLOWCTL 0x04 + +#define BT_PKT_STATUS 16 + +#define BT_SCM_PKT_STATUS 0x03 +#define BT_SCM_ERROR 0x04 + +#define BT_ISO_QOS 17 + +#define BT_ISO_QOS_CIG_UNSET 0xff +#define BT_ISO_QOS_CIS_UNSET 0xff + +#define BT_ISO_QOS_BIG_UNSET 0xff +#define BT_ISO_QOS_BIS_UNSET 0xff + +#define BT_ISO_SYNC_TIMEOUT 0x07d0 /* 20 secs */ + +/* For an ISO Broadcaster, this value is used to compute + * the desired Periodic Advertising Interval as a function + * of SDU interval, based on the formula: + * + * PA_Interval = SDU_Interval * sync_factor + * + * This is useful for adjusting how frequent to send PA + * announcements for Broadcast Sinks to discover, depending + * on scenario. + */ +#define BT_ISO_SYNC_FACTOR 0x01 + +#define BT_ISO_QOS_GROUP_UNSET 0xff +#define BT_ISO_QOS_STREAM_UNSET 0xff + +struct bt_iso_io_qos { + uint32_t interval; + uint16_t latency; + uint16_t sdu; + uint8_t phy; + uint8_t rtn; +}; + +struct bt_iso_ucast_qos { + uint8_t cig; + uint8_t cis; + uint8_t sca; + uint8_t packing; + uint8_t framing; + struct bt_iso_io_qos in; + struct bt_iso_io_qos out; +}; + +struct bt_iso_bcast_qos { + uint8_t big; + uint8_t bis; + uint8_t sync_factor; + uint8_t packing; + uint8_t framing; + struct bt_iso_io_qos in; + struct bt_iso_io_qos out; + uint8_t encryption; + uint8_t bcode[16]; + uint8_t options; + uint16_t skip; + uint16_t sync_timeout; + uint8_t sync_cte_type; + uint8_t mse; + uint16_t timeout; +}; + +/* (HCI_MAX_PER_AD_LENGTH - EIR_SERVICE_DATA_LENGTH) */ +#define BASE_MAX_LENGTH 248 +struct bt_iso_base { + uint8_t base_len; + uint8_t base[BASE_MAX_LENGTH]; +}; + +struct bt_iso_qos { + union { + struct bt_iso_ucast_qos ucast; + struct bt_iso_bcast_qos bcast; + }; +}; + +#define BT_CODEC 19 +struct bt_codec { + uint8_t id; + uint16_t cid; + uint16_t vid; + uint8_t data_path_id; + uint8_t num_caps; + struct codec_caps { + uint8_t len; + uint8_t data[]; + } caps[]; +} __attribute__((packed)); + +struct bt_codecs { + uint8_t num_codecs; + struct bt_codec codecs[]; +} __attribute__((packed)); + + +/* Connection and socket states */ +enum { + BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */ + BT_OPEN, + BT_BOUND, + BT_LISTEN, + BT_CONNECT, + BT_CONNECT2, + BT_CONFIG, + BT_DISCONN, + BT_CLOSED +}; + +#define BT_ISO_BASE 20 + +#define BT_PKT_SEQNUM 22 + +#define BT_SCM_PKT_SEQNUM 0x05 + +/* Byte order conversions */ +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define htobs(d) (d) +#define htobl(d) (d) +#define htobll(d) (d) +#define btohs(d) (d) +#define btohl(d) (d) +#define btohll(d) (d) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define htobs(d) bswap_16(d) +#define htobl(d) bswap_32(d) +#define htobll(d) bswap_64(d) +#define btohs(d) bswap_16(d) +#define btohl(d) bswap_32(d) +#define btohll(d) bswap_64(d) +#else +#error "Unknown byte order" +#endif + +/* Bluetooth unaligned access */ +#define bt_get_unaligned(ptr) \ +__extension__ ({ \ + struct __attribute__((packed)) { \ + __typeof__(*(ptr)) __v; \ + } *__p = (__typeof__(__p)) (ptr); \ + __p->__v; \ +}) + +#define bt_put_unaligned(val, ptr) \ +do { \ + struct __attribute__((packed)) { \ + __typeof__(*(ptr)) __v; \ + } *__p = (__typeof__(__p)) (ptr); \ + __p->__v = (val); \ +} while(0) + +#if __BYTE_ORDER == __LITTLE_ENDIAN +static inline uint64_t bt_get_le64(const void *ptr) +{ + return bt_get_unaligned((const uint64_t *) ptr); +} + +static inline uint64_t bt_get_be64(const void *ptr) +{ + return bswap_64(bt_get_unaligned((const uint64_t *) ptr)); +} + +static inline uint32_t bt_get_le32(const void *ptr) +{ + return bt_get_unaligned((const uint32_t *) ptr); +} + +static inline uint32_t bt_get_be32(const void *ptr) +{ + return bswap_32(bt_get_unaligned((const uint32_t *) ptr)); +} + +static inline uint16_t bt_get_le16(const void *ptr) +{ + return bt_get_unaligned((const uint16_t *) ptr); +} + +static inline uint16_t bt_get_be16(const void *ptr) +{ + return bswap_16(bt_get_unaligned((const uint16_t *) ptr)); +} + +static inline void bt_put_le64(uint64_t val, const void *ptr) +{ + bt_put_unaligned(val, (uint64_t *) ptr); +} + +static inline void bt_put_be64(uint64_t val, const void *ptr) +{ + bt_put_unaligned(bswap_64(val), (uint64_t *) ptr); +} + +static inline void bt_put_le32(uint32_t val, const void *ptr) +{ + bt_put_unaligned(val, (uint32_t *) ptr); +} + +static inline void bt_put_be32(uint32_t val, const void *ptr) +{ + bt_put_unaligned(bswap_32(val), (uint32_t *) ptr); +} + +static inline void bt_put_le16(uint16_t val, const void *ptr) +{ + bt_put_unaligned(val, (uint16_t *) ptr); +} + +static inline void bt_put_be16(uint16_t val, const void *ptr) +{ + bt_put_unaligned(bswap_16(val), (uint16_t *) ptr); +} + +#elif __BYTE_ORDER == __BIG_ENDIAN +static inline uint64_t bt_get_le64(const void *ptr) +{ + return bswap_64(bt_get_unaligned((const uint64_t *) ptr)); +} + +static inline uint64_t bt_get_be64(const void *ptr) +{ + return bt_get_unaligned((const uint64_t *) ptr); +} + +static inline uint32_t bt_get_le32(const void *ptr) +{ + return bswap_32(bt_get_unaligned((const uint32_t *) ptr)); +} + +static inline uint32_t bt_get_be32(const void *ptr) +{ + return bt_get_unaligned((const uint32_t *) ptr); +} + +static inline uint16_t bt_get_le16(const void *ptr) +{ + return bswap_16(bt_get_unaligned((const uint16_t *) ptr)); +} + +static inline uint16_t bt_get_be16(const void *ptr) +{ + return bt_get_unaligned((const uint16_t *) ptr); +} + +static inline void bt_put_le64(uint64_t val, const void *ptr) +{ + bt_put_unaligned(bswap_64(val), (uint64_t *) ptr); +} + +static inline void bt_put_be64(uint64_t val, const void *ptr) +{ + bt_put_unaligned(val, (uint64_t *) ptr); +} + +static inline void bt_put_le32(uint32_t val, const void *ptr) +{ + bt_put_unaligned(bswap_32(val), (uint32_t *) ptr); +} + +static inline void bt_put_be32(uint32_t val, const void *ptr) +{ + bt_put_unaligned(val, (uint32_t *) ptr); +} + +static inline void bt_put_le16(uint16_t val, const void *ptr) +{ + bt_put_unaligned(bswap_16(val), (uint16_t *) ptr); +} + +static inline void bt_put_be16(uint16_t val, const void *ptr) +{ + bt_put_unaligned(val, (uint16_t *) ptr); +} +#else +#error "Unknown byte order" +#endif + +/* BD Address */ +typedef struct { + uint8_t b[6]; +} __attribute__((packed)) bdaddr_t; + +/* BD Address type */ +#define BDADDR_BREDR 0x00 +#define BDADDR_LE_PUBLIC 0x01 +#define BDADDR_LE_RANDOM 0x02 + +#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) +#define BDADDR_ALL (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) +#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}}) + +/* Copy, swap, convert BD Address */ +static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) +{ + return memcmp(ba1, ba2, sizeof(bdaddr_t)); +} +static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) +{ + memcpy(dst, src, sizeof(bdaddr_t)); +} + +void baswap(bdaddr_t *dst, const bdaddr_t *src); +bdaddr_t *strtoba(const char *str); +char *batostr(const bdaddr_t *ba); +int ba2str(const bdaddr_t *ba, char *str); +int ba2strlc(const bdaddr_t *ba, char *str); +int str2ba(const char *str, bdaddr_t *ba); +int ba2oui(const bdaddr_t *ba, char *oui); +int bachk(const char *str); + +int baprintf(const char *format, ...); +int bafprintf(FILE *stream, const char *format, ...); +int basprintf(char *str, const char *format, ...); +int basnprintf(char *str, size_t size, const char *format, ...); + +void *bt_malloc(size_t size); +void *bt_malloc0(size_t size); +void bt_free(void *ptr); + +int bt_error(uint16_t code); +const char *bt_compidtostr(int id); + +typedef struct { + uint8_t data[3]; +} uint24_t; + +typedef struct { + uint8_t data[16]; +} uint128_t; + +static inline void bswap_128(const void *src, void *dst) +{ + const uint8_t *s = (const uint8_t *) src; + uint8_t *d = (uint8_t *) dst; + int i; + + for (i = 0; i < 16; i++) + d[15 - i] = s[i]; +} + +#if __BYTE_ORDER == __BIG_ENDIAN + +#define ntoh64(x) (x) + +static inline void ntoh128(const uint128_t *src, uint128_t *dst) +{ + memcpy(dst, src, sizeof(uint128_t)); +} + +static inline void btoh128(const uint128_t *src, uint128_t *dst) +{ + bswap_128(src, dst); +} + +#else + +static inline uint64_t ntoh64(uint64_t n) +{ + uint64_t h; + uint64_t tmp = ntohl(n & 0x00000000ffffffff); + + h = ntohl(n >> 32); + h |= tmp << 32; + + return h; +} + +static inline void ntoh128(const uint128_t *src, uint128_t *dst) +{ + bswap_128(src, dst); +} + +static inline void btoh128(const uint128_t *src, uint128_t *dst) +{ + memcpy(dst, src, sizeof(uint128_t)); +} + +#endif + +#define hton64(x) ntoh64(x) +#define hton128(x, y) ntoh128(x, y) +#define htob128(x, y) btoh128(x, y) + +#ifdef __cplusplus +} +#endif + +#endif /* __BLUETOOTH_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/bnep.h b/Sources/CBluetoothLinuxABI/include/bluetooth/bnep.h new file mode 100644 index 0000000..a0d3905 --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/bnep.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + */ + +#ifndef __BNEP_H +#define __BNEP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef ETH_ALEN +#define ETH_ALEN 6 /* from */ +#endif + +/* BNEP UUIDs */ +#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB +#define BNEP_UUID16 0x02 +#define BNEP_UUID32 0x04 +#define BNEP_UUID128 0x16 + +#define BNEP_SVC_PANU 0x1115 +#define BNEP_SVC_NAP 0x1116 +#define BNEP_SVC_GN 0x1117 + +/* BNEP packet types */ +#define BNEP_GENERAL 0x00 +#define BNEP_CONTROL 0x01 +#define BNEP_COMPRESSED 0x02 +#define BNEP_COMPRESSED_SRC_ONLY 0x03 +#define BNEP_COMPRESSED_DST_ONLY 0x04 + +/* BNEP control types */ +#define BNEP_CMD_NOT_UNDERSTOOD 0x00 +#define BNEP_SETUP_CONN_REQ 0x01 +#define BNEP_SETUP_CONN_RSP 0x02 +#define BNEP_FILTER_NET_TYPE_SET 0x03 +#define BNEP_FILTER_NET_TYPE_RSP 0x04 +#define BNEP_FILTER_MULT_ADDR_SET 0x05 +#define BNEP_FILTER_MULT_ADDR_RSP 0x06 + +/* BNEP response messages */ +#define BNEP_SUCCESS 0x00 + +#define BNEP_CONN_INVALID_DST 0x01 +#define BNEP_CONN_INVALID_SRC 0x02 +#define BNEP_CONN_INVALID_SVC 0x03 +#define BNEP_CONN_NOT_ALLOWED 0x04 + +#define BNEP_FILTER_UNSUPPORTED_REQ 0x01 +#define BNEP_FILTER_INVALID_RANGE 0x02 +#define BNEP_FILTER_INVALID_MCADDR 0x02 +#define BNEP_FILTER_LIMIT_REACHED 0x03 +#define BNEP_FILTER_DENIED_SECURITY 0x04 + +/* L2CAP settings */ +#define BNEP_MTU 1691 +#define BNEP_FLUSH_TO 0xffff +#define BNEP_CONNECT_TO 15 +#define BNEP_FILTER_TO 15 + +#ifndef BNEP_PSM +#define BNEP_PSM 0x0f +#endif + +/* BNEP headers */ +#define BNEP_TYPE_MASK 0x7f +#define BNEP_EXT_HEADER 0x80 + +struct bnep_setup_conn_req { + uint8_t type; + uint8_t ctrl; + uint8_t uuid_size; + uint8_t service[0]; +} __attribute__((packed)); + +struct bnep_set_filter_req { + uint8_t type; + uint8_t ctrl; + uint16_t len; + uint8_t list[0]; +} __attribute__((packed)); + +struct bnep_ctrl_cmd_not_understood_cmd { + uint8_t type; + uint8_t ctrl; + uint8_t unkn_ctrl; +} __attribute__((packed)); + +struct bnep_control_rsp { + uint8_t type; + uint8_t ctrl; + uint16_t resp; +} __attribute__((packed)); + +struct bnep_ext_hdr { + uint8_t type; + uint8_t len; + uint8_t data[0]; +} __attribute__((packed)); + +/* BNEP ioctl defines */ +#define BNEPCONNADD _IOW('B', 200, int) +#define BNEPCONNDEL _IOW('B', 201, int) +#define BNEPGETCONNLIST _IOR('B', 210, int) +#define BNEPGETCONNINFO _IOR('B', 211, int) +#define BNEPGETSUPPFEAT _IOR('B', 212, int) + +#define BNEP_SETUP_RESPONSE 0 + +struct bnep_connadd_req { + int sock; /* Connected socket */ + uint32_t flags; + uint16_t role; + char device[16]; /* Name of the Ethernet device */ +}; + +struct bnep_conndel_req { + uint32_t flags; + uint8_t dst[ETH_ALEN]; +}; + +struct bnep_conninfo { + uint32_t flags; + uint16_t role; + uint16_t state; + uint8_t dst[ETH_ALEN]; + char device[16]; +}; + +struct bnep_connlist_req { + uint32_t cnum; + struct bnep_conninfo *ci; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __BNEP_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/cmtp.h b/Sources/CBluetoothLinuxABI/include/bluetooth/cmtp.h new file mode 100644 index 0000000..7ba8bfc --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/cmtp.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + */ + +#ifndef __CMTP_H +#define __CMTP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* CMTP defaults */ +#define CMTP_MINIMUM_MTU 152 +#define CMTP_DEFAULT_MTU 672 + +/* CMTP ioctl defines */ +#define CMTPCONNADD _IOW('C', 200, int) +#define CMTPCONNDEL _IOW('C', 201, int) +#define CMTPGETCONNLIST _IOR('C', 210, int) +#define CMTPGETCONNINFO _IOR('C', 211, int) + +#define CMTP_LOOPBACK 0 + +struct cmtp_connadd_req { + int sock; /* Connected socket */ + uint32_t flags; +}; + +struct cmtp_conndel_req { + bdaddr_t bdaddr; + uint32_t flags; +}; + +struct cmtp_conninfo { + bdaddr_t bdaddr; + uint32_t flags; + uint16_t state; + int num; +}; + +struct cmtp_connlist_req { + uint32_t cnum; + struct cmtp_conninfo *ci; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __CMTP_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/hci.h b/Sources/CBluetoothLinuxABI/include/bluetooth/hci.h new file mode 100644 index 0000000..732477e --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/hci.h @@ -0,0 +1,2459 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + */ + +#ifndef __HCI_H +#define __HCI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#define HCI_MAX_DEV 16 + +#define HCI_MAX_AMP_SIZE (1492 + 4) +#define HCI_MAX_ACL_SIZE 1024 +#define HCI_MAX_SCO_SIZE 255 +#define HCI_MAX_EVENT_SIZE 260 +#define HCI_MAX_FRAME_SIZE (HCI_MAX_AMP_SIZE + 4) + +/* HCI dev events */ +#define HCI_DEV_REG 1 +#define HCI_DEV_UNREG 2 +#define HCI_DEV_UP 3 +#define HCI_DEV_DOWN 4 +#define HCI_DEV_SUSPEND 5 +#define HCI_DEV_RESUME 6 + +/* HCI bus types */ +#define HCI_VIRTUAL 0 +#define HCI_USB 1 +#define HCI_PCCARD 2 +#define HCI_UART 3 +#define HCI_RS232 4 +#define HCI_PCI 5 +#define HCI_SDIO 6 +#define HCI_SPI 7 +#define HCI_I2C 8 +#define HCI_SMD 9 +#define HCI_VIRTIO 10 +#define HCI_IPC 11 + +/* HCI controller types */ +#define HCI_PRIMARY 0x00 +#define HCI_AMP 0x01 +#define HCI_BREDR HCI_PRIMARY + +/* HCI device flags */ +enum { + HCI_UP, + HCI_INIT, + HCI_RUNNING, + + HCI_PSCAN, + HCI_ISCAN, + HCI_AUTH, + HCI_ENCRYPT, + HCI_INQUIRY, + + HCI_RAW, +}; + +/* LE address type */ +enum { + LE_PUBLIC_ADDRESS = 0x00, + LE_RANDOM_ADDRESS = 0x01 +}; + +/* HCI ioctl defines */ +#define HCIDEVUP _IOW('H', 201, int) +#define HCIDEVDOWN _IOW('H', 202, int) +#define HCIDEVRESET _IOW('H', 203, int) +#define HCIDEVRESTAT _IOW('H', 204, int) + +#define HCIGETDEVLIST _IOR('H', 210, int) +#define HCIGETDEVINFO _IOR('H', 211, int) +#define HCIGETCONNLIST _IOR('H', 212, int) +#define HCIGETCONNINFO _IOR('H', 213, int) +#define HCIGETAUTHINFO _IOR('H', 215, int) + +#define HCISETRAW _IOW('H', 220, int) +#define HCISETSCAN _IOW('H', 221, int) +#define HCISETAUTH _IOW('H', 222, int) +#define HCISETENCRYPT _IOW('H', 223, int) +#define HCISETPTYPE _IOW('H', 224, int) +#define HCISETLINKPOL _IOW('H', 225, int) +#define HCISETLINKMODE _IOW('H', 226, int) +#define HCISETACLMTU _IOW('H', 227, int) +#define HCISETSCOMTU _IOW('H', 228, int) + +#define HCIBLOCKADDR _IOW('H', 230, int) +#define HCIUNBLOCKADDR _IOW('H', 231, int) + +#define HCIINQUIRY _IOR('H', 240, int) + +#ifndef __NO_HCI_DEFS + +/* HCI Packet types */ +#define HCI_COMMAND_PKT 0x01 +#define HCI_ACLDATA_PKT 0x02 +#define HCI_SCODATA_PKT 0x03 +#define HCI_EVENT_PKT 0x04 +#define HCI_ISODATA_PKT 0x05 +#define HCI_VENDOR_PKT 0xff + +/* HCI Packet types */ +#define HCI_2DH1 0x0002 +#define HCI_3DH1 0x0004 +#define HCI_DM1 0x0008 +#define HCI_DH1 0x0010 +#define HCI_2DH3 0x0100 +#define HCI_3DH3 0x0200 +#define HCI_DM3 0x0400 +#define HCI_DH3 0x0800 +#define HCI_2DH5 0x1000 +#define HCI_3DH5 0x2000 +#define HCI_DM5 0x4000 +#define HCI_DH5 0x8000 + +#define HCI_HV1 0x0020 +#define HCI_HV2 0x0040 +#define HCI_HV3 0x0080 + +#define HCI_EV3 0x0008 +#define HCI_EV4 0x0010 +#define HCI_EV5 0x0020 +#define HCI_2EV3 0x0040 +#define HCI_3EV3 0x0080 +#define HCI_2EV5 0x0100 +#define HCI_3EV5 0x0200 + +#define SCO_PTYPE_MASK (HCI_HV1 | HCI_HV2 | HCI_HV3) +#define ACL_PTYPE_MASK (HCI_DM1 | HCI_DH1 | HCI_DM3 | HCI_DH3 | HCI_DM5 | HCI_DH5) + +/* HCI Error codes */ +#define HCI_UNKNOWN_COMMAND 0x01 +#define HCI_NO_CONNECTION 0x02 +#define HCI_HARDWARE_FAILURE 0x03 +#define HCI_PAGE_TIMEOUT 0x04 +#define HCI_AUTHENTICATION_FAILURE 0x05 +#define HCI_PIN_OR_KEY_MISSING 0x06 +#define HCI_MEMORY_FULL 0x07 +#define HCI_CONNECTION_TIMEOUT 0x08 +#define HCI_MAX_NUMBER_OF_CONNECTIONS 0x09 +#define HCI_MAX_NUMBER_OF_SCO_CONNECTIONS 0x0a +#define HCI_ACL_CONNECTION_EXISTS 0x0b +#define HCI_COMMAND_DISALLOWED 0x0c +#define HCI_REJECTED_LIMITED_RESOURCES 0x0d +#define HCI_REJECTED_SECURITY 0x0e +#define HCI_REJECTED_PERSONAL 0x0f +#define HCI_HOST_TIMEOUT 0x10 +#define HCI_UNSUPPORTED_FEATURE 0x11 +#define HCI_INVALID_PARAMETERS 0x12 +#define HCI_OE_USER_ENDED_CONNECTION 0x13 +#define HCI_OE_LOW_RESOURCES 0x14 +#define HCI_OE_POWER_OFF 0x15 +#define HCI_CONNECTION_TERMINATED 0x16 +#define HCI_REPEATED_ATTEMPTS 0x17 +#define HCI_PAIRING_NOT_ALLOWED 0x18 +#define HCI_UNKNOWN_LMP_PDU 0x19 +#define HCI_UNSUPPORTED_REMOTE_FEATURE 0x1a +#define HCI_SCO_OFFSET_REJECTED 0x1b +#define HCI_SCO_INTERVAL_REJECTED 0x1c +#define HCI_AIR_MODE_REJECTED 0x1d +#define HCI_INVALID_LMP_PARAMETERS 0x1e +#define HCI_UNSPECIFIED_ERROR 0x1f +#define HCI_UNSUPPORTED_LMP_PARAMETER_VALUE 0x20 +#define HCI_ROLE_CHANGE_NOT_ALLOWED 0x21 +#define HCI_LMP_RESPONSE_TIMEOUT 0x22 +#define HCI_LMP_ERROR_TRANSACTION_COLLISION 0x23 +#define HCI_LMP_PDU_NOT_ALLOWED 0x24 +#define HCI_ENCRYPTION_MODE_NOT_ACCEPTED 0x25 +#define HCI_UNIT_LINK_KEY_USED 0x26 +#define HCI_QOS_NOT_SUPPORTED 0x27 +#define HCI_INSTANT_PASSED 0x28 +#define HCI_PAIRING_NOT_SUPPORTED 0x29 +#define HCI_TRANSACTION_COLLISION 0x2a +#define HCI_QOS_UNACCEPTABLE_PARAMETER 0x2c +#define HCI_QOS_REJECTED 0x2d +#define HCI_CLASSIFICATION_NOT_SUPPORTED 0x2e +#define HCI_INSUFFICIENT_SECURITY 0x2f +#define HCI_PARAMETER_OUT_OF_RANGE 0x30 +#define HCI_ROLE_SWITCH_PENDING 0x32 +#define HCI_SLOT_VIOLATION 0x34 +#define HCI_ROLE_SWITCH_FAILED 0x35 +#define HCI_EIR_TOO_LARGE 0x36 +#define HCI_SIMPLE_PAIRING_NOT_SUPPORTED 0x37 +#define HCI_HOST_BUSY_PAIRING 0x38 + +/* ACL flags */ +#define ACL_START_NO_FLUSH 0x00 +#define ACL_CONT 0x01 +#define ACL_START 0x02 +#define ACL_ACTIVE_BCAST 0x04 +#define ACL_PICO_BCAST 0x08 + +/* Baseband links */ +#define SCO_LINK 0x00 +#define ACL_LINK 0x01 +#define ESCO_LINK 0x02 + +/* LMP features */ +#define LMP_3SLOT 0x01 +#define LMP_5SLOT 0x02 +#define LMP_ENCRYPT 0x04 +#define LMP_SOFFSET 0x08 +#define LMP_TACCURACY 0x10 +#define LMP_RSWITCH 0x20 +#define LMP_HOLD 0x40 +#define LMP_SNIFF 0x80 + +#define LMP_PARK 0x01 +#define LMP_RSSI 0x02 +#define LMP_QUALITY 0x04 +#define LMP_SCO 0x08 +#define LMP_HV2 0x10 +#define LMP_HV3 0x20 +#define LMP_ULAW 0x40 +#define LMP_ALAW 0x80 + +#define LMP_CVSD 0x01 +#define LMP_PSCHEME 0x02 +#define LMP_PCONTROL 0x04 +#define LMP_TRSP_SCO 0x08 +#define LMP_BCAST_ENC 0x80 + +#define LMP_EDR_ACL_2M 0x02 +#define LMP_EDR_ACL_3M 0x04 +#define LMP_ENH_ISCAN 0x08 +#define LMP_ILACE_ISCAN 0x10 +#define LMP_ILACE_PSCAN 0x20 +#define LMP_RSSI_INQ 0x40 +#define LMP_ESCO 0x80 + +#define LMP_EV4 0x01 +#define LMP_EV5 0x02 +#define LMP_AFH_CAP_SLV 0x08 +#define LMP_AFH_CLS_SLV 0x10 +#define LMP_NO_BREDR 0x20 +#define LMP_LE 0x40 +#define LMP_EDR_3SLOT 0x80 + +#define LMP_EDR_5SLOT 0x01 +#define LMP_SNIFF_SUBR 0x02 +#define LMP_PAUSE_ENC 0x04 +#define LMP_AFH_CAP_MST 0x08 +#define LMP_AFH_CLS_MST 0x10 +#define LMP_EDR_ESCO_2M 0x20 +#define LMP_EDR_ESCO_3M 0x40 +#define LMP_EDR_3S_ESCO 0x80 + +#define LMP_EXT_INQ 0x01 +#define LMP_LE_BREDR 0x02 +#define LMP_SIMPLE_PAIR 0x08 +#define LMP_ENCAPS_PDU 0x10 +#define LMP_ERR_DAT_REP 0x20 +#define LMP_NFLUSH_PKTS 0x40 + +#define LMP_LSTO 0x01 +#define LMP_INQ_TX_PWR 0x02 +#define LMP_EPC 0x04 +#define LMP_EXT_FEAT 0x80 + +/* Extended LMP features */ +#define LMP_HOST_SSP 0x01 +#define LMP_HOST_LE 0x02 +#define LMP_HOST_LE_BREDR 0x04 + +/* Link policies */ +#define HCI_LP_RSWITCH 0x0001 +#define HCI_LP_HOLD 0x0002 +#define HCI_LP_SNIFF 0x0004 +#define HCI_LP_PARK 0x0008 + +/* Link mode */ +#define HCI_LM_ACCEPT 0x8000 +#define HCI_LM_MASTER 0x0001 +#define HCI_LM_AUTH 0x0002 +#define HCI_LM_ENCRYPT 0x0004 +#define HCI_LM_TRUSTED 0x0008 +#define HCI_LM_RELIABLE 0x0010 +#define HCI_LM_SECURE 0x0020 + +/* Link Key types */ +#define HCI_LK_COMBINATION 0x00 +#define HCI_LK_LOCAL_UNIT 0x01 +#define HCI_LK_REMOTE_UNIT 0x02 +#define HCI_LK_DEBUG_COMBINATION 0x03 +#define HCI_LK_UNAUTH_COMBINATION 0x04 +#define HCI_LK_AUTH_COMBINATION 0x05 +#define HCI_LK_CHANGED_COMBINATION 0x06 +#define HCI_LK_INVALID 0xFF + +/* ----- HCI Commands ----- */ + +/* Link Control */ +#define OGF_LINK_CTL 0x01 + +#define OCF_INQUIRY 0x0001 +typedef struct { + uint8_t lap[3]; + uint8_t length; /* 1.28s units */ + uint8_t num_rsp; +} __attribute__ ((packed)) inquiry_cp; +#define INQUIRY_CP_SIZE 5 + +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} __attribute__ ((packed)) status_bdaddr_rp; +#define STATUS_BDADDR_RP_SIZE 7 + +#define OCF_INQUIRY_CANCEL 0x0002 + +#define OCF_PERIODIC_INQUIRY 0x0003 +typedef struct { + uint16_t max_period; /* 1.28s units */ + uint16_t min_period; /* 1.28s units */ + uint8_t lap[3]; + uint8_t length; /* 1.28s units */ + uint8_t num_rsp; +} __attribute__ ((packed)) periodic_inquiry_cp; +#define PERIODIC_INQUIRY_CP_SIZE 9 + +#define OCF_EXIT_PERIODIC_INQUIRY 0x0004 + +#define OCF_CREATE_CONN 0x0005 +typedef struct { + bdaddr_t bdaddr; + uint16_t pkt_type; + uint8_t pscan_rep_mode; + uint8_t pscan_mode; + uint16_t clock_offset; + uint8_t role_switch; +} __attribute__ ((packed)) create_conn_cp; +#define CREATE_CONN_CP_SIZE 13 + +#define OCF_DISCONNECT 0x0006 +typedef struct { + uint16_t handle; + uint8_t reason; +} __attribute__ ((packed)) disconnect_cp; +#define DISCONNECT_CP_SIZE 3 + +#define OCF_ADD_SCO 0x0007 +typedef struct { + uint16_t handle; + uint16_t pkt_type; +} __attribute__ ((packed)) add_sco_cp; +#define ADD_SCO_CP_SIZE 4 + +#define OCF_CREATE_CONN_CANCEL 0x0008 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) create_conn_cancel_cp; +#define CREATE_CONN_CANCEL_CP_SIZE 6 + +#define OCF_ACCEPT_CONN_REQ 0x0009 +typedef struct { + bdaddr_t bdaddr; + uint8_t role; +} __attribute__ ((packed)) accept_conn_req_cp; +#define ACCEPT_CONN_REQ_CP_SIZE 7 + +#define OCF_REJECT_CONN_REQ 0x000A +typedef struct { + bdaddr_t bdaddr; + uint8_t reason; +} __attribute__ ((packed)) reject_conn_req_cp; +#define REJECT_CONN_REQ_CP_SIZE 7 + +#define OCF_LINK_KEY_REPLY 0x000B +typedef struct { + bdaddr_t bdaddr; + uint8_t link_key[16]; +} __attribute__ ((packed)) link_key_reply_cp; +#define LINK_KEY_REPLY_CP_SIZE 22 + +#define OCF_LINK_KEY_NEG_REPLY 0x000C + +#define OCF_PIN_CODE_REPLY 0x000D +typedef struct { + bdaddr_t bdaddr; + uint8_t pin_len; + uint8_t pin_code[16]; +} __attribute__ ((packed)) pin_code_reply_cp; +#define PIN_CODE_REPLY_CP_SIZE 23 + +#define OCF_PIN_CODE_NEG_REPLY 0x000E + +#define OCF_SET_CONN_PTYPE 0x000F +typedef struct { + uint16_t handle; + uint16_t pkt_type; +} __attribute__ ((packed)) set_conn_ptype_cp; +#define SET_CONN_PTYPE_CP_SIZE 4 + +#define OCF_AUTH_REQUESTED 0x0011 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) auth_requested_cp; +#define AUTH_REQUESTED_CP_SIZE 2 + +#define OCF_SET_CONN_ENCRYPT 0x0013 +typedef struct { + uint16_t handle; + uint8_t encrypt; +} __attribute__ ((packed)) set_conn_encrypt_cp; +#define SET_CONN_ENCRYPT_CP_SIZE 3 + +#define OCF_CHANGE_CONN_LINK_KEY 0x0015 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) change_conn_link_key_cp; +#define CHANGE_CONN_LINK_KEY_CP_SIZE 2 + +#define OCF_MASTER_LINK_KEY 0x0017 +typedef struct { + uint8_t key_flag; +} __attribute__ ((packed)) master_link_key_cp; +#define MASTER_LINK_KEY_CP_SIZE 1 + +#define OCF_REMOTE_NAME_REQ 0x0019 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_mode; + uint16_t clock_offset; +} __attribute__ ((packed)) remote_name_req_cp; +#define REMOTE_NAME_REQ_CP_SIZE 10 + +#define OCF_REMOTE_NAME_REQ_CANCEL 0x001A +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) remote_name_req_cancel_cp; +#define REMOTE_NAME_REQ_CANCEL_CP_SIZE 6 + +#define OCF_READ_REMOTE_FEATURES 0x001B +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_remote_features_cp; +#define READ_REMOTE_FEATURES_CP_SIZE 2 + +#define OCF_READ_REMOTE_EXT_FEATURES 0x001C +typedef struct { + uint16_t handle; + uint8_t page_num; +} __attribute__ ((packed)) read_remote_ext_features_cp; +#define READ_REMOTE_EXT_FEATURES_CP_SIZE 3 + +#define OCF_READ_REMOTE_VERSION 0x001D +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_remote_version_cp; +#define READ_REMOTE_VERSION_CP_SIZE 2 + +#define OCF_READ_CLOCK_OFFSET 0x001F +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_clock_offset_cp; +#define READ_CLOCK_OFFSET_CP_SIZE 2 + +#define OCF_READ_LMP_HANDLE 0x0020 + +#define OCF_SETUP_SYNC_CONN 0x0028 +typedef struct { + uint16_t handle; + uint32_t tx_bandwith; + uint32_t rx_bandwith; + uint16_t max_latency; + uint16_t voice_setting; + uint8_t retrans_effort; + uint16_t pkt_type; +} __attribute__ ((packed)) setup_sync_conn_cp; +#define SETUP_SYNC_CONN_CP_SIZE 17 + +#define OCF_ACCEPT_SYNC_CONN_REQ 0x0029 +typedef struct { + bdaddr_t bdaddr; + uint32_t tx_bandwith; + uint32_t rx_bandwith; + uint16_t max_latency; + uint16_t voice_setting; + uint8_t retrans_effort; + uint16_t pkt_type; +} __attribute__ ((packed)) accept_sync_conn_req_cp; +#define ACCEPT_SYNC_CONN_REQ_CP_SIZE 21 + +#define OCF_REJECT_SYNC_CONN_REQ 0x002A +typedef struct { + bdaddr_t bdaddr; + uint8_t reason; +} __attribute__ ((packed)) reject_sync_conn_req_cp; +#define REJECT_SYNC_CONN_REQ_CP_SIZE 7 + +#define OCF_IO_CAPABILITY_REPLY 0x002B +typedef struct { + bdaddr_t bdaddr; + uint8_t capability; + uint8_t oob_data; + uint8_t authentication; +} __attribute__ ((packed)) io_capability_reply_cp; +#define IO_CAPABILITY_REPLY_CP_SIZE 9 + +#define OCF_USER_CONFIRM_REPLY 0x002C +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) user_confirm_reply_cp; +#define USER_CONFIRM_REPLY_CP_SIZE 6 + +#define OCF_USER_CONFIRM_NEG_REPLY 0x002D + +#define OCF_USER_PASSKEY_REPLY 0x002E +typedef struct { + bdaddr_t bdaddr; + uint32_t passkey; +} __attribute__ ((packed)) user_passkey_reply_cp; +#define USER_PASSKEY_REPLY_CP_SIZE 10 + +#define OCF_USER_PASSKEY_NEG_REPLY 0x002F + +#define OCF_REMOTE_OOB_DATA_REPLY 0x0030 +typedef struct { + bdaddr_t bdaddr; + uint8_t hash[16]; + uint8_t randomizer[16]; +} __attribute__ ((packed)) remote_oob_data_reply_cp; +#define REMOTE_OOB_DATA_REPLY_CP_SIZE 38 + +#define OCF_REMOTE_OOB_DATA_NEG_REPLY 0x0033 + +#define OCF_IO_CAPABILITY_NEG_REPLY 0x0034 +typedef struct { + bdaddr_t bdaddr; + uint8_t reason; +} __attribute__ ((packed)) io_capability_neg_reply_cp; +#define IO_CAPABILITY_NEG_REPLY_CP_SIZE 7 + +#define OCF_CREATE_PHYSICAL_LINK 0x0035 +typedef struct { + uint8_t handle; + uint8_t key_length; + uint8_t key_type; + uint8_t key[32]; +} __attribute__ ((packed)) create_physical_link_cp; +#define CREATE_PHYSICAL_LINK_CP_SIZE 35 + +#define OCF_ACCEPT_PHYSICAL_LINK 0x0036 +typedef struct { + uint8_t handle; + uint8_t key_length; + uint8_t key_type; + uint8_t key[32]; +} __attribute__ ((packed)) accept_physical_link_cp; +#define ACCEPT_PHYSICAL_LINK_CP_SIZE 35 + +#define OCF_DISCONNECT_PHYSICAL_LINK 0x0037 +typedef struct { + uint8_t handle; + uint8_t reason; +} __attribute__ ((packed)) disconnect_physical_link_cp; +#define DISCONNECT_PHYSICAL_LINK_CP_SIZE 2 + +#define OCF_CREATE_LOGICAL_LINK 0x0038 +typedef struct { + uint8_t handle; + uint8_t tx_flow[16]; + uint8_t rx_flow[16]; +} __attribute__ ((packed)) create_logical_link_cp; +#define CREATE_LOGICAL_LINK_CP_SIZE 33 + +#define OCF_ACCEPT_LOGICAL_LINK 0x0039 + +#define OCF_DISCONNECT_LOGICAL_LINK 0x003A +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) disconnect_logical_link_cp; +#define DISCONNECT_LOGICAL_LINK_CP_SIZE 2 + +#define OCF_LOGICAL_LINK_CANCEL 0x003B +typedef struct { + uint8_t handle; + uint8_t tx_flow_id; +} __attribute__ ((packed)) cancel_logical_link_cp; +#define LOGICAL_LINK_CANCEL_CP_SIZE 2 +typedef struct { + uint8_t status; + uint8_t handle; + uint8_t tx_flow_id; +} __attribute__ ((packed)) cancel_logical_link_rp; +#define LOGICAL_LINK_CANCEL_RP_SIZE 3 + +#define OCF_FLOW_SPEC_MODIFY 0x003C + +/* Link Policy */ +#define OGF_LINK_POLICY 0x02 + +#define OCF_HOLD_MODE 0x0001 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; +} __attribute__ ((packed)) hold_mode_cp; +#define HOLD_MODE_CP_SIZE 6 + +#define OCF_SNIFF_MODE 0x0003 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; + uint16_t attempt; + uint16_t timeout; +} __attribute__ ((packed)) sniff_mode_cp; +#define SNIFF_MODE_CP_SIZE 10 + +#define OCF_EXIT_SNIFF_MODE 0x0004 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) exit_sniff_mode_cp; +#define EXIT_SNIFF_MODE_CP_SIZE 2 + +#define OCF_PARK_MODE 0x0005 +typedef struct { + uint16_t handle; + uint16_t max_interval; + uint16_t min_interval; +} __attribute__ ((packed)) park_mode_cp; +#define PARK_MODE_CP_SIZE 6 + +#define OCF_EXIT_PARK_MODE 0x0006 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) exit_park_mode_cp; +#define EXIT_PARK_MODE_CP_SIZE 2 + +#define OCF_QOS_SETUP 0x0007 +typedef struct { + uint8_t service_type; /* 1 = best effort */ + uint32_t token_rate; /* Byte per seconds */ + uint32_t peak_bandwidth; /* Byte per seconds */ + uint32_t latency; /* Microseconds */ + uint32_t delay_variation; /* Microseconds */ +} __attribute__ ((packed)) hci_qos; +#define HCI_QOS_CP_SIZE 17 +typedef struct { + uint16_t handle; + uint8_t flags; /* Reserved */ + hci_qos qos; +} __attribute__ ((packed)) qos_setup_cp; +#define QOS_SETUP_CP_SIZE (3 + HCI_QOS_CP_SIZE) + +#define OCF_ROLE_DISCOVERY 0x0009 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) role_discovery_cp; +#define ROLE_DISCOVERY_CP_SIZE 2 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t role; +} __attribute__ ((packed)) role_discovery_rp; +#define ROLE_DISCOVERY_RP_SIZE 4 + +#define OCF_SWITCH_ROLE 0x000B +typedef struct { + bdaddr_t bdaddr; + uint8_t role; +} __attribute__ ((packed)) switch_role_cp; +#define SWITCH_ROLE_CP_SIZE 7 + +#define OCF_READ_LINK_POLICY 0x000C +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) read_link_policy_cp; +#define READ_LINK_POLICY_CP_SIZE 2 +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t policy; +} __attribute__ ((packed)) read_link_policy_rp; +#define READ_LINK_POLICY_RP_SIZE 5 + +#define OCF_WRITE_LINK_POLICY 0x000D +typedef struct { + uint16_t handle; + uint16_t policy; +} __attribute__ ((packed)) write_link_policy_cp; +#define WRITE_LINK_POLICY_CP_SIZE 4 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) write_link_policy_rp; +#define WRITE_LINK_POLICY_RP_SIZE 3 + +#define OCF_READ_DEFAULT_LINK_POLICY 0x000E + +#define OCF_WRITE_DEFAULT_LINK_POLICY 0x000F + +#define OCF_FLOW_SPECIFICATION 0x0010 + +#define OCF_SNIFF_SUBRATING 0x0011 +typedef struct { + uint16_t handle; + uint16_t max_latency; + uint16_t min_remote_timeout; + uint16_t min_local_timeout; +} __attribute__ ((packed)) sniff_subrating_cp; +#define SNIFF_SUBRATING_CP_SIZE 8 + +/* Host Controller and Baseband */ +#define OGF_HOST_CTL 0x03 + +#define OCF_SET_EVENT_MASK 0x0001 +typedef struct { + uint8_t mask[8]; +} __attribute__ ((packed)) set_event_mask_cp; +#define SET_EVENT_MASK_CP_SIZE 8 + +#define OCF_RESET 0x0003 + +#define OCF_SET_EVENT_FLT 0x0005 +typedef struct { + uint8_t flt_type; + uint8_t cond_type; + uint8_t condition[]; +} __attribute__ ((packed)) set_event_flt_cp; +#define SET_EVENT_FLT_CP_SIZE 2 + +/* Filter types */ +#define FLT_CLEAR_ALL 0x00 +#define FLT_INQ_RESULT 0x01 +#define FLT_CONN_SETUP 0x02 +/* INQ_RESULT Condition types */ +#define INQ_RESULT_RETURN_ALL 0x00 +#define INQ_RESULT_RETURN_CLASS 0x01 +#define INQ_RESULT_RETURN_BDADDR 0x02 +/* CONN_SETUP Condition types */ +#define CONN_SETUP_ALLOW_ALL 0x00 +#define CONN_SETUP_ALLOW_CLASS 0x01 +#define CONN_SETUP_ALLOW_BDADDR 0x02 +/* CONN_SETUP Conditions */ +#define CONN_SETUP_AUTO_OFF 0x01 +#define CONN_SETUP_AUTO_ON 0x02 + +#define OCF_FLUSH 0x0008 + +#define OCF_READ_PIN_TYPE 0x0009 +typedef struct { + uint8_t status; + uint8_t pin_type; +} __attribute__ ((packed)) read_pin_type_rp; +#define READ_PIN_TYPE_RP_SIZE 2 + +#define OCF_WRITE_PIN_TYPE 0x000A +typedef struct { + uint8_t pin_type; +} __attribute__ ((packed)) write_pin_type_cp; +#define WRITE_PIN_TYPE_CP_SIZE 1 + +#define OCF_CREATE_NEW_UNIT_KEY 0x000B + +#define OCF_READ_STORED_LINK_KEY 0x000D +typedef struct { + bdaddr_t bdaddr; + uint8_t read_all; +} __attribute__ ((packed)) read_stored_link_key_cp; +#define READ_STORED_LINK_KEY_CP_SIZE 7 +typedef struct { + uint8_t status; + uint16_t max_keys; + uint16_t num_keys; +} __attribute__ ((packed)) read_stored_link_key_rp; +#define READ_STORED_LINK_KEY_RP_SIZE 5 + +#define OCF_WRITE_STORED_LINK_KEY 0x0011 +typedef struct { + uint8_t num_keys; + /* variable length part */ +} __attribute__ ((packed)) write_stored_link_key_cp; +#define WRITE_STORED_LINK_KEY_CP_SIZE 1 +typedef struct { + uint8_t status; + uint8_t num_keys; +} __attribute__ ((packed)) write_stored_link_key_rp; +#define READ_WRITE_LINK_KEY_RP_SIZE 2 + +#define OCF_DELETE_STORED_LINK_KEY 0x0012 +typedef struct { + bdaddr_t bdaddr; + uint8_t delete_all; +} __attribute__ ((packed)) delete_stored_link_key_cp; +#define DELETE_STORED_LINK_KEY_CP_SIZE 7 +typedef struct { + uint8_t status; + uint16_t num_keys; +} __attribute__ ((packed)) delete_stored_link_key_rp; +#define DELETE_STORED_LINK_KEY_RP_SIZE 3 + +#define HCI_MAX_NAME_LENGTH 248 + +#define OCF_CHANGE_LOCAL_NAME 0x0013 +typedef struct { + uint8_t name[HCI_MAX_NAME_LENGTH]; +} __attribute__ ((packed)) change_local_name_cp; +#define CHANGE_LOCAL_NAME_CP_SIZE 248 + +#define OCF_READ_LOCAL_NAME 0x0014 +typedef struct { + uint8_t status; + uint8_t name[HCI_MAX_NAME_LENGTH]; +} __attribute__ ((packed)) read_local_name_rp; +#define READ_LOCAL_NAME_RP_SIZE 249 + +#define OCF_READ_CONN_ACCEPT_TIMEOUT 0x0015 +typedef struct { + uint8_t status; + uint16_t timeout; +} __attribute__ ((packed)) read_conn_accept_timeout_rp; +#define READ_CONN_ACCEPT_TIMEOUT_RP_SIZE 3 + +#define OCF_WRITE_CONN_ACCEPT_TIMEOUT 0x0016 +typedef struct { + uint16_t timeout; +} __attribute__ ((packed)) write_conn_accept_timeout_cp; +#define WRITE_CONN_ACCEPT_TIMEOUT_CP_SIZE 2 + +#define OCF_READ_PAGE_TIMEOUT 0x0017 +typedef struct { + uint8_t status; + uint16_t timeout; +} __attribute__ ((packed)) read_page_timeout_rp; +#define READ_PAGE_TIMEOUT_RP_SIZE 3 + +#define OCF_WRITE_PAGE_TIMEOUT 0x0018 +typedef struct { + uint16_t timeout; +} __attribute__ ((packed)) write_page_timeout_cp; +#define WRITE_PAGE_TIMEOUT_CP_SIZE 2 + +#define OCF_READ_SCAN_ENABLE 0x0019 +typedef struct { + uint8_t status; + uint8_t enable; +} __attribute__ ((packed)) read_scan_enable_rp; +#define READ_SCAN_ENABLE_RP_SIZE 2 + +#define OCF_WRITE_SCAN_ENABLE 0x001A + #define SCAN_DISABLED 0x00 + #define SCAN_INQUIRY 0x01 + #define SCAN_PAGE 0x02 + +#define OCF_READ_PAGE_ACTIVITY 0x001B +typedef struct { + uint8_t status; + uint16_t interval; + uint16_t window; +} __attribute__ ((packed)) read_page_activity_rp; +#define READ_PAGE_ACTIVITY_RP_SIZE 5 + +#define OCF_WRITE_PAGE_ACTIVITY 0x001C +typedef struct { + uint16_t interval; + uint16_t window; +} __attribute__ ((packed)) write_page_activity_cp; +#define WRITE_PAGE_ACTIVITY_CP_SIZE 4 + +#define OCF_READ_INQ_ACTIVITY 0x001D +typedef struct { + uint8_t status; + uint16_t interval; + uint16_t window; +} __attribute__ ((packed)) read_inq_activity_rp; +#define READ_INQ_ACTIVITY_RP_SIZE 5 + +#define OCF_WRITE_INQ_ACTIVITY 0x001E +typedef struct { + uint16_t interval; + uint16_t window; +} __attribute__ ((packed)) write_inq_activity_cp; +#define WRITE_INQ_ACTIVITY_CP_SIZE 4 + +#define OCF_READ_AUTH_ENABLE 0x001F + +#define OCF_WRITE_AUTH_ENABLE 0x0020 + #define AUTH_DISABLED 0x00 + #define AUTH_ENABLED 0x01 + +#define OCF_READ_ENCRYPT_MODE 0x0021 + +#define OCF_WRITE_ENCRYPT_MODE 0x0022 + #define ENCRYPT_DISABLED 0x00 + #define ENCRYPT_P2P 0x01 + #define ENCRYPT_BOTH 0x02 + +#define OCF_READ_CLASS_OF_DEV 0x0023 +typedef struct { + uint8_t status; + uint8_t dev_class[3]; +} __attribute__ ((packed)) read_class_of_dev_rp; +#define READ_CLASS_OF_DEV_RP_SIZE 4 + +#define OCF_WRITE_CLASS_OF_DEV 0x0024 +typedef struct { + uint8_t dev_class[3]; +} __attribute__ ((packed)) write_class_of_dev_cp; +#define WRITE_CLASS_OF_DEV_CP_SIZE 3 + +#define OCF_READ_VOICE_SETTING 0x0025 +typedef struct { + uint8_t status; + uint16_t voice_setting; +} __attribute__ ((packed)) read_voice_setting_rp; +#define READ_VOICE_SETTING_RP_SIZE 3 + +#define OCF_WRITE_VOICE_SETTING 0x0026 +typedef struct { + uint16_t voice_setting; +} __attribute__ ((packed)) write_voice_setting_cp; +#define WRITE_VOICE_SETTING_CP_SIZE 2 + +#define OCF_READ_AUTOMATIC_FLUSH_TIMEOUT 0x0027 + +#define OCF_WRITE_AUTOMATIC_FLUSH_TIMEOUT 0x0028 + +#define OCF_READ_NUM_BROADCAST_RETRANS 0x0029 + +#define OCF_WRITE_NUM_BROADCAST_RETRANS 0x002A + +#define OCF_READ_HOLD_MODE_ACTIVITY 0x002B + +#define OCF_WRITE_HOLD_MODE_ACTIVITY 0x002C + +#define OCF_READ_TRANSMIT_POWER_LEVEL 0x002D +typedef struct { + uint16_t handle; + uint8_t type; +} __attribute__ ((packed)) read_transmit_power_level_cp; +#define READ_TRANSMIT_POWER_LEVEL_CP_SIZE 3 +typedef struct { + uint8_t status; + uint16_t handle; + int8_t level; +} __attribute__ ((packed)) read_transmit_power_level_rp; +#define READ_TRANSMIT_POWER_LEVEL_RP_SIZE 4 + +#define OCF_READ_SYNC_FLOW_ENABLE 0x002E + +#define OCF_WRITE_SYNC_FLOW_ENABLE 0x002F + +#define OCF_SET_CONTROLLER_TO_HOST_FC 0x0031 + +#define OCF_HOST_BUFFER_SIZE 0x0033 +typedef struct { + uint16_t acl_mtu; + uint8_t sco_mtu; + uint16_t acl_max_pkt; + uint16_t sco_max_pkt; +} __attribute__ ((packed)) host_buffer_size_cp; +#define HOST_BUFFER_SIZE_CP_SIZE 7 + +#define OCF_HOST_NUM_COMP_PKTS 0x0035 +typedef struct { + uint8_t num_hndl; + /* variable length part */ +} __attribute__ ((packed)) host_num_comp_pkts_cp; +#define HOST_NUM_COMP_PKTS_CP_SIZE 1 + +#define OCF_READ_LINK_SUPERVISION_TIMEOUT 0x0036 +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t timeout; +} __attribute__ ((packed)) read_link_supervision_timeout_rp; +#define READ_LINK_SUPERVISION_TIMEOUT_RP_SIZE 5 + +#define OCF_WRITE_LINK_SUPERVISION_TIMEOUT 0x0037 +typedef struct { + uint16_t handle; + uint16_t timeout; +} __attribute__ ((packed)) write_link_supervision_timeout_cp; +#define WRITE_LINK_SUPERVISION_TIMEOUT_CP_SIZE 4 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) write_link_supervision_timeout_rp; +#define WRITE_LINK_SUPERVISION_TIMEOUT_RP_SIZE 3 + +#define OCF_READ_NUM_SUPPORTED_IAC 0x0038 + +#define MAX_IAC_LAP 0x40 +#define OCF_READ_CURRENT_IAC_LAP 0x0039 +typedef struct { + uint8_t status; + uint8_t num_current_iac; + uint8_t lap[MAX_IAC_LAP][3]; +} __attribute__ ((packed)) read_current_iac_lap_rp; +#define READ_CURRENT_IAC_LAP_RP_SIZE 2+3*MAX_IAC_LAP + +#define OCF_WRITE_CURRENT_IAC_LAP 0x003A +typedef struct { + uint8_t num_current_iac; + uint8_t lap[MAX_IAC_LAP][3]; +} __attribute__ ((packed)) write_current_iac_lap_cp; +#define WRITE_CURRENT_IAC_LAP_CP_SIZE 1+3*MAX_IAC_LAP + +#define OCF_READ_PAGE_SCAN_PERIOD_MODE 0x003B + +#define OCF_WRITE_PAGE_SCAN_PERIOD_MODE 0x003C + +#define OCF_READ_PAGE_SCAN_MODE 0x003D + +#define OCF_WRITE_PAGE_SCAN_MODE 0x003E + +#define OCF_SET_AFH_CLASSIFICATION 0x003F +typedef struct { + uint8_t map[10]; +} __attribute__ ((packed)) set_afh_classification_cp; +#define SET_AFH_CLASSIFICATION_CP_SIZE 10 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) set_afh_classification_rp; +#define SET_AFH_CLASSIFICATION_RP_SIZE 1 + +#define OCF_READ_INQUIRY_SCAN_TYPE 0x0042 +typedef struct { + uint8_t status; + uint8_t type; +} __attribute__ ((packed)) read_inquiry_scan_type_rp; +#define READ_INQUIRY_SCAN_TYPE_RP_SIZE 2 + +#define OCF_WRITE_INQUIRY_SCAN_TYPE 0x0043 +typedef struct { + uint8_t type; +} __attribute__ ((packed)) write_inquiry_scan_type_cp; +#define WRITE_INQUIRY_SCAN_TYPE_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_inquiry_scan_type_rp; +#define WRITE_INQUIRY_SCAN_TYPE_RP_SIZE 1 + +#define OCF_READ_INQUIRY_MODE 0x0044 +typedef struct { + uint8_t status; + uint8_t mode; +} __attribute__ ((packed)) read_inquiry_mode_rp; +#define READ_INQUIRY_MODE_RP_SIZE 2 + +#define OCF_WRITE_INQUIRY_MODE 0x0045 +typedef struct { + uint8_t mode; +} __attribute__ ((packed)) write_inquiry_mode_cp; +#define WRITE_INQUIRY_MODE_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_inquiry_mode_rp; +#define WRITE_INQUIRY_MODE_RP_SIZE 1 + +#define OCF_READ_PAGE_SCAN_TYPE 0x0046 + +#define OCF_WRITE_PAGE_SCAN_TYPE 0x0047 + #define PAGE_SCAN_TYPE_STANDARD 0x00 + #define PAGE_SCAN_TYPE_INTERLACED 0x01 + +#define OCF_READ_AFH_MODE 0x0048 +typedef struct { + uint8_t status; + uint8_t mode; +} __attribute__ ((packed)) read_afh_mode_rp; +#define READ_AFH_MODE_RP_SIZE 2 + +#define OCF_WRITE_AFH_MODE 0x0049 +typedef struct { + uint8_t mode; +} __attribute__ ((packed)) write_afh_mode_cp; +#define WRITE_AFH_MODE_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_afh_mode_rp; +#define WRITE_AFH_MODE_RP_SIZE 1 + +#define HCI_MAX_EIR_LENGTH 240 + +#define OCF_READ_EXT_INQUIRY_RESPONSE 0x0051 +typedef struct { + uint8_t status; + uint8_t fec; + uint8_t data[HCI_MAX_EIR_LENGTH]; +} __attribute__ ((packed)) read_ext_inquiry_response_rp; +#define READ_EXT_INQUIRY_RESPONSE_RP_SIZE 242 + +#define OCF_WRITE_EXT_INQUIRY_RESPONSE 0x0052 +typedef struct { + uint8_t fec; + uint8_t data[HCI_MAX_EIR_LENGTH]; +} __attribute__ ((packed)) write_ext_inquiry_response_cp; +#define WRITE_EXT_INQUIRY_RESPONSE_CP_SIZE 241 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_ext_inquiry_response_rp; +#define WRITE_EXT_INQUIRY_RESPONSE_RP_SIZE 1 + +#define OCF_REFRESH_ENCRYPTION_KEY 0x0053 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) refresh_encryption_key_cp; +#define REFRESH_ENCRYPTION_KEY_CP_SIZE 2 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) refresh_encryption_key_rp; +#define REFRESH_ENCRYPTION_KEY_RP_SIZE 1 + +#define OCF_READ_SIMPLE_PAIRING_MODE 0x0055 +typedef struct { + uint8_t status; + uint8_t mode; +} __attribute__ ((packed)) read_simple_pairing_mode_rp; +#define READ_SIMPLE_PAIRING_MODE_RP_SIZE 2 + +#define OCF_WRITE_SIMPLE_PAIRING_MODE 0x0056 +typedef struct { + uint8_t mode; +} __attribute__ ((packed)) write_simple_pairing_mode_cp; +#define WRITE_SIMPLE_PAIRING_MODE_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_simple_pairing_mode_rp; +#define WRITE_SIMPLE_PAIRING_MODE_RP_SIZE 1 + +#define OCF_READ_LOCAL_OOB_DATA 0x0057 +typedef struct { + uint8_t status; + uint8_t hash[16]; + uint8_t randomizer[16]; +} __attribute__ ((packed)) read_local_oob_data_rp; +#define READ_LOCAL_OOB_DATA_RP_SIZE 33 + +#define OCF_READ_INQ_RESPONSE_TX_POWER_LEVEL 0x0058 +typedef struct { + uint8_t status; + int8_t level; +} __attribute__ ((packed)) read_inq_response_tx_power_level_rp; +#define READ_INQ_RESPONSE_TX_POWER_LEVEL_RP_SIZE 2 + +#define OCF_READ_INQUIRY_TRANSMIT_POWER_LEVEL 0x0058 +typedef struct { + uint8_t status; + int8_t level; +} __attribute__ ((packed)) read_inquiry_transmit_power_level_rp; +#define READ_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 2 + +#define OCF_WRITE_INQUIRY_TRANSMIT_POWER_LEVEL 0x0059 +typedef struct { + int8_t level; +} __attribute__ ((packed)) write_inquiry_transmit_power_level_cp; +#define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_inquiry_transmit_power_level_rp; +#define WRITE_INQUIRY_TRANSMIT_POWER_LEVEL_RP_SIZE 1 + +#define OCF_READ_DEFAULT_ERROR_DATA_REPORTING 0x005A +typedef struct { + uint8_t status; + uint8_t reporting; +} __attribute__ ((packed)) read_default_error_data_reporting_rp; +#define READ_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 2 + +#define OCF_WRITE_DEFAULT_ERROR_DATA_REPORTING 0x005B +typedef struct { + uint8_t reporting; +} __attribute__ ((packed)) write_default_error_data_reporting_cp; +#define WRITE_DEFAULT_ERROR_DATA_REPORTING_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_default_error_data_reporting_rp; +#define WRITE_DEFAULT_ERROR_DATA_REPORTING_RP_SIZE 1 + +#define OCF_ENHANCED_FLUSH 0x005F +typedef struct { + uint16_t handle; + uint8_t type; +} __attribute__ ((packed)) enhanced_flush_cp; +#define ENHANCED_FLUSH_CP_SIZE 3 + +#define OCF_SEND_KEYPRESS_NOTIFY 0x0060 +typedef struct { + bdaddr_t bdaddr; + uint8_t type; +} __attribute__ ((packed)) send_keypress_notify_cp; +#define SEND_KEYPRESS_NOTIFY_CP_SIZE 7 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) send_keypress_notify_rp; +#define SEND_KEYPRESS_NOTIFY_RP_SIZE 1 + +#define OCF_READ_LOGICAL_LINK_ACCEPT_TIMEOUT 0x0061 +typedef struct { + uint8_t status; + uint16_t timeout; +} __attribute__ ((packed)) read_log_link_accept_timeout_rp; +#define READ_LOGICAL_LINK_ACCEPT_TIMEOUT_RP_SIZE 3 + +#define OCF_WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT 0x0062 +typedef struct { + uint16_t timeout; +} __attribute__ ((packed)) write_log_link_accept_timeout_cp; +#define WRITE_LOGICAL_LINK_ACCEPT_TIMEOUT_CP_SIZE 2 + +#define OCF_SET_EVENT_MASK_PAGE_2 0x0063 + +#define OCF_READ_LOCATION_DATA 0x0064 + +#define OCF_WRITE_LOCATION_DATA 0x0065 + +#define OCF_READ_FLOW_CONTROL_MODE 0x0066 + +#define OCF_WRITE_FLOW_CONTROL_MODE 0x0067 + +#define OCF_READ_ENHANCED_TRANSMIT_POWER_LEVEL 0x0068 +typedef struct { + uint8_t status; + uint16_t handle; + int8_t level_gfsk; + int8_t level_dqpsk; + int8_t level_8dpsk; +} __attribute__ ((packed)) read_enhanced_transmit_power_level_rp; +#define READ_ENHANCED_TRANSMIT_POWER_LEVEL_RP_SIZE 6 + +#define OCF_READ_BEST_EFFORT_FLUSH_TIMEOUT 0x0069 +typedef struct { + uint8_t status; + uint32_t timeout; +} __attribute__ ((packed)) read_best_effort_flush_timeout_rp; +#define READ_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 5 + +#define OCF_WRITE_BEST_EFFORT_FLUSH_TIMEOUT 0x006A +typedef struct { + uint16_t handle; + uint32_t timeout; +} __attribute__ ((packed)) write_best_effort_flush_timeout_cp; +#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_CP_SIZE 6 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_best_effort_flush_timeout_rp; +#define WRITE_BEST_EFFORT_FLUSH_TIMEOUT_RP_SIZE 1 + +#define OCF_READ_LE_HOST_SUPPORTED 0x006C +typedef struct { + uint8_t status; + uint8_t le; + uint8_t simul; +} __attribute__ ((packed)) read_le_host_supported_rp; +#define READ_LE_HOST_SUPPORTED_RP_SIZE 3 + +#define OCF_WRITE_LE_HOST_SUPPORTED 0x006D +typedef struct { + uint8_t le; + uint8_t simul; +} __attribute__ ((packed)) write_le_host_supported_cp; +#define WRITE_LE_HOST_SUPPORTED_CP_SIZE 2 + +/* Informational Parameters */ +#define OGF_INFO_PARAM 0x04 + +#define OCF_READ_LOCAL_VERSION 0x0001 +typedef struct { + uint8_t status; + uint8_t hci_ver; + uint16_t hci_rev; + uint8_t lmp_ver; + uint16_t manufacturer; + uint16_t lmp_subver; +} __attribute__ ((packed)) read_local_version_rp; +#define READ_LOCAL_VERSION_RP_SIZE 9 + +#define OCF_READ_LOCAL_COMMANDS 0x0002 +typedef struct { + uint8_t status; + uint8_t commands[64]; +} __attribute__ ((packed)) read_local_commands_rp; +#define READ_LOCAL_COMMANDS_RP_SIZE 65 + +#define OCF_READ_LOCAL_FEATURES 0x0003 +typedef struct { + uint8_t status; + uint8_t features[8]; +} __attribute__ ((packed)) read_local_features_rp; +#define READ_LOCAL_FEATURES_RP_SIZE 9 + +#define OCF_READ_LOCAL_EXT_FEATURES 0x0004 +typedef struct { + uint8_t page_num; +} __attribute__ ((packed)) read_local_ext_features_cp; +#define READ_LOCAL_EXT_FEATURES_CP_SIZE 1 +typedef struct { + uint8_t status; + uint8_t page_num; + uint8_t max_page_num; + uint8_t features[8]; +} __attribute__ ((packed)) read_local_ext_features_rp; +#define READ_LOCAL_EXT_FEATURES_RP_SIZE 11 + +#define OCF_READ_BUFFER_SIZE 0x0005 +typedef struct { + uint8_t status; + uint16_t acl_mtu; + uint8_t sco_mtu; + uint16_t acl_max_pkt; + uint16_t sco_max_pkt; +} __attribute__ ((packed)) read_buffer_size_rp; +#define READ_BUFFER_SIZE_RP_SIZE 8 + +#define OCF_READ_COUNTRY_CODE 0x0007 + +#define OCF_READ_BD_ADDR 0x0009 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} __attribute__ ((packed)) read_bd_addr_rp; +#define READ_BD_ADDR_RP_SIZE 7 + +#define OCF_READ_DATA_BLOCK_SIZE 0x000A +typedef struct { + uint8_t status; + uint16_t max_acl_len; + uint16_t data_block_len; + uint16_t num_blocks; +} __attribute__ ((packed)) read_data_block_size_rp; + +/* Status params */ +#define OGF_STATUS_PARAM 0x05 + +#define OCF_READ_FAILED_CONTACT_COUNTER 0x0001 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t counter; +} __attribute__ ((packed)) read_failed_contact_counter_rp; +#define READ_FAILED_CONTACT_COUNTER_RP_SIZE 4 + +#define OCF_RESET_FAILED_CONTACT_COUNTER 0x0002 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) reset_failed_contact_counter_rp; +#define RESET_FAILED_CONTACT_COUNTER_RP_SIZE 3 + +#define OCF_READ_LINK_QUALITY 0x0003 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t link_quality; +} __attribute__ ((packed)) read_link_quality_rp; +#define READ_LINK_QUALITY_RP_SIZE 4 + +#define OCF_READ_RSSI 0x0005 +typedef struct { + uint8_t status; + uint16_t handle; + int8_t rssi; +} __attribute__ ((packed)) read_rssi_rp; +#define READ_RSSI_RP_SIZE 4 + +#define OCF_READ_AFH_MAP 0x0006 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t mode; + uint8_t map[10]; +} __attribute__ ((packed)) read_afh_map_rp; +#define READ_AFH_MAP_RP_SIZE 14 + +#define OCF_READ_CLOCK 0x0007 +typedef struct { + uint16_t handle; + uint8_t which_clock; +} __attribute__ ((packed)) read_clock_cp; +#define READ_CLOCK_CP_SIZE 3 +typedef struct { + uint8_t status; + uint16_t handle; + uint32_t clock; + uint16_t accuracy; +} __attribute__ ((packed)) read_clock_rp; +#define READ_CLOCK_RP_SIZE 9 + +#define OCF_READ_LOCAL_AMP_INFO 0x0009 +typedef struct { + uint8_t status; + uint8_t amp_status; + uint32_t total_bandwidth; + uint32_t max_guaranteed_bandwidth; + uint32_t min_latency; + uint32_t max_pdu_size; + uint8_t controller_type; + uint16_t pal_caps; + uint16_t max_amp_assoc_length; + uint32_t max_flush_timeout; + uint32_t best_effort_flush_timeout; +} __attribute__ ((packed)) read_local_amp_info_rp; +#define READ_LOCAL_AMP_INFO_RP_SIZE 31 + +#define OCF_READ_LOCAL_AMP_ASSOC 0x000A +typedef struct { + uint8_t handle; + uint16_t length_so_far; + uint16_t assoc_length; +} __attribute__ ((packed)) read_local_amp_assoc_cp; +#define READ_LOCAL_AMP_ASSOC_CP_SIZE 5 +typedef struct { + uint8_t status; + uint8_t handle; + uint16_t length; + uint8_t fragment[HCI_MAX_NAME_LENGTH]; +} __attribute__ ((packed)) read_local_amp_assoc_rp; +#define READ_LOCAL_AMP_ASSOC_RP_SIZE 252 + +#define OCF_WRITE_REMOTE_AMP_ASSOC 0x000B +typedef struct { + uint8_t handle; + uint16_t length_so_far; + uint16_t remaining_length; + uint8_t fragment[HCI_MAX_NAME_LENGTH]; +} __attribute__ ((packed)) write_remote_amp_assoc_cp; +#define WRITE_REMOTE_AMP_ASSOC_CP_SIZE 253 +typedef struct { + uint8_t status; + uint8_t handle; +} __attribute__ ((packed)) write_remote_amp_assoc_rp; +#define WRITE_REMOTE_AMP_ASSOC_RP_SIZE 2 + +/* Testing commands */ +#define OGF_TESTING_CMD 0x3e + +#define OCF_READ_LOOPBACK_MODE 0x0001 + +#define OCF_WRITE_LOOPBACK_MODE 0x0002 + +#define OCF_ENABLE_DEVICE_UNDER_TEST_MODE 0x0003 + +#define OCF_WRITE_SIMPLE_PAIRING_DEBUG_MODE 0x0004 +typedef struct { + uint8_t mode; +} __attribute__ ((packed)) write_simple_pairing_debug_mode_cp; +#define WRITE_SIMPLE_PAIRING_DEBUG_MODE_CP_SIZE 1 +typedef struct { + uint8_t status; +} __attribute__ ((packed)) write_simple_pairing_debug_mode_rp; +#define WRITE_SIMPLE_PAIRING_DEBUG_MODE_RP_SIZE 1 + +/* LE commands */ +#define OGF_LE_CTL 0x08 + +#define OCF_LE_SET_EVENT_MASK 0x0001 +typedef struct { + uint8_t mask[8]; +} __attribute__ ((packed)) le_set_event_mask_cp; +#define LE_SET_EVENT_MASK_CP_SIZE 8 + +#define OCF_LE_READ_BUFFER_SIZE 0x0002 +typedef struct { + uint8_t status; + uint16_t pkt_len; + uint8_t max_pkt; +} __attribute__ ((packed)) le_read_buffer_size_rp; +#define LE_READ_BUFFER_SIZE_RP_SIZE 4 + +#define OCF_LE_READ_LOCAL_SUPPORTED_FEATURES 0x0003 +typedef struct { + uint8_t status; + uint8_t features[8]; +} __attribute__ ((packed)) le_read_local_supported_features_rp; +#define LE_READ_LOCAL_SUPPORTED_FEATURES_RP_SIZE 9 + +#define OCF_LE_SET_RANDOM_ADDRESS 0x0005 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) le_set_random_address_cp; +#define LE_SET_RANDOM_ADDRESS_CP_SIZE 6 + +#define OCF_LE_SET_ADVERTISING_PARAMETERS 0x0006 +typedef struct { + uint16_t min_interval; + uint16_t max_interval; + uint8_t advtype; + uint8_t own_bdaddr_type; + uint8_t direct_bdaddr_type; + bdaddr_t direct_bdaddr; + uint8_t chan_map; + uint8_t filter; +} __attribute__ ((packed)) le_set_advertising_parameters_cp; +#define LE_SET_ADVERTISING_PARAMETERS_CP_SIZE 15 + +#define OCF_LE_READ_ADVERTISING_CHANNEL_TX_POWER 0x0007 +typedef struct { + uint8_t status; + int8_t level; +} __attribute__ ((packed)) le_read_advertising_channel_tx_power_rp; +#define LE_READ_ADVERTISING_CHANNEL_TX_POWER_RP_SIZE 2 + +#define OCF_LE_SET_ADVERTISING_DATA 0x0008 +typedef struct { + uint8_t length; + uint8_t data[31]; +} __attribute__ ((packed)) le_set_advertising_data_cp; +#define LE_SET_ADVERTISING_DATA_CP_SIZE 32 + +#define OCF_LE_SET_SCAN_RESPONSE_DATA 0x0009 +typedef struct { + uint8_t length; + uint8_t data[31]; +} __attribute__ ((packed)) le_set_scan_response_data_cp; +#define LE_SET_SCAN_RESPONSE_DATA_CP_SIZE 32 + +#define OCF_LE_SET_ADVERTISE_ENABLE 0x000A +typedef struct { + uint8_t enable; +} __attribute__ ((packed)) le_set_advertise_enable_cp; +#define LE_SET_ADVERTISE_ENABLE_CP_SIZE 1 + +#define OCF_LE_SET_SCAN_PARAMETERS 0x000B +typedef struct { + uint8_t type; + uint16_t interval; + uint16_t window; + uint8_t own_bdaddr_type; + uint8_t filter; +} __attribute__ ((packed)) le_set_scan_parameters_cp; +#define LE_SET_SCAN_PARAMETERS_CP_SIZE 7 + +#define OCF_LE_SET_SCAN_ENABLE 0x000C +typedef struct { + uint8_t enable; + uint8_t filter_dup; +} __attribute__ ((packed)) le_set_scan_enable_cp; +#define LE_SET_SCAN_ENABLE_CP_SIZE 2 + +#define OCF_LE_CREATE_CONN 0x000D +typedef struct { + uint16_t interval; + uint16_t window; + uint8_t initiator_filter; + uint8_t peer_bdaddr_type; + bdaddr_t peer_bdaddr; + uint8_t own_bdaddr_type; + uint16_t min_interval; + uint16_t max_interval; + uint16_t latency; + uint16_t supervision_timeout; + uint16_t min_ce_length; + uint16_t max_ce_length; +} __attribute__ ((packed)) le_create_connection_cp; +#define LE_CREATE_CONN_CP_SIZE 25 + +#define OCF_LE_CREATE_CONN_CANCEL 0x000E + +#define OCF_LE_READ_WHITE_LIST_SIZE 0x000F +typedef struct { + uint8_t status; + uint8_t size; +} __attribute__ ((packed)) le_read_white_list_size_rp; +#define LE_READ_WHITE_LIST_SIZE_RP_SIZE 2 + +#define OCF_LE_CLEAR_WHITE_LIST 0x0010 + +#define OCF_LE_ADD_DEVICE_TO_WHITE_LIST 0x0011 +typedef struct { + uint8_t bdaddr_type; + bdaddr_t bdaddr; +} __attribute__ ((packed)) le_add_device_to_white_list_cp; +#define LE_ADD_DEVICE_TO_WHITE_LIST_CP_SIZE 7 + +#define OCF_LE_REMOVE_DEVICE_FROM_WHITE_LIST 0x0012 +typedef struct { + uint8_t bdaddr_type; + bdaddr_t bdaddr; +} __attribute__ ((packed)) le_remove_device_from_white_list_cp; +#define LE_REMOVE_DEVICE_FROM_WHITE_LIST_CP_SIZE 7 + +#define OCF_LE_CONN_UPDATE 0x0013 +typedef struct { + uint16_t handle; + uint16_t min_interval; + uint16_t max_interval; + uint16_t latency; + uint16_t supervision_timeout; + uint16_t min_ce_length; + uint16_t max_ce_length; +} __attribute__ ((packed)) le_connection_update_cp; +#define LE_CONN_UPDATE_CP_SIZE 14 + +#define OCF_LE_SET_HOST_CHANNEL_CLASSIFICATION 0x0014 +typedef struct { + uint8_t map[5]; +} __attribute__ ((packed)) le_set_host_channel_classification_cp; +#define LE_SET_HOST_CHANNEL_CLASSIFICATION_CP_SIZE 5 + +#define OCF_LE_READ_CHANNEL_MAP 0x0015 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) le_read_channel_map_cp; +#define LE_READ_CHANNEL_MAP_CP_SIZE 2 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t map[5]; +} __attribute__ ((packed)) le_read_channel_map_rp; +#define LE_READ_CHANNEL_MAP_RP_SIZE 8 + +#define OCF_LE_READ_REMOTE_USED_FEATURES 0x0016 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) le_read_remote_used_features_cp; +#define LE_READ_REMOTE_USED_FEATURES_CP_SIZE 2 + +#define OCF_LE_ENCRYPT 0x0017 +typedef struct { + uint8_t key[16]; + uint8_t plaintext[16]; +} __attribute__ ((packed)) le_encrypt_cp; +#define LE_ENCRYPT_CP_SIZE 32 +typedef struct { + uint8_t status; + uint8_t data[16]; +} __attribute__ ((packed)) le_encrypt_rp; +#define LE_ENCRYPT_RP_SIZE 17 + +#define OCF_LE_RAND 0x0018 +typedef struct { + uint8_t status; + uint64_t random; +} __attribute__ ((packed)) le_rand_rp; +#define LE_RAND_RP_SIZE 9 + +#define OCF_LE_START_ENCRYPTION 0x0019 +typedef struct { + uint16_t handle; + uint64_t random; + uint16_t diversifier; + uint8_t key[16]; +} __attribute__ ((packed)) le_start_encryption_cp; +#define LE_START_ENCRYPTION_CP_SIZE 28 + +#define OCF_LE_LTK_REPLY 0x001A +typedef struct { + uint16_t handle; + uint8_t key[16]; +} __attribute__ ((packed)) le_ltk_reply_cp; +#define LE_LTK_REPLY_CP_SIZE 18 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) le_ltk_reply_rp; +#define LE_LTK_REPLY_RP_SIZE 3 + +#define OCF_LE_LTK_NEG_REPLY 0x001B +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) le_ltk_neg_reply_cp; +#define LE_LTK_NEG_REPLY_CP_SIZE 2 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) le_ltk_neg_reply_rp; +#define LE_LTK_NEG_REPLY_RP_SIZE 3 + +#define OCF_LE_READ_SUPPORTED_STATES 0x001C +typedef struct { + uint8_t status; + uint64_t states; +} __attribute__ ((packed)) le_read_supported_states_rp; +#define LE_READ_SUPPORTED_STATES_RP_SIZE 9 + +#define OCF_LE_RECEIVER_TEST 0x001D +typedef struct { + uint8_t frequency; +} __attribute__ ((packed)) le_receiver_test_cp; +#define LE_RECEIVER_TEST_CP_SIZE 1 + +#define OCF_LE_TRANSMITTER_TEST 0x001E +typedef struct { + uint8_t frequency; + uint8_t length; + uint8_t payload; +} __attribute__ ((packed)) le_transmitter_test_cp; +#define LE_TRANSMITTER_TEST_CP_SIZE 3 + +#define OCF_LE_TEST_END 0x001F +typedef struct { + uint8_t status; + uint16_t num_pkts; +} __attribute__ ((packed)) le_test_end_rp; +#define LE_TEST_END_RP_SIZE 3 + +#define OCF_LE_ADD_DEVICE_TO_RESOLV_LIST 0x0027 +typedef struct { + uint8_t bdaddr_type; + bdaddr_t bdaddr; + uint8_t peer_irk[16]; + uint8_t local_irk[16]; +} __attribute__ ((packed)) le_add_device_to_resolv_list_cp; +#define LE_ADD_DEVICE_TO_RESOLV_LIST_CP_SIZE 39 + +#define OCF_LE_REMOVE_DEVICE_FROM_RESOLV_LIST 0x0028 +typedef struct { + uint8_t bdaddr_type; + bdaddr_t bdaddr; +} __attribute__ ((packed)) le_remove_device_from_resolv_list_cp; +#define LE_REMOVE_DEVICE_FROM_RESOLV_LIST_CP_SIZE 7 + +#define OCF_LE_CLEAR_RESOLV_LIST 0x0029 + +#define OCF_LE_READ_RESOLV_LIST_SIZE 0x002A +typedef struct { + uint8_t status; + uint8_t size; +} __attribute__ ((packed)) le_read_resolv_list_size_rp; +#define LE_READ_RESOLV_LIST_SIZE_RP_SIZE 2 + +#define OCF_LE_SET_ADDRESS_RESOLUTION_ENABLE 0x002D +typedef struct { + uint8_t enable; +} __attribute__ ((packed)) le_set_address_resolution_enable_cp; +#define LE_SET_ADDRESS_RESOLUTION_ENABLE_CP_SIZE 1 + +/* Vendor specific commands */ +#define OGF_VENDOR_CMD 0x3f + +/* ---- HCI Events ---- */ + +#define EVT_INQUIRY_COMPLETE 0x01 + +#define EVT_INQUIRY_RESULT 0x02 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t pscan_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; +} __attribute__ ((packed)) inquiry_info; +#define INQUIRY_INFO_SIZE 14 + +#define EVT_CONN_COMPLETE 0x03 +typedef struct { + uint8_t status; + uint16_t handle; + bdaddr_t bdaddr; + uint8_t link_type; + uint8_t encr_mode; +} __attribute__ ((packed)) evt_conn_complete; +#define EVT_CONN_COMPLETE_SIZE 11 + +#define EVT_CONN_REQUEST 0x04 +typedef struct { + bdaddr_t bdaddr; + uint8_t dev_class[3]; + uint8_t link_type; +} __attribute__ ((packed)) evt_conn_request; +#define EVT_CONN_REQUEST_SIZE 10 + +#define EVT_DISCONN_COMPLETE 0x05 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t reason; +} __attribute__ ((packed)) evt_disconn_complete; +#define EVT_DISCONN_COMPLETE_SIZE 4 + +#define EVT_AUTH_COMPLETE 0x06 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) evt_auth_complete; +#define EVT_AUTH_COMPLETE_SIZE 3 + +#define EVT_REMOTE_NAME_REQ_COMPLETE 0x07 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; + uint8_t name[HCI_MAX_NAME_LENGTH]; +} __attribute__ ((packed)) evt_remote_name_req_complete; +#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255 + +#define EVT_ENCRYPT_CHANGE 0x08 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t encrypt; +} __attribute__ ((packed)) evt_encrypt_change; +#define EVT_ENCRYPT_CHANGE_SIZE 4 + +#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE 0x09 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) evt_change_conn_link_key_complete; +#define EVT_CHANGE_CONN_LINK_KEY_COMPLETE_SIZE 3 + +#define EVT_MASTER_LINK_KEY_COMPLETE 0x0A +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t key_flag; +} __attribute__ ((packed)) evt_master_link_key_complete; +#define EVT_MASTER_LINK_KEY_COMPLETE_SIZE 4 + +#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t features[8]; +} __attribute__ ((packed)) evt_read_remote_features_complete; +#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11 + +#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t lmp_ver; + uint16_t manufacturer; + uint16_t lmp_subver; +} __attribute__ ((packed)) evt_read_remote_version_complete; +#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8 + +#define EVT_QOS_SETUP_COMPLETE 0x0D +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t flags; /* Reserved */ + hci_qos qos; +} __attribute__ ((packed)) evt_qos_setup_complete; +#define EVT_QOS_SETUP_COMPLETE_SIZE (4 + HCI_QOS_CP_SIZE) + +#define EVT_CMD_COMPLETE 0x0E +typedef struct { + uint8_t ncmd; + uint16_t opcode; +} __attribute__ ((packed)) evt_cmd_complete; +#define EVT_CMD_COMPLETE_SIZE 3 + +#define EVT_CMD_STATUS 0x0F +typedef struct { + uint8_t status; + uint8_t ncmd; + uint16_t opcode; +} __attribute__ ((packed)) evt_cmd_status; +#define EVT_CMD_STATUS_SIZE 4 + +#define EVT_HARDWARE_ERROR 0x10 +typedef struct { + uint8_t code; +} __attribute__ ((packed)) evt_hardware_error; +#define EVT_HARDWARE_ERROR_SIZE 1 + +#define EVT_FLUSH_OCCURRED 0x11 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) evt_flush_occured; +#define EVT_FLUSH_OCCURRED_SIZE 2 + +#define EVT_ROLE_CHANGE 0x12 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; + uint8_t role; +} __attribute__ ((packed)) evt_role_change; +#define EVT_ROLE_CHANGE_SIZE 8 + +#define EVT_NUM_COMP_PKTS 0x13 +typedef struct { + uint8_t num_hndl; + /* variable length part */ +} __attribute__ ((packed)) evt_num_comp_pkts; +#define EVT_NUM_COMP_PKTS_SIZE 1 + +#define EVT_MODE_CHANGE 0x14 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t mode; + uint16_t interval; +} __attribute__ ((packed)) evt_mode_change; +#define EVT_MODE_CHANGE_SIZE 6 + +#define EVT_RETURN_LINK_KEYS 0x15 +typedef struct { + uint8_t num_keys; + /* variable length part */ +} __attribute__ ((packed)) evt_return_link_keys; +#define EVT_RETURN_LINK_KEYS_SIZE 1 + +#define EVT_PIN_CODE_REQ 0x16 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) evt_pin_code_req; +#define EVT_PIN_CODE_REQ_SIZE 6 + +#define EVT_LINK_KEY_REQ 0x17 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) evt_link_key_req; +#define EVT_LINK_KEY_REQ_SIZE 6 + +#define EVT_LINK_KEY_NOTIFY 0x18 +typedef struct { + bdaddr_t bdaddr; + uint8_t link_key[16]; + uint8_t key_type; +} __attribute__ ((packed)) evt_link_key_notify; +#define EVT_LINK_KEY_NOTIFY_SIZE 23 + +#define EVT_LOOPBACK_COMMAND 0x19 + +#define EVT_DATA_BUFFER_OVERFLOW 0x1A +typedef struct { + uint8_t link_type; +} __attribute__ ((packed)) evt_data_buffer_overflow; +#define EVT_DATA_BUFFER_OVERFLOW_SIZE 1 + +#define EVT_MAX_SLOTS_CHANGE 0x1B +typedef struct { + uint16_t handle; + uint8_t max_slots; +} __attribute__ ((packed)) evt_max_slots_change; +#define EVT_MAX_SLOTS_CHANGE_SIZE 3 + +#define EVT_READ_CLOCK_OFFSET_COMPLETE 0x1C +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t clock_offset; +} __attribute__ ((packed)) evt_read_clock_offset_complete; +#define EVT_READ_CLOCK_OFFSET_COMPLETE_SIZE 5 + +#define EVT_CONN_PTYPE_CHANGED 0x1D +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t ptype; +} __attribute__ ((packed)) evt_conn_ptype_changed; +#define EVT_CONN_PTYPE_CHANGED_SIZE 5 + +#define EVT_QOS_VIOLATION 0x1E +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) evt_qos_violation; +#define EVT_QOS_VIOLATION_SIZE 2 + +#define EVT_PSCAN_REP_MODE_CHANGE 0x20 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; +} __attribute__ ((packed)) evt_pscan_rep_mode_change; +#define EVT_PSCAN_REP_MODE_CHANGE_SIZE 7 + +#define EVT_FLOW_SPEC_COMPLETE 0x21 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t flags; + uint8_t direction; + hci_qos qos; +} __attribute__ ((packed)) evt_flow_spec_complete; +#define EVT_FLOW_SPEC_COMPLETE_SIZE (5 + HCI_QOS_CP_SIZE) + +#define EVT_INQUIRY_RESULT_WITH_RSSI 0x22 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; +} __attribute__ ((packed)) inquiry_info_with_rssi; +#define INQUIRY_INFO_WITH_RSSI_SIZE 14 +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t pscan_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; +} __attribute__ ((packed)) inquiry_info_with_rssi_and_pscan_mode; +#define INQUIRY_INFO_WITH_RSSI_AND_PSCAN_MODE_SIZE 15 + +#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE 0x23 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t page_num; + uint8_t max_page_num; + uint8_t features[8]; +} __attribute__ ((packed)) evt_read_remote_ext_features_complete; +#define EVT_READ_REMOTE_EXT_FEATURES_COMPLETE_SIZE 13 + +#define EVT_SYNC_CONN_COMPLETE 0x2C +typedef struct { + uint8_t status; + uint16_t handle; + bdaddr_t bdaddr; + uint8_t link_type; + uint8_t trans_interval; + uint8_t retrans_window; + uint16_t rx_pkt_len; + uint16_t tx_pkt_len; + uint8_t air_mode; +} __attribute__ ((packed)) evt_sync_conn_complete; +#define EVT_SYNC_CONN_COMPLETE_SIZE 17 + +#define EVT_SYNC_CONN_CHANGED 0x2D +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t trans_interval; + uint8_t retrans_window; + uint16_t rx_pkt_len; + uint16_t tx_pkt_len; +} __attribute__ ((packed)) evt_sync_conn_changed; +#define EVT_SYNC_CONN_CHANGED_SIZE 9 + +#define EVT_SNIFF_SUBRATING 0x2E +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t max_tx_latency; + uint16_t max_rx_latency; + uint16_t min_remote_timeout; + uint16_t min_local_timeout; +} __attribute__ ((packed)) evt_sniff_subrating; +#define EVT_SNIFF_SUBRATING_SIZE 11 + +#define EVT_EXTENDED_INQUIRY_RESULT 0x2F +typedef struct { + bdaddr_t bdaddr; + uint8_t pscan_rep_mode; + uint8_t pscan_period_mode; + uint8_t dev_class[3]; + uint16_t clock_offset; + int8_t rssi; + uint8_t data[HCI_MAX_EIR_LENGTH]; +} __attribute__ ((packed)) extended_inquiry_info; +#define EXTENDED_INQUIRY_INFO_SIZE 254 + +#define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE 0x30 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) evt_encryption_key_refresh_complete; +#define EVT_ENCRYPTION_KEY_REFRESH_COMPLETE_SIZE 3 + +#define EVT_IO_CAPABILITY_REQUEST 0x31 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) evt_io_capability_request; +#define EVT_IO_CAPABILITY_REQUEST_SIZE 6 + +#define EVT_IO_CAPABILITY_RESPONSE 0x32 +typedef struct { + bdaddr_t bdaddr; + uint8_t capability; + uint8_t oob_data; + uint8_t authentication; +} __attribute__ ((packed)) evt_io_capability_response; +#define EVT_IO_CAPABILITY_RESPONSE_SIZE 9 + +#define EVT_USER_CONFIRM_REQUEST 0x33 +typedef struct { + bdaddr_t bdaddr; + uint32_t passkey; +} __attribute__ ((packed)) evt_user_confirm_request; +#define EVT_USER_CONFIRM_REQUEST_SIZE 10 + +#define EVT_USER_PASSKEY_REQUEST 0x34 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) evt_user_passkey_request; +#define EVT_USER_PASSKEY_REQUEST_SIZE 6 + +#define EVT_REMOTE_OOB_DATA_REQUEST 0x35 +typedef struct { + bdaddr_t bdaddr; +} __attribute__ ((packed)) evt_remote_oob_data_request; +#define EVT_REMOTE_OOB_DATA_REQUEST_SIZE 6 + +#define EVT_SIMPLE_PAIRING_COMPLETE 0x36 +typedef struct { + uint8_t status; + bdaddr_t bdaddr; +} __attribute__ ((packed)) evt_simple_pairing_complete; +#define EVT_SIMPLE_PAIRING_COMPLETE_SIZE 7 + +#define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED 0x38 +typedef struct { + uint16_t handle; + uint16_t timeout; +} __attribute__ ((packed)) evt_link_supervision_timeout_changed; +#define EVT_LINK_SUPERVISION_TIMEOUT_CHANGED_SIZE 4 + +#define EVT_ENHANCED_FLUSH_COMPLETE 0x39 +typedef struct { + uint16_t handle; +} __attribute__ ((packed)) evt_enhanced_flush_complete; +#define EVT_ENHANCED_FLUSH_COMPLETE_SIZE 2 + +#define EVT_USER_PASSKEY_NOTIFY 0x3B +typedef struct { + bdaddr_t bdaddr; + uint32_t passkey; +} __attribute__ ((packed)) evt_user_passkey_notify; +#define EVT_USER_PASSKEY_NOTIFY_SIZE 10 + +#define EVT_KEYPRESS_NOTIFY 0x3C +typedef struct { + bdaddr_t bdaddr; + uint8_t type; +} __attribute__ ((packed)) evt_keypress_notify; +#define EVT_KEYPRESS_NOTIFY_SIZE 7 + +#define EVT_REMOTE_HOST_FEATURES_NOTIFY 0x3D +typedef struct { + bdaddr_t bdaddr; + uint8_t features[8]; +} __attribute__ ((packed)) evt_remote_host_features_notify; +#define EVT_REMOTE_HOST_FEATURES_NOTIFY_SIZE 14 + +#define EVT_LE_META_EVENT 0x3E +typedef struct { + uint8_t subevent; + uint8_t data[]; +} __attribute__ ((packed)) evt_le_meta_event; +#define EVT_LE_META_EVENT_SIZE 1 + +#define EVT_LE_CONN_COMPLETE 0x01 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t role; + uint8_t peer_bdaddr_type; + bdaddr_t peer_bdaddr; + uint16_t interval; + uint16_t latency; + uint16_t supervision_timeout; + uint8_t master_clock_accuracy; +} __attribute__ ((packed)) evt_le_connection_complete; +#define EVT_LE_CONN_COMPLETE_SIZE 18 + +#define EVT_LE_ADVERTISING_REPORT 0x02 +typedef struct { + uint8_t evt_type; + uint8_t bdaddr_type; + bdaddr_t bdaddr; + uint8_t length; + uint8_t data[]; +} __attribute__ ((packed)) le_advertising_info; +#define LE_ADVERTISING_INFO_SIZE 9 + +#define EVT_LE_CONN_UPDATE_COMPLETE 0x03 +typedef struct { + uint8_t status; + uint16_t handle; + uint16_t interval; + uint16_t latency; + uint16_t supervision_timeout; +} __attribute__ ((packed)) evt_le_connection_update_complete; +#define EVT_LE_CONN_UPDATE_COMPLETE_SIZE 9 + +#define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE 0x04 +typedef struct { + uint8_t status; + uint16_t handle; + uint8_t features[8]; +} __attribute__ ((packed)) evt_le_read_remote_used_features_complete; +#define EVT_LE_READ_REMOTE_USED_FEATURES_COMPLETE_SIZE 11 + +#define EVT_LE_LTK_REQUEST 0x05 +typedef struct { + uint16_t handle; + uint64_t random; + uint16_t diversifier; +} __attribute__ ((packed)) evt_le_long_term_key_request; +#define EVT_LE_LTK_REQUEST_SIZE 12 + +#define EVT_PHYSICAL_LINK_COMPLETE 0x40 +typedef struct { + uint8_t status; + uint8_t handle; +} __attribute__ ((packed)) evt_physical_link_complete; +#define EVT_PHYSICAL_LINK_COMPLETE_SIZE 2 + +#define EVT_CHANNEL_SELECTED 0x41 + +#define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE 0x42 +typedef struct { + uint8_t status; + uint8_t handle; + uint8_t reason; +} __attribute__ ((packed)) evt_disconn_physical_link_complete; +#define EVT_DISCONNECT_PHYSICAL_LINK_COMPLETE_SIZE 3 + +#define EVT_PHYSICAL_LINK_LOSS_EARLY_WARNING 0x43 +typedef struct { + uint8_t handle; + uint8_t reason; +} __attribute__ ((packed)) evt_physical_link_loss_warning; +#define EVT_PHYSICAL_LINK_LOSS_WARNING_SIZE 2 + +#define EVT_PHYSICAL_LINK_RECOVERY 0x44 +typedef struct { + uint8_t handle; +} __attribute__ ((packed)) evt_physical_link_recovery; +#define EVT_PHYSICAL_LINK_RECOVERY_SIZE 1 + +#define EVT_LOGICAL_LINK_COMPLETE 0x45 +typedef struct { + uint8_t status; + uint16_t log_handle; + uint8_t handle; + uint8_t tx_flow_id; +} __attribute__ ((packed)) evt_logical_link_complete; +#define EVT_LOGICAL_LINK_COMPLETE_SIZE 5 + +#define EVT_DISCONNECT_LOGICAL_LINK_COMPLETE 0x46 + +#define EVT_FLOW_SPEC_MODIFY_COMPLETE 0x47 +typedef struct { + uint8_t status; + uint16_t handle; +} __attribute__ ((packed)) evt_flow_spec_modify_complete; +#define EVT_FLOW_SPEC_MODIFY_COMPLETE_SIZE 3 + +#define EVT_NUMBER_COMPLETED_BLOCKS 0x48 +typedef struct { + uint16_t handle; + uint16_t num_cmplt_pkts; + uint16_t num_cmplt_blks; +} __attribute__ ((packed)) cmplt_handle; +typedef struct { + uint16_t total_num_blocks; + uint8_t num_handles; + cmplt_handle handles[]; +} __attribute__ ((packed)) evt_num_completed_blocks; + +#define EVT_AMP_STATUS_CHANGE 0x4D +typedef struct { + uint8_t status; + uint8_t amp_status; +} __attribute__ ((packed)) evt_amp_status_change; +#define EVT_AMP_STATUS_CHANGE_SIZE 2 + +#define EVT_TESTING 0xFE + +#define EVT_VENDOR 0xFF + +/* Internal events generated by BlueZ stack */ +#define EVT_STACK_INTERNAL 0xFD +typedef struct { + uint16_t type; + uint8_t data[]; +} __attribute__ ((packed)) evt_stack_internal; +#define EVT_STACK_INTERNAL_SIZE 2 + +#define EVT_SI_DEVICE 0x01 +typedef struct { + uint16_t event; + uint16_t dev_id; +} __attribute__ ((packed)) evt_si_device; +#define EVT_SI_DEVICE_SIZE 4 + +/* -------- HCI Packet structures -------- */ +#define HCI_TYPE_LEN 1 + +typedef struct { + uint16_t opcode; /* OCF & OGF */ + uint8_t plen; +} __attribute__ ((packed)) hci_command_hdr; +#define HCI_COMMAND_HDR_SIZE 3 + +typedef struct { + uint8_t evt; + uint8_t plen; +} __attribute__ ((packed)) hci_event_hdr; +#define HCI_EVENT_HDR_SIZE 2 + +typedef struct { + uint16_t handle; /* Handle & Flags(PB, BC) */ + uint16_t dlen; +} __attribute__ ((packed)) hci_acl_hdr; +#define HCI_ACL_HDR_SIZE 4 + +typedef struct { + uint16_t handle; + uint8_t dlen; +} __attribute__ ((packed)) hci_sco_hdr; +#define HCI_SCO_HDR_SIZE 3 + +typedef struct { + uint16_t device; + uint16_t type; + uint16_t plen; +} __attribute__ ((packed)) hci_msg_hdr; +#define HCI_MSG_HDR_SIZE 6 + +typedef struct { + uint16_t handle; + uint16_t dlen; +} __attribute__ ((packed)) hci_iso_hdr; +#define HCI_ISO_HDR_SIZE 4 + +/* Command opcode pack/unpack */ +#define cmd_opcode_pack(ogf, ocf) (uint16_t)((ocf & 0x03ff)|(ogf << 10)) +#define cmd_opcode_ogf(op) (op >> 10) +#define cmd_opcode_ocf(op) (op & 0x03ff) + +/* ACL handle and flags pack/unpack */ +#define acl_handle_pack(h, f) (uint16_t)((h & 0x0fff)|(f << 12)) +#define acl_handle(h) (h & 0x0fff) +#define acl_flags(h) (h >> 12) + +/* ISO handle and flags pack/unpack */ +#define iso_flags_pb(f) (f & 0x0003) +#define iso_flags_ts(f) ((f >> 2) & 0x0001) +#define iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2)) + +#endif /* _NO_HCI_DEFS */ + +/* HCI Socket options */ +#define HCI_DATA_DIR 1 +#define HCI_FILTER 2 +#define HCI_TIME_STAMP 3 + +/* HCI CMSG flags */ +#define HCI_CMSG_DIR 0x0001 +#define HCI_CMSG_TSTAMP 0x0002 + +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; +#define HCI_DEV_NONE 0xffff + +#define HCI_CHANNEL_RAW 0 +#define HCI_CHANNEL_USER 1 +#define HCI_CHANNEL_MONITOR 2 +#define HCI_CHANNEL_CONTROL 3 +#define HCI_CHANNEL_LOGGING 4 + +struct hci_filter { + uint32_t type_mask; + uint32_t event_mask[2]; + uint16_t opcode; +}; + +#define HCI_FLT_TYPE_BITS 31 +#define HCI_FLT_EVENT_BITS 63 +#define HCI_FLT_OGF_BITS 63 +#define HCI_FLT_OCF_BITS 127 + +/* Ioctl requests structures */ +struct hci_dev_stats { + uint32_t err_rx; + uint32_t err_tx; + uint32_t cmd_tx; + uint32_t evt_rx; + uint32_t acl_tx; + uint32_t acl_rx; + uint32_t sco_tx; + uint32_t sco_rx; + uint32_t byte_rx; + uint32_t byte_tx; +}; + +struct hci_dev_info { + uint16_t dev_id; + char name[8]; + + bdaddr_t bdaddr; + + uint32_t flags; + uint8_t type; + + uint8_t features[8]; + + uint32_t pkt_type; + uint32_t link_policy; + uint32_t link_mode; + + uint16_t acl_mtu; + uint16_t acl_pkts; + uint16_t sco_mtu; + uint16_t sco_pkts; + + struct hci_dev_stats stat; +}; + +struct hci_conn_info { + uint16_t handle; + bdaddr_t bdaddr; + uint8_t type; + uint8_t out; + uint16_t state; + uint32_t link_mode; +}; + +struct hci_dev_req { + uint16_t dev_id; + uint32_t dev_opt; +}; + +struct hci_dev_list_req { + uint16_t dev_num; + struct hci_dev_req dev_req[]; /* hci_dev_req structures */ +}; + +struct hci_conn_list_req { + uint16_t dev_id; + uint16_t conn_num; + struct hci_conn_info conn_info[]; +}; + +struct hci_conn_info_req { + bdaddr_t bdaddr; + uint8_t type; + struct hci_conn_info conn_info[]; +}; + +struct hci_auth_info_req { + bdaddr_t bdaddr; + uint8_t type; +}; + +struct hci_inquiry_req { + uint16_t dev_id; + uint16_t flags; + uint8_t lap[3]; + uint8_t length; + uint8_t num_rsp; +}; +#define IREQ_CACHE_FLUSH 0x0001 + +#ifdef __cplusplus +} +#endif + +#endif /* __HCI_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/hci_lib.h b/Sources/CBluetoothLinuxABI/include/bluetooth/hci_lib.h new file mode 100644 index 0000000..eeb5141 --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/hci_lib.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + */ + +#ifndef __HCI_LIB_H +#define __HCI_LIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include +#include + +struct hci_request { + uint16_t ogf; + uint16_t ocf; + int event; + void *cparam; + int clen; + void *rparam; + int rlen; +}; + +struct hci_version { + uint16_t manufacturer; + uint8_t hci_ver; + uint16_t hci_rev; + uint8_t lmp_ver; + uint16_t lmp_subver; +}; + +int hci_open_dev(int dev_id); +int hci_close_dev(int dd); +int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param); +int hci_send_req(int dd, struct hci_request *req, int timeout); + +int hci_create_connection(int dd, const bdaddr_t *bdaddr, uint16_t ptype, uint16_t clkoffset, uint8_t rswitch, uint16_t *handle, int to); +int hci_disconnect(int dd, uint16_t handle, uint8_t reason, int to); + +int hci_inquiry(int dev_id, int len, int num_rsp, const uint8_t *lap, inquiry_info **ii, long flags); +int hci_devinfo(int dev_id, struct hci_dev_info *di); +int hci_devba(int dev_id, bdaddr_t *bdaddr); +int hci_devid(const char *str); + +int hci_read_local_name(int dd, int len, char *name, int to); +int hci_write_local_name(int dd, const char *name, int to); +int hci_read_remote_name(int dd, const bdaddr_t *bdaddr, int len, char *name, int to); +int hci_read_remote_name_with_clock_offset(int dd, const bdaddr_t *bdaddr, uint8_t pscan_rep_mode, uint16_t clkoffset, int len, char *name, int to); +int hci_read_remote_name_cancel(int dd, const bdaddr_t *bdaddr, int to); +int hci_read_remote_version(int dd, uint16_t handle, struct hci_version *ver, int to); +int hci_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to); +int hci_read_remote_ext_features(int dd, uint16_t handle, uint8_t page, uint8_t *max_page, uint8_t *features, int to); +int hci_read_clock_offset(int dd, uint16_t handle, uint16_t *clkoffset, int to); +int hci_read_local_version(int dd, struct hci_version *ver, int to); +int hci_read_local_commands(int dd, uint8_t *commands, int to); +int hci_read_local_features(int dd, uint8_t *features, int to); +int hci_read_local_ext_features(int dd, uint8_t page, uint8_t *max_page, uint8_t *features, int to); +int hci_read_bd_addr(int dd, bdaddr_t *bdaddr, int to); +int hci_read_class_of_dev(int dd, uint8_t *cls, int to); +int hci_write_class_of_dev(int dd, uint32_t cls, int to); +int hci_read_voice_setting(int dd, uint16_t *vs, int to); +int hci_write_voice_setting(int dd, uint16_t vs, int to); +int hci_read_current_iac_lap(int dd, uint8_t *num_iac, uint8_t *lap, int to); +int hci_write_current_iac_lap(int dd, uint8_t num_iac, uint8_t *lap, int to); +int hci_read_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to); +int hci_write_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t *key, int to); +int hci_delete_stored_link_key(int dd, bdaddr_t *bdaddr, uint8_t all, int to); +int hci_authenticate_link(int dd, uint16_t handle, int to); +int hci_encrypt_link(int dd, uint16_t handle, uint8_t encrypt, int to); +int hci_change_link_key(int dd, uint16_t handle, int to); +int hci_switch_role(int dd, bdaddr_t *bdaddr, uint8_t role, int to); +int hci_park_mode(int dd, uint16_t handle, uint16_t max_interval, uint16_t min_interval, int to); +int hci_exit_park_mode(int dd, uint16_t handle, int to); +int hci_read_inquiry_scan_type(int dd, uint8_t *type, int to); +int hci_write_inquiry_scan_type(int dd, uint8_t type, int to); +int hci_read_inquiry_mode(int dd, uint8_t *mode, int to); +int hci_write_inquiry_mode(int dd, uint8_t mode, int to); +int hci_read_afh_mode(int dd, uint8_t *mode, int to); +int hci_write_afh_mode(int dd, uint8_t mode, int to); +int hci_read_ext_inquiry_response(int dd, uint8_t *fec, uint8_t *data, int to); +int hci_write_ext_inquiry_response(int dd, uint8_t fec, uint8_t *data, int to); +int hci_read_simple_pairing_mode(int dd, uint8_t *mode, int to); +int hci_write_simple_pairing_mode(int dd, uint8_t mode, int to); +int hci_read_local_oob_data(int dd, uint8_t *hash, uint8_t *randomizer, int to); +int hci_read_inq_response_tx_power_level(int dd, int8_t *level, int to); +int hci_read_inquiry_transmit_power_level(int dd, int8_t *level, int to); +int hci_write_inquiry_transmit_power_level(int dd, int8_t level, int to); +int hci_read_transmit_power_level(int dd, uint16_t handle, uint8_t type, int8_t *level, int to); +int hci_read_link_policy(int dd, uint16_t handle, uint16_t *policy, int to); +int hci_write_link_policy(int dd, uint16_t handle, uint16_t policy, int to); +int hci_read_link_supervision_timeout(int dd, uint16_t handle, uint16_t *timeout, int to); +int hci_write_link_supervision_timeout(int dd, uint16_t handle, uint16_t timeout, int to); +int hci_set_afh_classification(int dd, uint8_t *map, int to); +int hci_read_link_quality(int dd, uint16_t handle, uint8_t *link_quality, int to); +int hci_read_rssi(int dd, uint16_t handle, int8_t *rssi, int to); +int hci_read_afh_map(int dd, uint16_t handle, uint8_t *mode, uint8_t *map, int to); +int hci_read_clock(int dd, uint16_t handle, uint8_t which, uint32_t *clock, uint16_t *accuracy, int to); + +int hci_le_set_scan_enable(int dev_id, uint8_t enable, uint8_t filter_dup, int to); +int hci_le_set_scan_parameters(int dev_id, uint8_t type, uint16_t interval, + uint16_t window, uint8_t own_type, + uint8_t filter, int to); +int hci_le_set_advertise_enable(int dev_id, uint8_t enable, int to); +int hci_le_create_conn(int dd, uint16_t interval, uint16_t window, + uint8_t initiator_filter, uint8_t peer_bdaddr_type, + bdaddr_t peer_bdaddr, uint8_t own_bdaddr_type, + uint16_t min_interval, uint16_t max_interval, + uint16_t latency, uint16_t supervision_timeout, + uint16_t min_ce_length, uint16_t max_ce_length, + uint16_t *handle, int to); +int hci_le_conn_update(int dd, uint16_t handle, uint16_t min_interval, + uint16_t max_interval, uint16_t latency, + uint16_t supervision_timeout, int to); +int hci_le_add_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to); +int hci_le_rm_white_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to); +int hci_le_read_white_list_size(int dd, uint8_t *size, int to); +int hci_le_clear_white_list(int dd, int to); +int hci_le_add_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, + uint8_t *peer_irk, uint8_t *local_irk, int to); +int hci_le_rm_resolving_list(int dd, const bdaddr_t *bdaddr, uint8_t type, int to); +int hci_le_clear_resolving_list(int dd, int to); +int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to); +int hci_le_set_address_resolution_enable(int dev_id, uint8_t enable, int to); +int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to); + +int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg); +int hci_get_route(bdaddr_t *bdaddr); + +const char *hci_bustostr(int bus); +char *hci_typetostr(int type); +const char *hci_dtypetostr(int type); +char *hci_dflagstostr(uint32_t flags); +char *hci_ptypetostr(unsigned int ptype); +int hci_strtoptype(char *str, unsigned int *val); +char *hci_scoptypetostr(unsigned int ptype); +int hci_strtoscoptype(char *str, unsigned int *val); +char *hci_lptostr(unsigned int ptype); +int hci_strtolp(char *str, unsigned int *val); +char *hci_lmtostr(unsigned int ptype); +int hci_strtolm(char *str, unsigned int *val); + +char *hci_cmdtostr(unsigned int cmd); +char *hci_commandstostr(const uint8_t *commands, const char *pref, int width); + +char *hci_vertostr(unsigned int ver); +int hci_strtover(char *str, unsigned int *ver); +char *lmp_vertostr(unsigned int ver); +int lmp_strtover(char *str, unsigned int *ver); +char *pal_vertostr(unsigned int ver); +int pal_strtover(char *str, unsigned int *ver); + +char *lmp_featurestostr(uint8_t *features, char *pref, int width); + +static inline void hci_set_bit(int nr, void *addr) +{ + *((uint32_t *) addr + (nr >> 5)) |= (1 << (nr & 31)); +} + +static inline void hci_clear_bit(int nr, void *addr) +{ + *((uint32_t *) addr + (nr >> 5)) &= ~(1 << (nr & 31)); +} + +static inline int hci_test_bit(int nr, void *addr) +{ + return *((uint32_t *) addr + (nr >> 5)) & (1 << (nr & 31)); +} + +/* HCI filter tools */ +static inline void hci_filter_clear(struct hci_filter *f) +{ + memset(f, 0, sizeof(*f)); +} +static inline void hci_filter_set_ptype(int t, struct hci_filter *f) +{ + hci_set_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask); +} +static inline void hci_filter_clear_ptype(int t, struct hci_filter *f) +{ + hci_clear_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask); +} +static inline int hci_filter_test_ptype(int t, struct hci_filter *f) +{ + return hci_test_bit((t == HCI_VENDOR_PKT) ? 0 : (t & HCI_FLT_TYPE_BITS), &f->type_mask); +} +static inline void hci_filter_all_ptypes(struct hci_filter *f) +{ + memset((void *) &f->type_mask, 0xff, sizeof(f->type_mask)); +} +static inline void hci_filter_set_event(int e, struct hci_filter *f) +{ + hci_set_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask); +} +static inline void hci_filter_clear_event(int e, struct hci_filter *f) +{ + hci_clear_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask); +} +static inline int hci_filter_test_event(int e, struct hci_filter *f) +{ + return hci_test_bit((e & HCI_FLT_EVENT_BITS), &f->event_mask); +} +static inline void hci_filter_all_events(struct hci_filter *f) +{ + memset((void *) f->event_mask, 0xff, sizeof(f->event_mask)); +} +static inline void hci_filter_set_opcode(int opcode, struct hci_filter *f) +{ + f->opcode = opcode; +} +static inline void hci_filter_clear_opcode(struct hci_filter *f) +{ + f->opcode = 0; +} +static inline int hci_filter_test_opcode(int opcode, struct hci_filter *f) +{ + return (f->opcode == opcode); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __HCI_LIB_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/hidp.h b/Sources/CBluetoothLinuxABI/include/bluetooth/hidp.h new file mode 100644 index 0000000..da42a1b --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/hidp.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2003-2010 Marcel Holtmann + * + * + */ + +#ifndef __HIDP_H +#define __HIDP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* HIDP defaults */ +#define HIDP_MINIMUM_MTU 48 +#define HIDP_DEFAULT_MTU 48 + +/* HIDP ioctl defines */ +#define HIDPCONNADD _IOW('H', 200, int) +#define HIDPCONNDEL _IOW('H', 201, int) +#define HIDPGETCONNLIST _IOR('H', 210, int) +#define HIDPGETCONNINFO _IOR('H', 211, int) + +#define HIDP_VIRTUAL_CABLE_UNPLUG 0 +#define HIDP_BOOT_PROTOCOL_MODE 1 +#define HIDP_BLUETOOTH_VENDOR_ID 9 + +struct hidp_connadd_req { + int ctrl_sock; /* Connected control socket */ + int intr_sock; /* Connected interrupt socket */ + uint16_t parser; /* Parser version */ + uint16_t rd_size; /* Report descriptor size */ + uint8_t *rd_data; /* Report descriptor data */ + uint8_t country; + uint8_t subclass; + uint16_t vendor; + uint16_t product; + uint16_t version; + uint32_t flags; + uint32_t idle_to; + char name[128]; /* Device name */ +}; + +struct hidp_conndel_req { + bdaddr_t bdaddr; + uint32_t flags; +}; + +struct hidp_conninfo { + bdaddr_t bdaddr; + uint32_t flags; + uint16_t state; + uint16_t vendor; + uint16_t product; + uint16_t version; + char name[128]; +}; + +struct hidp_connlist_req { + uint32_t cnum; + struct hidp_conninfo *ci; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __HIDP_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/l2cap.h b/Sources/CBluetoothLinuxABI/include/bluetooth/l2cap.h new file mode 100644 index 0000000..62cc04b --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/l2cap.h @@ -0,0 +1,268 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2000-2001 Qualcomm Incorporated + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * Copyright (c) 2012 Code Aurora Forum. All rights reserved. + * + * + */ + +#ifndef __L2CAP_H +#define __L2CAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* L2CAP defaults */ +#define L2CAP_DEFAULT_MTU 672 +#define L2CAP_DEFAULT_FLUSH_TO 0xFFFF + +/* L2CAP socket address */ +struct sockaddr_l2 { + sa_family_t l2_family; + unsigned short l2_psm; + bdaddr_t l2_bdaddr; + unsigned short l2_cid; + uint8_t l2_bdaddr_type; +}; + +/* L2CAP socket options */ +#define L2CAP_OPTIONS 0x01 +struct l2cap_options { + uint16_t omtu; + uint16_t imtu; + uint16_t flush_to; + uint8_t mode; + uint8_t fcs; + uint8_t max_tx; + uint16_t txwin_size; +}; + +#define L2CAP_CONNINFO 0x02 +struct l2cap_conninfo { + uint16_t hci_handle; + uint8_t dev_class[3]; +}; + +#define L2CAP_LM 0x03 +#define L2CAP_LM_MASTER 0x0001 +#define L2CAP_LM_AUTH 0x0002 +#define L2CAP_LM_ENCRYPT 0x0004 +#define L2CAP_LM_TRUSTED 0x0008 +#define L2CAP_LM_RELIABLE 0x0010 +#define L2CAP_LM_SECURE 0x0020 + +/* L2CAP command codes */ +#define L2CAP_COMMAND_REJ 0x01 +#define L2CAP_CONN_REQ 0x02 +#define L2CAP_CONN_RSP 0x03 +#define L2CAP_CONF_REQ 0x04 +#define L2CAP_CONF_RSP 0x05 +#define L2CAP_DISCONN_REQ 0x06 +#define L2CAP_DISCONN_RSP 0x07 +#define L2CAP_ECHO_REQ 0x08 +#define L2CAP_ECHO_RSP 0x09 +#define L2CAP_INFO_REQ 0x0a +#define L2CAP_INFO_RSP 0x0b +#define L2CAP_CREATE_REQ 0x0c +#define L2CAP_CREATE_RSP 0x0d +#define L2CAP_MOVE_REQ 0x0e +#define L2CAP_MOVE_RSP 0x0f +#define L2CAP_MOVE_CFM 0x10 +#define L2CAP_MOVE_CFM_RSP 0x11 + +/* L2CAP extended feature mask */ +#define L2CAP_FEAT_FLOWCTL 0x00000001 +#define L2CAP_FEAT_RETRANS 0x00000002 +#define L2CAP_FEAT_BIDIR_QOS 0x00000004 +#define L2CAP_FEAT_ERTM 0x00000008 +#define L2CAP_FEAT_STREAMING 0x00000010 +#define L2CAP_FEAT_FCS 0x00000020 +#define L2CAP_FEAT_EXT_FLOW 0x00000040 +#define L2CAP_FEAT_FIXED_CHAN 0x00000080 +#define L2CAP_FEAT_EXT_WINDOW 0x00000100 +#define L2CAP_FEAT_UCD 0x00000200 + +/* L2CAP fixed channels */ +#define L2CAP_FC_L2CAP 0x02 +#define L2CAP_FC_CONNLESS 0x04 +#define L2CAP_FC_A2MP 0x08 + +/* L2CAP structures */ +typedef struct { + uint16_t len; + uint16_t cid; +} __attribute__ ((packed)) l2cap_hdr; +#define L2CAP_HDR_SIZE 4 + +typedef struct { + uint8_t code; + uint8_t ident; + uint16_t len; +} __attribute__ ((packed)) l2cap_cmd_hdr; +#define L2CAP_CMD_HDR_SIZE 4 + +typedef struct { + uint16_t reason; +} __attribute__ ((packed)) l2cap_cmd_rej; +#define L2CAP_CMD_REJ_SIZE 2 + +typedef struct { + uint16_t psm; + uint16_t scid; +} __attribute__ ((packed)) l2cap_conn_req; +#define L2CAP_CONN_REQ_SIZE 4 + +typedef struct { + uint16_t dcid; + uint16_t scid; + uint16_t result; + uint16_t status; +} __attribute__ ((packed)) l2cap_conn_rsp; +#define L2CAP_CONN_RSP_SIZE 8 + +/* connect result */ +#define L2CAP_CR_SUCCESS 0x0000 +#define L2CAP_CR_PEND 0x0001 +#define L2CAP_CR_BAD_PSM 0x0002 +#define L2CAP_CR_SEC_BLOCK 0x0003 +#define L2CAP_CR_NO_MEM 0x0004 + +/* connect status */ +#define L2CAP_CS_NO_INFO 0x0000 +#define L2CAP_CS_AUTHEN_PEND 0x0001 +#define L2CAP_CS_AUTHOR_PEND 0x0002 + +typedef struct { + uint16_t dcid; + uint16_t flags; + uint8_t data[0]; +} __attribute__ ((packed)) l2cap_conf_req; +#define L2CAP_CONF_REQ_SIZE 4 + +typedef struct { + uint16_t scid; + uint16_t flags; + uint16_t result; + uint8_t data[0]; +} __attribute__ ((packed)) l2cap_conf_rsp; +#define L2CAP_CONF_RSP_SIZE 6 + +#define L2CAP_CONF_SUCCESS 0x0000 +#define L2CAP_CONF_UNACCEPT 0x0001 +#define L2CAP_CONF_REJECT 0x0002 +#define L2CAP_CONF_UNKNOWN 0x0003 +#define L2CAP_CONF_PENDING 0x0004 +#define L2CAP_CONF_EFS_REJECT 0x0005 + +typedef struct { + uint8_t type; + uint8_t len; + uint8_t val[0]; +} __attribute__ ((packed)) l2cap_conf_opt; +#define L2CAP_CONF_OPT_SIZE 2 + +#define L2CAP_CONF_MTU 0x01 +#define L2CAP_CONF_FLUSH_TO 0x02 +#define L2CAP_CONF_QOS 0x03 +#define L2CAP_CONF_RFC 0x04 +#define L2CAP_CONF_FCS 0x05 +#define L2CAP_CONF_EFS 0x06 +#define L2CAP_CONF_EWS 0x07 + +#define L2CAP_CONF_MAX_SIZE 22 + +#define L2CAP_MODE_BASIC 0x00 +#define L2CAP_MODE_RETRANS 0x01 +#define L2CAP_MODE_FLOWCTL 0x02 +#define L2CAP_MODE_ERTM 0x03 +#define L2CAP_MODE_STREAMING 0x04 +#define L2CAP_MODE_LE_FLOWCTL 0x80 +#define L2CAP_MODE_ECRED 0x81 + +#define L2CAP_SERVTYPE_NOTRAFFIC 0x00 +#define L2CAP_SERVTYPE_BESTEFFORT 0x01 +#define L2CAP_SERVTYPE_GUARANTEED 0x02 + +typedef struct { + uint16_t dcid; + uint16_t scid; +} __attribute__ ((packed)) l2cap_disconn_req; +#define L2CAP_DISCONN_REQ_SIZE 4 + +typedef struct { + uint16_t dcid; + uint16_t scid; +} __attribute__ ((packed)) l2cap_disconn_rsp; +#define L2CAP_DISCONN_RSP_SIZE 4 + +typedef struct { + uint16_t type; +} __attribute__ ((packed)) l2cap_info_req; +#define L2CAP_INFO_REQ_SIZE 2 + +typedef struct { + uint16_t type; + uint16_t result; + uint8_t data[0]; +} __attribute__ ((packed)) l2cap_info_rsp; +#define L2CAP_INFO_RSP_SIZE 4 + +/* info type */ +#define L2CAP_IT_CL_MTU 0x0001 +#define L2CAP_IT_FEAT_MASK 0x0002 + +/* info result */ +#define L2CAP_IR_SUCCESS 0x0000 +#define L2CAP_IR_NOTSUPP 0x0001 + +typedef struct { + uint16_t psm; + uint16_t scid; + uint8_t id; +} __attribute__ ((packed)) l2cap_create_req; +#define L2CAP_CREATE_REQ_SIZE 5 + +typedef struct { + uint16_t dcid; + uint16_t scid; + uint16_t result; + uint16_t status; +} __attribute__ ((packed)) l2cap_create_rsp; +#define L2CAP_CREATE_RSP_SIZE 8 + +typedef struct { + uint16_t icid; + uint8_t id; +} __attribute__ ((packed)) l2cap_move_req; +#define L2CAP_MOVE_REQ_SIZE 3 + +typedef struct { + uint16_t icid; + uint16_t result; +} __attribute__ ((packed)) l2cap_move_rsp; +#define L2CAP_MOVE_RSP_SIZE 4 + +typedef struct { + uint16_t icid; + uint16_t result; +} __attribute__ ((packed)) l2cap_move_cfm; +#define L2CAP_MOVE_CFM_SIZE 4 + +typedef struct { + uint16_t icid; +} __attribute__ ((packed)) l2cap_move_cfm_rsp; +#define L2CAP_MOVE_CFM_RSP_SIZE 2 + +#ifdef __cplusplus +} +#endif + +#endif /* __L2CAP_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/rfcomm.h b/Sources/CBluetoothLinuxABI/include/bluetooth/rfcomm.h new file mode 100644 index 0000000..0347ddc --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/rfcomm.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + */ + +#ifndef __RFCOMM_H +#define __RFCOMM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* RFCOMM defaults */ +#define RFCOMM_DEFAULT_MTU 127 + +#define RFCOMM_PSM 3 + +/* RFCOMM socket address */ +struct sockaddr_rc { + sa_family_t rc_family; + bdaddr_t rc_bdaddr; + uint8_t rc_channel; +}; + +/* RFCOMM socket options */ +#define RFCOMM_CONNINFO 0x02 +struct rfcomm_conninfo { + uint16_t hci_handle; + uint8_t dev_class[3]; +}; + +#define RFCOMM_LM 0x03 +#define RFCOMM_LM_MASTER 0x0001 +#define RFCOMM_LM_AUTH 0x0002 +#define RFCOMM_LM_ENCRYPT 0x0004 +#define RFCOMM_LM_TRUSTED 0x0008 +#define RFCOMM_LM_RELIABLE 0x0010 +#define RFCOMM_LM_SECURE 0x0020 + +/* RFCOMM TTY support */ +#define RFCOMM_MAX_DEV 256 + +#define RFCOMMCREATEDEV _IOW('R', 200, int) +#define RFCOMMRELEASEDEV _IOW('R', 201, int) +#define RFCOMMGETDEVLIST _IOR('R', 210, int) +#define RFCOMMGETDEVINFO _IOR('R', 211, int) + +struct rfcomm_dev_req { + int16_t dev_id; + uint32_t flags; + bdaddr_t src; + bdaddr_t dst; + uint8_t channel; +}; +#define RFCOMM_REUSE_DLC 0 +#define RFCOMM_RELEASE_ONHUP 1 +#define RFCOMM_HANGUP_NOW 2 +#define RFCOMM_TTY_ATTACHED 3 + +struct rfcomm_dev_info { + int16_t id; + uint32_t flags; + uint16_t state; + bdaddr_t src; + bdaddr_t dst; + uint8_t channel; +}; + +struct rfcomm_dev_list_req { + uint16_t dev_num; + struct rfcomm_dev_info dev_info[0]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __RFCOMM_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/sco.h b/Sources/CBluetoothLinuxABI/include/bluetooth/sco.h new file mode 100644 index 0000000..307d81f --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/sco.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * + * + */ + +#ifndef __SCO_H +#define __SCO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* SCO defaults */ +#define SCO_DEFAULT_MTU 500 +#define SCO_DEFAULT_FLUSH_TO 0xFFFF + +#define SCO_CONN_TIMEOUT (HZ * 40) +#define SCO_DISCONN_TIMEOUT (HZ * 2) +#define SCO_CONN_IDLE_TIMEOUT (HZ * 60) + +/* SCO socket address */ +struct sockaddr_sco { + sa_family_t sco_family; + bdaddr_t sco_bdaddr; +}; + +/* set/get sockopt defines */ +#define SCO_OPTIONS 0x01 +struct sco_options { + uint16_t mtu; +}; + +#define SCO_CONNINFO 0x02 +struct sco_conninfo { + uint16_t hci_handle; + uint8_t dev_class[3]; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __SCO_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/sdp.h b/Sources/CBluetoothLinuxABI/include/bluetooth/sdp.h new file mode 100644 index 0000000..6f05d43 --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/sdp.h @@ -0,0 +1,529 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2001-2002 Nokia Corporation + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * Copyright (C) 2002-2003 Stephen Crane + * + * + */ + +#ifndef __SDP_H +#define __SDP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define SDP_UNIX_PATH "/var/run/sdp" +#define SDP_RESPONSE_TIMEOUT 20 +#define SDP_REQ_BUFFER_SIZE 2048 +#define SDP_RSP_BUFFER_SIZE 65535 +#define SDP_PDU_CHUNK_SIZE 1024 + +/* + * All definitions are based on Bluetooth Assigned Numbers + * of the Bluetooth Specification + */ +#define SDP_PSM 0x0001 + +/* + * Protocol UUIDs + */ +#define SDP_UUID 0x0001 +#define UDP_UUID 0x0002 +#define RFCOMM_UUID 0x0003 +#define TCP_UUID 0x0004 +#define TCS_BIN_UUID 0x0005 +#define TCS_AT_UUID 0x0006 +#define ATT_UUID 0x0007 +#define OBEX_UUID 0x0008 +#define IP_UUID 0x0009 +#define FTP_UUID 0x000a +#define HTTP_UUID 0x000c +#define WSP_UUID 0x000e +#define BNEP_UUID 0x000f +#define UPNP_UUID 0x0010 +#define HIDP_UUID 0x0011 +#define HCRP_CTRL_UUID 0x0012 +#define HCRP_DATA_UUID 0x0014 +#define HCRP_NOTE_UUID 0x0016 +#define AVCTP_UUID 0x0017 +#define AVDTP_UUID 0x0019 +#define CMTP_UUID 0x001b +#define UDI_UUID 0x001d +#define MCAP_CTRL_UUID 0x001e +#define MCAP_DATA_UUID 0x001f +#define L2CAP_UUID 0x0100 + +/* + * Service class identifiers of standard services and service groups + */ +#define SDP_SERVER_SVCLASS_ID 0x1000 +#define BROWSE_GRP_DESC_SVCLASS_ID 0x1001 +#define PUBLIC_BROWSE_GROUP 0x1002 +#define SERIAL_PORT_SVCLASS_ID 0x1101 +#define LAN_ACCESS_SVCLASS_ID 0x1102 +#define DIALUP_NET_SVCLASS_ID 0x1103 +#define IRMC_SYNC_SVCLASS_ID 0x1104 +#define OBEX_OBJPUSH_SVCLASS_ID 0x1105 +#define OBEX_FILETRANS_SVCLASS_ID 0x1106 +#define IRMC_SYNC_CMD_SVCLASS_ID 0x1107 +#define HEADSET_SVCLASS_ID 0x1108 +#define CORDLESS_TELEPHONY_SVCLASS_ID 0x1109 +#define AUDIO_SOURCE_SVCLASS_ID 0x110a +#define AUDIO_SINK_SVCLASS_ID 0x110b +#define AV_REMOTE_TARGET_SVCLASS_ID 0x110c +#define ADVANCED_AUDIO_SVCLASS_ID 0x110d +#define AV_REMOTE_SVCLASS_ID 0x110e +#define AV_REMOTE_CONTROLLER_SVCLASS_ID 0x110f +#define INTERCOM_SVCLASS_ID 0x1110 +#define FAX_SVCLASS_ID 0x1111 +#define HEADSET_AGW_SVCLASS_ID 0x1112 +#define WAP_SVCLASS_ID 0x1113 +#define WAP_CLIENT_SVCLASS_ID 0x1114 +#define PANU_SVCLASS_ID 0x1115 +#define NAP_SVCLASS_ID 0x1116 +#define GN_SVCLASS_ID 0x1117 +#define DIRECT_PRINTING_SVCLASS_ID 0x1118 +#define REFERENCE_PRINTING_SVCLASS_ID 0x1119 +#define IMAGING_SVCLASS_ID 0x111a +#define IMAGING_RESPONDER_SVCLASS_ID 0x111b +#define IMAGING_ARCHIVE_SVCLASS_ID 0x111c +#define IMAGING_REFOBJS_SVCLASS_ID 0x111d +#define HANDSFREE_SVCLASS_ID 0x111e +#define HANDSFREE_AGW_SVCLASS_ID 0x111f +#define DIRECT_PRT_REFOBJS_SVCLASS_ID 0x1120 +#define REFLECTED_UI_SVCLASS_ID 0x1121 +#define BASIC_PRINTING_SVCLASS_ID 0x1122 +#define PRINTING_STATUS_SVCLASS_ID 0x1123 +#define HID_SVCLASS_ID 0x1124 +#define HCR_SVCLASS_ID 0x1125 +#define HCR_PRINT_SVCLASS_ID 0x1126 +#define HCR_SCAN_SVCLASS_ID 0x1127 +#define CIP_SVCLASS_ID 0x1128 +#define VIDEO_CONF_GW_SVCLASS_ID 0x1129 +#define UDI_MT_SVCLASS_ID 0x112a +#define UDI_TA_SVCLASS_ID 0x112b +#define AV_SVCLASS_ID 0x112c +#define SAP_SVCLASS_ID 0x112d +#define PBAP_PCE_SVCLASS_ID 0x112e +#define PBAP_PSE_SVCLASS_ID 0x112f +#define PBAP_SVCLASS_ID 0x1130 +#define MAP_MSE_SVCLASS_ID 0x1132 +#define MAP_MCE_SVCLASS_ID 0x1133 +#define MAP_SVCLASS_ID 0x1134 +#define GNSS_SVCLASS_ID 0x1135 +#define GNSS_SERVER_SVCLASS_ID 0x1136 +#define MPS_SC_SVCLASS_ID 0x113A +#define MPS_SVCLASS_ID 0x113B +#define PNP_INFO_SVCLASS_ID 0x1200 +#define GENERIC_NETWORKING_SVCLASS_ID 0x1201 +#define GENERIC_FILETRANS_SVCLASS_ID 0x1202 +#define GENERIC_AUDIO_SVCLASS_ID 0x1203 +#define GENERIC_TELEPHONY_SVCLASS_ID 0x1204 +#define UPNP_SVCLASS_ID 0x1205 +#define UPNP_IP_SVCLASS_ID 0x1206 +#define UPNP_PAN_SVCLASS_ID 0x1300 +#define UPNP_LAP_SVCLASS_ID 0x1301 +#define UPNP_L2CAP_SVCLASS_ID 0x1302 +#define VIDEO_SOURCE_SVCLASS_ID 0x1303 +#define VIDEO_SINK_SVCLASS_ID 0x1304 +#define VIDEO_DISTRIBUTION_SVCLASS_ID 0x1305 +#define HDP_SVCLASS_ID 0x1400 +#define HDP_SOURCE_SVCLASS_ID 0x1401 +#define HDP_SINK_SVCLASS_ID 0x1402 +#define GENERIC_ACCESS_SVCLASS_ID 0x1800 +#define GENERIC_ATTRIB_SVCLASS_ID 0x1801 +#define APPLE_AGENT_SVCLASS_ID 0x2112 + +/* + * Standard profile descriptor identifiers; note these + * may be identical to some of the service classes defined above + */ +#define SDP_SERVER_PROFILE_ID SDP_SERVER_SVCLASS_ID +#define BROWSE_GRP_DESC_PROFILE_ID BROWSE_GRP_DESC_SVCLASS_ID +#define SERIAL_PORT_PROFILE_ID SERIAL_PORT_SVCLASS_ID +#define LAN_ACCESS_PROFILE_ID LAN_ACCESS_SVCLASS_ID +#define DIALUP_NET_PROFILE_ID DIALUP_NET_SVCLASS_ID +#define IRMC_SYNC_PROFILE_ID IRMC_SYNC_SVCLASS_ID +#define OBEX_OBJPUSH_PROFILE_ID OBEX_OBJPUSH_SVCLASS_ID +#define OBEX_FILETRANS_PROFILE_ID OBEX_FILETRANS_SVCLASS_ID +#define IRMC_SYNC_CMD_PROFILE_ID IRMC_SYNC_CMD_SVCLASS_ID +#define HEADSET_PROFILE_ID HEADSET_SVCLASS_ID +#define CORDLESS_TELEPHONY_PROFILE_ID CORDLESS_TELEPHONY_SVCLASS_ID +#define AUDIO_SOURCE_PROFILE_ID AUDIO_SOURCE_SVCLASS_ID +#define AUDIO_SINK_PROFILE_ID AUDIO_SINK_SVCLASS_ID +#define AV_REMOTE_TARGET_PROFILE_ID AV_REMOTE_TARGET_SVCLASS_ID +#define ADVANCED_AUDIO_PROFILE_ID ADVANCED_AUDIO_SVCLASS_ID +#define AV_REMOTE_PROFILE_ID AV_REMOTE_SVCLASS_ID +#define INTERCOM_PROFILE_ID INTERCOM_SVCLASS_ID +#define FAX_PROFILE_ID FAX_SVCLASS_ID +#define HEADSET_AGW_PROFILE_ID HEADSET_AGW_SVCLASS_ID +#define WAP_PROFILE_ID WAP_SVCLASS_ID +#define WAP_CLIENT_PROFILE_ID WAP_CLIENT_SVCLASS_ID +#define PANU_PROFILE_ID PANU_SVCLASS_ID +#define NAP_PROFILE_ID NAP_SVCLASS_ID +#define GN_PROFILE_ID GN_SVCLASS_ID +#define DIRECT_PRINTING_PROFILE_ID DIRECT_PRINTING_SVCLASS_ID +#define REFERENCE_PRINTING_PROFILE_ID REFERENCE_PRINTING_SVCLASS_ID +#define IMAGING_PROFILE_ID IMAGING_SVCLASS_ID +#define IMAGING_RESPONDER_PROFILE_ID IMAGING_RESPONDER_SVCLASS_ID +#define IMAGING_ARCHIVE_PROFILE_ID IMAGING_ARCHIVE_SVCLASS_ID +#define IMAGING_REFOBJS_PROFILE_ID IMAGING_REFOBJS_SVCLASS_ID +#define HANDSFREE_PROFILE_ID HANDSFREE_SVCLASS_ID +#define HANDSFREE_AGW_PROFILE_ID HANDSFREE_AGW_SVCLASS_ID +#define DIRECT_PRT_REFOBJS_PROFILE_ID DIRECT_PRT_REFOBJS_SVCLASS_ID +#define REFLECTED_UI_PROFILE_ID REFLECTED_UI_SVCLASS_ID +#define BASIC_PRINTING_PROFILE_ID BASIC_PRINTING_SVCLASS_ID +#define PRINTING_STATUS_PROFILE_ID PRINTING_STATUS_SVCLASS_ID +#define HID_PROFILE_ID HID_SVCLASS_ID +#define HCR_PROFILE_ID HCR_SCAN_SVCLASS_ID +#define HCR_PRINT_PROFILE_ID HCR_PRINT_SVCLASS_ID +#define HCR_SCAN_PROFILE_ID HCR_SCAN_SVCLASS_ID +#define CIP_PROFILE_ID CIP_SVCLASS_ID +#define VIDEO_CONF_GW_PROFILE_ID VIDEO_CONF_GW_SVCLASS_ID +#define UDI_MT_PROFILE_ID UDI_MT_SVCLASS_ID +#define UDI_TA_PROFILE_ID UDI_TA_SVCLASS_ID +#define AV_PROFILE_ID AV_SVCLASS_ID +#define SAP_PROFILE_ID SAP_SVCLASS_ID +#define PBAP_PCE_PROFILE_ID PBAP_PCE_SVCLASS_ID +#define PBAP_PSE_PROFILE_ID PBAP_PSE_SVCLASS_ID +#define PBAP_PROFILE_ID PBAP_SVCLASS_ID +#define MAP_PROFILE_ID MAP_SVCLASS_ID +#define PNP_INFO_PROFILE_ID PNP_INFO_SVCLASS_ID +#define GENERIC_NETWORKING_PROFILE_ID GENERIC_NETWORKING_SVCLASS_ID +#define GENERIC_FILETRANS_PROFILE_ID GENERIC_FILETRANS_SVCLASS_ID +#define GENERIC_AUDIO_PROFILE_ID GENERIC_AUDIO_SVCLASS_ID +#define GENERIC_TELEPHONY_PROFILE_ID GENERIC_TELEPHONY_SVCLASS_ID +#define UPNP_PROFILE_ID UPNP_SVCLASS_ID +#define UPNP_IP_PROFILE_ID UPNP_IP_SVCLASS_ID +#define UPNP_PAN_PROFILE_ID UPNP_PAN_SVCLASS_ID +#define UPNP_LAP_PROFILE_ID UPNP_LAP_SVCLASS_ID +#define UPNP_L2CAP_PROFILE_ID UPNP_L2CAP_SVCLASS_ID +#define VIDEO_SOURCE_PROFILE_ID VIDEO_SOURCE_SVCLASS_ID +#define VIDEO_SINK_PROFILE_ID VIDEO_SINK_SVCLASS_ID +#define VIDEO_DISTRIBUTION_PROFILE_ID VIDEO_DISTRIBUTION_SVCLASS_ID +#define HDP_PROFILE_ID HDP_SVCLASS_ID +#define HDP_SOURCE_PROFILE_ID HDP_SOURCE_SVCLASS_ID +#define HDP_SINK_PROFILE_ID HDP_SINK_SVCLASS_ID +#define GENERIC_ACCESS_PROFILE_ID GENERIC_ACCESS_SVCLASS_ID +#define GENERIC_ATTRIB_PROFILE_ID GENERIC_ATTRIB_SVCLASS_ID +#define APPLE_AGENT_PROFILE_ID APPLE_AGENT_SVCLASS_ID +#define MPS_PROFILE_ID MPS_SC_SVCLASS_ID + +/* + * Compatibility macros for the old MDP acronym + */ +#define MDP_SVCLASS_ID HDP_SVCLASS_ID +#define MDP_SOURCE_SVCLASS_ID HDP_SOURCE_SVCLASS_ID +#define MDP_SINK_SVCLASS_ID HDP_SINK_SVCLASS_ID +#define MDP_PROFILE_ID HDP_PROFILE_ID +#define MDP_SOURCE_PROFILE_ID HDP_SOURCE_PROFILE_ID +#define MDP_SINK_PROFILE_ID HDP_SINK_PROFILE_ID + +/* + * Attribute identifier codes + */ +#define SDP_SERVER_RECORD_HANDLE 0x0000 + +/* + * Possible values for attribute-id are listed below. + * See SDP Spec, section "Service Attribute Definitions" for more details. + */ +#define SDP_ATTR_RECORD_HANDLE 0x0000 +#define SDP_ATTR_SVCLASS_ID_LIST 0x0001 +#define SDP_ATTR_RECORD_STATE 0x0002 +#define SDP_ATTR_SERVICE_ID 0x0003 +#define SDP_ATTR_PROTO_DESC_LIST 0x0004 +#define SDP_ATTR_BROWSE_GRP_LIST 0x0005 +#define SDP_ATTR_LANG_BASE_ATTR_ID_LIST 0x0006 +#define SDP_ATTR_SVCINFO_TTL 0x0007 +#define SDP_ATTR_SERVICE_AVAILABILITY 0x0008 +#define SDP_ATTR_PFILE_DESC_LIST 0x0009 +#define SDP_ATTR_DOC_URL 0x000a +#define SDP_ATTR_CLNT_EXEC_URL 0x000b +#define SDP_ATTR_ICON_URL 0x000c +#define SDP_ATTR_ADD_PROTO_DESC_LIST 0x000d + +#define SDP_ATTR_GROUP_ID 0x0200 +#define SDP_ATTR_IP_SUBNET 0x0200 +#define SDP_ATTR_VERSION_NUM_LIST 0x0200 +#define SDP_ATTR_SUPPORTED_FEATURES_LIST 0x0200 +#define SDP_ATTR_GOEP_L2CAP_PSM 0x0200 +#define SDP_ATTR_SVCDB_STATE 0x0201 + +#define SDP_ATTR_MPSD_SCENARIOS 0x0200 +#define SDP_ATTR_MPMD_SCENARIOS 0x0201 +#define SDP_ATTR_MPS_DEPENDENCIES 0x0202 + +#define SDP_ATTR_SERVICE_VERSION 0x0300 +#define SDP_ATTR_EXTERNAL_NETWORK 0x0301 +#define SDP_ATTR_SUPPORTED_DATA_STORES_LIST 0x0301 +#define SDP_ATTR_DATA_EXCHANGE_SPEC 0x0301 +#define SDP_ATTR_NETWORK 0x0301 +#define SDP_ATTR_FAX_CLASS1_SUPPORT 0x0302 +#define SDP_ATTR_REMOTE_AUDIO_VOLUME_CONTROL 0x0302 +#define SDP_ATTR_MCAP_SUPPORTED_PROCEDURES 0x0302 +#define SDP_ATTR_FAX_CLASS20_SUPPORT 0x0303 +#define SDP_ATTR_SUPPORTED_FORMATS_LIST 0x0303 +#define SDP_ATTR_FAX_CLASS2_SUPPORT 0x0304 +#define SDP_ATTR_AUDIO_FEEDBACK_SUPPORT 0x0305 +#define SDP_ATTR_NETWORK_ADDRESS 0x0306 +#define SDP_ATTR_WAP_GATEWAY 0x0307 +#define SDP_ATTR_HOMEPAGE_URL 0x0308 +#define SDP_ATTR_WAP_STACK_TYPE 0x0309 +#define SDP_ATTR_SECURITY_DESC 0x030a +#define SDP_ATTR_NET_ACCESS_TYPE 0x030b +#define SDP_ATTR_MAX_NET_ACCESSRATE 0x030c +#define SDP_ATTR_IP4_SUBNET 0x030d +#define SDP_ATTR_IP6_SUBNET 0x030e +#define SDP_ATTR_SUPPORTED_CAPABILITIES 0x0310 +#define SDP_ATTR_SUPPORTED_FEATURES 0x0311 +#define SDP_ATTR_SUPPORTED_FUNCTIONS 0x0312 +#define SDP_ATTR_TOTAL_IMAGING_DATA_CAPACITY 0x0313 +#define SDP_ATTR_SUPPORTED_REPOSITORIES 0x0314 +#define SDP_ATTR_MAS_INSTANCE_ID 0x0315 +#define SDP_ATTR_SUPPORTED_MESSAGE_TYPES 0x0316 +#define SDP_ATTR_PBAP_SUPPORTED_FEATURES 0x0317 +#define SDP_ATTR_MAP_SUPPORTED_FEATURES 0x0317 + +#define SDP_ATTR_SPECIFICATION_ID 0x0200 +#define SDP_ATTR_VENDOR_ID 0x0201 +#define SDP_ATTR_PRODUCT_ID 0x0202 +#define SDP_ATTR_VERSION 0x0203 +#define SDP_ATTR_PRIMARY_RECORD 0x0204 +#define SDP_ATTR_VENDOR_ID_SOURCE 0x0205 + +#define SDP_ATTR_HID_DEVICE_RELEASE_NUMBER 0x0200 +#define SDP_ATTR_HID_PARSER_VERSION 0x0201 +#define SDP_ATTR_HID_DEVICE_SUBCLASS 0x0202 +#define SDP_ATTR_HID_COUNTRY_CODE 0x0203 +#define SDP_ATTR_HID_VIRTUAL_CABLE 0x0204 +#define SDP_ATTR_HID_RECONNECT_INITIATE 0x0205 +#define SDP_ATTR_HID_DESCRIPTOR_LIST 0x0206 +#define SDP_ATTR_HID_LANG_ID_BASE_LIST 0x0207 +#define SDP_ATTR_HID_SDP_DISABLE 0x0208 +#define SDP_ATTR_HID_BATTERY_POWER 0x0209 +#define SDP_ATTR_HID_REMOTE_WAKEUP 0x020a +#define SDP_ATTR_HID_PROFILE_VERSION 0x020b +#define SDP_ATTR_HID_SUPERVISION_TIMEOUT 0x020c +#define SDP_ATTR_HID_NORMALLY_CONNECTABLE 0x020d +#define SDP_ATTR_HID_BOOT_DEVICE 0x020e + +/* + * These identifiers are based on the SDP spec stating that + * "base attribute id of the primary (universal) language must be 0x0100" + * + * Other languages should have their own offset; e.g.: + * #define XXXLangBase yyyy + * #define AttrServiceName_XXX 0x0000+XXXLangBase + */ +#define SDP_PRIMARY_LANG_BASE 0x0100 + +#define SDP_ATTR_SVCNAME_PRIMARY 0x0000 + SDP_PRIMARY_LANG_BASE +#define SDP_ATTR_SVCDESC_PRIMARY 0x0001 + SDP_PRIMARY_LANG_BASE +#define SDP_ATTR_PROVNAME_PRIMARY 0x0002 + SDP_PRIMARY_LANG_BASE + +/* + * The Data representation in SDP PDUs (pps 339, 340 of BT SDP Spec) + * These are the exact data type+size descriptor values + * that go into the PDU buffer. + * + * The datatype (leading 5bits) + size descriptor (last 3 bits) + * is 8 bits. The size descriptor is critical to extract the + * right number of bytes for the data value from the PDU. + * + * For most basic types, the datatype+size descriptor is + * straightforward. However for constructed types and strings, + * the size of the data is in the next "n" bytes following the + * 8 bits (datatype+size) descriptor. Exactly what the "n" is + * specified in the 3 bits of the data size descriptor. + * + * TextString and URLString can be of size 2^{8, 16, 32} bytes + * DataSequence and DataSequenceAlternates can be of size 2^{8, 16, 32} + * The size are computed post-facto in the API and are not known apriori + */ +#define SDP_DATA_NIL 0x00 +#define SDP_UINT8 0x08 +#define SDP_UINT16 0x09 +#define SDP_UINT32 0x0A +#define SDP_UINT64 0x0B +#define SDP_UINT128 0x0C +#define SDP_INT8 0x10 +#define SDP_INT16 0x11 +#define SDP_INT32 0x12 +#define SDP_INT64 0x13 +#define SDP_INT128 0x14 +#define SDP_UUID_UNSPEC 0x18 +#define SDP_UUID16 0x19 +#define SDP_UUID32 0x1A +#define SDP_UUID128 0x1C +#define SDP_TEXT_STR_UNSPEC 0x20 +#define SDP_TEXT_STR8 0x25 +#define SDP_TEXT_STR16 0x26 +#define SDP_TEXT_STR32 0x27 +#define SDP_BOOL 0x28 +#define SDP_SEQ_UNSPEC 0x30 +#define SDP_SEQ8 0x35 +#define SDP_SEQ16 0x36 +#define SDP_SEQ32 0x37 +#define SDP_ALT_UNSPEC 0x38 +#define SDP_ALT8 0x3D +#define SDP_ALT16 0x3E +#define SDP_ALT32 0x3F +#define SDP_URL_STR_UNSPEC 0x40 +#define SDP_URL_STR8 0x45 +#define SDP_URL_STR16 0x46 +#define SDP_URL_STR32 0x47 + +/* + * The PDU identifiers of SDP packets between client and server + */ +#define SDP_ERROR_RSP 0x01 +#define SDP_SVC_SEARCH_REQ 0x02 +#define SDP_SVC_SEARCH_RSP 0x03 +#define SDP_SVC_ATTR_REQ 0x04 +#define SDP_SVC_ATTR_RSP 0x05 +#define SDP_SVC_SEARCH_ATTR_REQ 0x06 +#define SDP_SVC_SEARCH_ATTR_RSP 0x07 + +/* + * Some additions to support service registration. + * These are outside the scope of the Bluetooth specification + */ +#define SDP_SVC_REGISTER_REQ 0x75 +#define SDP_SVC_REGISTER_RSP 0x76 +#define SDP_SVC_UPDATE_REQ 0x77 +#define SDP_SVC_UPDATE_RSP 0x78 +#define SDP_SVC_REMOVE_REQ 0x79 +#define SDP_SVC_REMOVE_RSP 0x80 + +/* + * SDP Error codes + */ +#define SDP_INVALID_VERSION 0x0001 +#define SDP_INVALID_RECORD_HANDLE 0x0002 +#define SDP_INVALID_SYNTAX 0x0003 +#define SDP_INVALID_PDU_SIZE 0x0004 +#define SDP_INVALID_CSTATE 0x0005 + +/* + * SDP PDU + */ +typedef struct { + uint8_t pdu_id; + uint16_t tid; + uint16_t plen; +} __attribute__ ((packed)) sdp_pdu_hdr_t; + +/* + * Common definitions for attributes in the SDP. + * Should the type of any of these change, you need only make a change here. + */ + +typedef struct { + uint8_t type; + union { + uint16_t uuid16; + uint32_t uuid32; + uint128_t uuid128; + } value; +} uuid_t; + +#define SDP_IS_UUID(x) ((x) == SDP_UUID16 || (x) == SDP_UUID32 || \ + (x) == SDP_UUID128) +#define SDP_IS_ALT(x) ((x) == SDP_ALT8 || (x) == SDP_ALT16 || (x) == SDP_ALT32) +#define SDP_IS_SEQ(x) ((x) == SDP_SEQ8 || (x) == SDP_SEQ16 || (x) == SDP_SEQ32) +#define SDP_IS_TEXT_STR(x) ((x) == SDP_TEXT_STR8 || (x) == SDP_TEXT_STR16 || \ + (x) == SDP_TEXT_STR32) + +typedef struct _sdp_list sdp_list_t; +struct _sdp_list { + sdp_list_t *next; + void *data; +}; + +/* + * User-visible strings can be in many languages + * in addition to the universal language. + * + * Language meta-data includes language code in ISO639 + * followed by the encoding format. The third field in this + * structure is the attribute offset for the language. + * User-visible strings in the specified language can be + * obtained at this offset. + */ +typedef struct { + uint16_t code_ISO639; + uint16_t encoding; + uint16_t base_offset; +} sdp_lang_attr_t; + +/* + * Profile descriptor is the Bluetooth profile metadata. If a + * service conforms to a well-known profile, then its profile + * identifier (UUID) is an attribute of the service. In addition, + * if the profile has a version number it is specified here. + */ +typedef struct { + uuid_t uuid; + uint16_t version; +} sdp_profile_desc_t; + +typedef struct { + uint8_t major; + uint8_t minor; +} sdp_version_t; + +typedef struct { + uint8_t *data; + uint32_t data_size; + uint32_t buf_size; +} sdp_buf_t; + +typedef struct { + uint32_t handle; + + /* Search pattern: a sequence of all UUIDs seen in this record */ + sdp_list_t *pattern; + sdp_list_t *attrlist; + + /* Main service class for Extended Inquiry Response */ + uuid_t svclass; +} sdp_record_t; + +typedef struct sdp_data_struct sdp_data_t; +struct sdp_data_struct { + uint8_t dtd; + uint16_t attrId; + union { + int8_t int8; + int16_t int16; + int32_t int32; + int64_t int64; + uint128_t int128; + uint8_t uint8; + uint16_t uint16; + uint32_t uint32; + uint64_t uint64; + uint128_t uint128; + uuid_t uuid; + char *str; + sdp_data_t *dataseq; + } val; + sdp_data_t *next; + int unitSize; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __SDP_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/sdp_lib.h b/Sources/CBluetoothLinuxABI/include/bluetooth/sdp_lib.h new file mode 100644 index 0000000..7a48ad5 --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/sdp_lib.h @@ -0,0 +1,628 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2001-2002 Nokia Corporation + * Copyright (C) 2002-2003 Maxim Krasnyansky + * Copyright (C) 2002-2010 Marcel Holtmann + * Copyright (C) 2002-2003 Stephen Crane + * + * + */ + +#ifndef __SDP_LIB_H +#define __SDP_LIB_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * SDP lists + */ +typedef void(*sdp_list_func_t)(void *, void *); +typedef void(*sdp_free_func_t)(void *); +typedef int (*sdp_comp_func_t)(const void *, const void *); + +sdp_list_t *sdp_list_append(sdp_list_t *list, void *d); +sdp_list_t *sdp_list_remove(sdp_list_t *list, void *d); +sdp_list_t *sdp_list_insert_sorted(sdp_list_t *list, void *data, sdp_comp_func_t f); +void sdp_list_free(sdp_list_t *list, sdp_free_func_t f); + +static inline int sdp_list_len(const sdp_list_t *list) +{ + int n = 0; + for (; list; list = list->next) + n++; + return n; +} + +static inline sdp_list_t *sdp_list_find(sdp_list_t *list, void *u, sdp_comp_func_t f) +{ + for (; list; list = list->next) + if (f(list->data, u) == 0) + return list; + return NULL; +} + +static inline void sdp_list_foreach(sdp_list_t *list, sdp_list_func_t f, void *u) +{ + for (; list; list = list->next) + f(list->data, u); +} + +/* + * Values of the flags parameter to sdp_record_register + */ +#define SDP_RECORD_PERSIST 0x01 +#define SDP_DEVICE_RECORD 0x02 + +/* + * Values of the flags parameter to sdp_connect + */ +#define SDP_RETRY_IF_BUSY 0x01 +#define SDP_WAIT_ON_CLOSE 0x02 +#define SDP_NON_BLOCKING 0x04 +#define SDP_LARGE_MTU 0x08 + +/* + * a session with an SDP server + */ +typedef struct { + int sock; + int state; + int local; + int flags; + uint16_t tid; /* Current transaction ID */ + void *priv; +} sdp_session_t; + +typedef enum { + /* + * Attributes are specified as individual elements + */ + SDP_ATTR_REQ_INDIVIDUAL = 1, + /* + * Attributes are specified as a range + */ + SDP_ATTR_REQ_RANGE +} sdp_attrreq_type_t; + +/* + * When the pdu_id(type) is a sdp error response, check the status value + * to figure out the error reason. For status values 0x0001-0x0006 check + * Bluetooth SPEC. If the status is 0xffff, call sdp_get_error function + * to get the real reason: + * - wrong transaction ID(EPROTO) + * - wrong PDU id or(EPROTO) + * - I/O error + */ +typedef void sdp_callback_t(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *udata); + +/* + * create an L2CAP connection to a Bluetooth device + * + * INPUT: + * + * bdaddr_t *src: + * Address of the local device to use to make the connection + * (or BDADDR_ANY) + * + * bdaddr_t *dst: + * Address of the SDP server device + */ +sdp_session_t *sdp_connect(const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags); +int sdp_close(sdp_session_t *session); +int sdp_get_socket(const sdp_session_t *session); + +/* + * SDP transaction: functions for asynchronous search. + */ +sdp_session_t *sdp_create(int sk, uint32_t flags); +int sdp_get_error(sdp_session_t *session); +int sdp_process(sdp_session_t *session); +int sdp_set_notify(sdp_session_t *session, sdp_callback_t *func, void *udata); + +int sdp_service_search_async(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num); +int sdp_service_attr_async(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list); +int sdp_service_search_attr_async(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list); + +uint16_t sdp_gen_tid(sdp_session_t *session); + +/* + * find all devices in the piconet + */ +int sdp_general_inquiry(inquiry_info *ii, int dev_num, int duration, uint8_t *found); + +/* flexible extraction of basic attributes - Jean II */ +int sdp_get_int_attr(const sdp_record_t *rec, uint16_t attr, int *value); +int sdp_get_string_attr(const sdp_record_t *rec, uint16_t attr, char *value, + size_t valuelen); + +/* + * Basic sdp data functions + */ +sdp_data_t *sdp_data_alloc(uint8_t dtd, const void *value); +sdp_data_t *sdp_data_alloc_with_length(uint8_t dtd, const void *value, uint32_t length); +void sdp_data_free(sdp_data_t *data); +sdp_data_t *sdp_data_get(const sdp_record_t *rec, uint16_t attr_id); + +sdp_data_t *sdp_seq_alloc(void **dtds, void **values, int len); +sdp_data_t *sdp_seq_alloc_with_length(void **dtds, void **values, int *length, int len); +sdp_data_t *sdp_seq_append(sdp_data_t *seq, sdp_data_t *data); + +int sdp_attr_add(sdp_record_t *rec, uint16_t attr, sdp_data_t *data); +void sdp_attr_remove(sdp_record_t *rec, uint16_t attr); +void sdp_attr_replace(sdp_record_t *rec, uint16_t attr, sdp_data_t *data); +int sdp_set_uuidseq_attr(sdp_record_t *rec, uint16_t attr, sdp_list_t *seq); +int sdp_get_uuidseq_attr(const sdp_record_t *rec, uint16_t attr, sdp_list_t **seqp); + +/* + * NOTE that none of the functions below will update the SDP server, + * unless the {register, update}sdp_record_t() function is invoked. + * All functions which return an integer value, return 0 on success + * or -1 on failure. + */ + +/* + * Create an attribute and add it to the service record's attribute list. + * This consists of the data type descriptor of the attribute, + * the value of the attribute and the attribute identifier. + */ +int sdp_attr_add_new(sdp_record_t *rec, uint16_t attr, uint8_t dtd, const void *p); + +/* + * Set the information attributes of the service record. + * The set of attributes comprises service name, description + * and provider name + */ +void sdp_set_info_attr(sdp_record_t *rec, const char *name, const char *prov, const char *desc); + +/* + * Set the ServiceClassID attribute to the sequence specified by seq. + * Note that the identifiers need to be in sorted order from the most + * specific to the most generic service class that this service + * conforms to. + */ +static inline int sdp_set_service_classes(sdp_record_t *rec, sdp_list_t *seq) +{ + return sdp_set_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seq); +} + +/* + * Get the service classes to which the service conforms. + * + * When set, the list contains elements of ServiceClassIdentifer(uint16_t) + * ordered from most specific to most generic + */ +static inline int sdp_get_service_classes(const sdp_record_t *rec, sdp_list_t **seqp) +{ + return sdp_get_uuidseq_attr(rec, SDP_ATTR_SVCLASS_ID_LIST, seqp); +} + +/* + * Set the BrowseGroupList attribute to the list specified by seq. + * + * A service can belong to one or more service groups + * and the list comprises such group identifiers (UUIDs) + */ +static inline int sdp_set_browse_groups(sdp_record_t *rec, sdp_list_t *seq) +{ + return sdp_set_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seq); +} + +/* + * Set the access protocols of the record to those specified in proto + */ +int sdp_set_access_protos(sdp_record_t *rec, const sdp_list_t *proto); + +/* + * Set the additional access protocols of the record to those specified in proto + */ +int sdp_set_add_access_protos(sdp_record_t *rec, const sdp_list_t *proto); + +/* + * Get protocol port (i.e. PSM for L2CAP, Channel for RFCOMM) + */ +int sdp_get_proto_port(const sdp_list_t *list, int proto); + +/* + * Get protocol descriptor. + */ +sdp_data_t *sdp_get_proto_desc(sdp_list_t *list, int proto); + +/* + * Set the LanguageBase attributes to the values specified in list + * (a linked list of sdp_lang_attr_t objects, one for each language in + * which user-visible attributes are present). + */ +int sdp_set_lang_attr(sdp_record_t *rec, const sdp_list_t *list); + +/* + * Set the ServiceInfoTimeToLive attribute of the service. + * This is the number of seconds that this record is guaranteed + * not to change after being obtained by a client. + */ +static inline int sdp_set_service_ttl(sdp_record_t *rec, uint32_t ttl) +{ + return sdp_attr_add_new(rec, SDP_ATTR_SVCINFO_TTL, SDP_UINT32, &ttl); +} + +/* + * Set the ServiceRecordState attribute of a service. This is + * guaranteed to change if there is any kind of modification to + * the record. + */ +static inline int sdp_set_record_state(sdp_record_t *rec, uint32_t state) +{ + return sdp_attr_add_new(rec, SDP_ATTR_RECORD_STATE, SDP_UINT32, &state); +} + +/* + * Set the ServiceID attribute of a service. + */ +void sdp_set_service_id(sdp_record_t *rec, uuid_t uuid); + +/* + * Set the GroupID attribute of a service + */ +void sdp_set_group_id(sdp_record_t *rec, uuid_t grouuuid); + +/* + * Set the ServiceAvailability attribute of a service. + * + * Note that this represents the relative availability + * of the service: 0x00 means completely unavailable; + * 0xFF means maximum availability. + */ +static inline int sdp_set_service_avail(sdp_record_t *rec, uint8_t avail) +{ + return sdp_attr_add_new(rec, SDP_ATTR_SERVICE_AVAILABILITY, SDP_UINT8, &avail); +} + +/* + * Set the profile descriptor list attribute of a record. + * + * Each element in the list is an object of type + * sdp_profile_desc_t which is a definition of the + * Bluetooth profile that this service conforms to. + */ +int sdp_set_profile_descs(sdp_record_t *rec, const sdp_list_t *desc); + +/* + * Set URL attributes of a record. + * + * ClientExecutableURL: a URL to a client's platform specific (WinCE, + * PalmOS) executable code that can be used to access this service. + * + * DocumentationURL: a URL pointing to service documentation + * + * IconURL: a URL to an icon that can be used to represent this service. + * + * Note: pass NULL for any URLs that you don't want to set or remove + */ +void sdp_set_url_attr(sdp_record_t *rec, const char *clientExecURL, const char *docURL, const char *iconURL); + +/* + * a service search request. + * + * INPUT : + * + * sdp_list_t *search + * list containing elements of the search + * pattern. Each entry in the list is a UUID + * of the service to be searched + * + * uint16_t max_rec_num + * An integer specifying the maximum number of + * entries that the client can handle in the response. + * + * OUTPUT : + * + * int return value + * 0 + * The request completed successfully. This does not + * mean the requested services were found + * -1 + * The request completed unsuccessfully + * + * sdp_list_t *rsp_list + * This variable is set on a successful return if there are + * non-zero service handles. It is a singly linked list of + * service record handles (uint16_t) + */ +int sdp_service_search_req(sdp_session_t *session, const sdp_list_t *search, uint16_t max_rec_num, sdp_list_t **rsp_list); + +/* + * a service attribute request. + * + * INPUT : + * + * uint32_t handle + * The handle of the service for which the attribute(s) are + * requested + * + * sdp_attrreq_type_t reqtype + * Attribute identifiers are 16 bit unsigned integers specified + * in one of 2 ways described below : + * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers + * They are the actual attribute identifiers in ascending order + * + * SDP_ATTR_REQ_RANGE - 32bit identifier range + * The high-order 16bits is the start of range + * the low-order 16bits are the end of range + * 0x0000 to 0xFFFF gets all attributes + * + * sdp_list_t *attrid_list + * Singly linked list containing attribute identifiers desired. + * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL) + * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE) + * + * OUTPUT : + * int return value + * 0 + * The request completed successfully. This does not + * mean the requested services were found + * -1 + * The request completed unsuccessfully due to a timeout + */ +sdp_record_t *sdp_service_attr_req(sdp_session_t *session, uint32_t handle, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list); + +/* + * This is a service search request combined with the service + * attribute request. First a service class match is done and + * for matching service, requested attributes are extracted + * + * INPUT : + * + * sdp_list_t *search + * Singly linked list containing elements of the search + * pattern. Each entry in the list is a UUID(DataTypeSDP_UUID16) + * of the service to be searched + * + * AttributeSpecification attrSpec + * Attribute identifiers are 16 bit unsigned integers specified + * in one of 2 ways described below : + * SDP_ATTR_REQ_INDIVIDUAL - 16bit individual identifiers + * They are the actual attribute identifiers in ascending order + * + * SDP_ATTR_REQ_RANGE - 32bit identifier range + * The high-order 16bits is the start of range + * the low-order 16bits are the end of range + * 0x0000 to 0xFFFF gets all attributes + * + * sdp_list_t *attrid_list + * Singly linked list containing attribute identifiers desired. + * Every element is either a uint16_t(attrSpec = SDP_ATTR_REQ_INDIVIDUAL) + * or a uint32_t(attrSpec=SDP_ATTR_REQ_RANGE) + * + * OUTPUT : + * int return value + * 0 + * The request completed successfully. This does not + * mean the requested services were found + * -1 + * The request completed unsuccessfully due to a timeout + * + * sdp_list_t *rsp_list + * This variable is set on a successful return to point to + * service(s) found. Each element of this list is of type + * sdp_record_t *. + */ +int sdp_service_search_attr_req(sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list); + +/* + * Allocate/free a service record and its attributes + */ +sdp_record_t *sdp_record_alloc(void); +void sdp_record_free(sdp_record_t *rec); + +/* + * Register a service record. + * + * Note: It is the responsibility of the Service Provider to create the + * record first and set its attributes using setXXX() methods. + * + * The service provider must then call sdp_record_register() to make + * the service record visible to SDP clients. This function returns 0 + * on success or -1 on failure (and sets errno). + */ +int sdp_device_record_register_binary(sdp_session_t *session, bdaddr_t *device, uint8_t *data, uint32_t size, uint8_t flags, uint32_t *handle); +int sdp_device_record_register(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec, uint8_t flags); +int sdp_record_register(sdp_session_t *session, sdp_record_t *rec, uint8_t flags); + +/* + * Unregister a service record. + */ +int sdp_device_record_unregister_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle); +int sdp_device_record_unregister(sdp_session_t *session, bdaddr_t *device, sdp_record_t *rec); +int sdp_record_unregister(sdp_session_t *session, sdp_record_t *rec); + +/* + * Update an existing service record. (Calling this function + * before a previous call to sdp_record_register() will result + * in an error.) + */ +int sdp_device_record_update_binary(sdp_session_t *session, bdaddr_t *device, uint32_t handle, uint8_t *data, uint32_t size); +int sdp_device_record_update(sdp_session_t *session, bdaddr_t *device, const sdp_record_t *rec); +int sdp_record_update(sdp_session_t *sess, const sdp_record_t *rec); + +void sdp_record_print(const sdp_record_t *rec); + +/* + * UUID functions + */ +uuid_t *sdp_uuid16_create(uuid_t *uuid, uint16_t data); +uuid_t *sdp_uuid32_create(uuid_t *uuid, uint32_t data); +uuid_t *sdp_uuid128_create(uuid_t *uuid, const void *data); +int sdp_uuid16_cmp(const void *p1, const void *p2); +int sdp_uuid128_cmp(const void *p1, const void *p2); +int sdp_uuid_cmp(const void *p1, const void *p2); +uuid_t *sdp_uuid_to_uuid128(const uuid_t *uuid); +void sdp_uuid16_to_uuid128(uuid_t *uuid128, const uuid_t *uuid16); +void sdp_uuid32_to_uuid128(uuid_t *uuid128, const uuid_t *uuid32); +int sdp_uuid128_to_uuid(uuid_t *uuid); +int sdp_uuid_to_proto(uuid_t *uuid); +int sdp_uuid_extract(const uint8_t *buffer, int bufsize, uuid_t *uuid, int *scanned); +void sdp_uuid_print(const uuid_t *uuid); + +#define MAX_LEN_UUID_STR 37 +#define MAX_LEN_PROTOCOL_UUID_STR 8 +#define MAX_LEN_SERVICECLASS_UUID_STR 28 +#define MAX_LEN_PROFILEDESCRIPTOR_UUID_STR 28 + +int sdp_uuid2strn(const uuid_t *uuid, char *str, size_t n); +int sdp_proto_uuid2strn(const uuid_t *uuid, char *str, size_t n); +int sdp_svclass_uuid2strn(const uuid_t *uuid, char *str, size_t n); +int sdp_profile_uuid2strn(const uuid_t *uuid, char *str, size_t n); + +/* + * In all the sdp_get_XXX(handle, XXX *xxx) functions below, + * the XXX * is set to point to the value, should it exist + * and 0 is returned. If the value does not exist, -1 is + * returned and errno set to ENODATA. + * + * In all the methods below, the memory management rules are + * simple. Don't free anything! The pointer returned, in the + * case of constructed types, is a pointer to the contents + * of the sdp_record_t. + */ + +/* + * Get the access protocols from the service record + */ +int sdp_get_access_protos(const sdp_record_t *rec, sdp_list_t **protos); + +/* + * Get the additional access protocols from the service record + */ +int sdp_get_add_access_protos(const sdp_record_t *rec, sdp_list_t **protos); + +/* + * Extract the list of browse groups to which the service belongs. + * When set, seqp contains elements of GroupID (uint16_t) + */ +static inline int sdp_get_browse_groups(const sdp_record_t *rec, sdp_list_t **seqp) +{ + return sdp_get_uuidseq_attr(rec, SDP_ATTR_BROWSE_GRP_LIST, seqp); +} + +/* + * Extract language attribute meta-data of the service record. + * For each language in the service record, LangSeq has a struct of type + * sdp_lang_attr_t. + */ +int sdp_get_lang_attr(const sdp_record_t *rec, sdp_list_t **langSeq); + +/* + * Extract the Bluetooth profile descriptor sequence from a record. + * Each element in the list is of type sdp_profile_desc_t + * which contains the UUID of the profile and its version number + * (encoded as major and minor in the high-order 8bits + * and low-order 8bits respectively of the uint16_t) + */ +int sdp_get_profile_descs(const sdp_record_t *rec, sdp_list_t **profDesc); + +/* + * Extract SDP server version numbers + * + * Note: that this is an attribute of the SDP server only and + * contains a list of uint16_t each of which represent the + * major and minor SDP version numbers supported by this server + */ +int sdp_get_server_ver(const sdp_record_t *rec, sdp_list_t **pVnumList); + +int sdp_get_service_id(const sdp_record_t *rec, uuid_t *uuid); +int sdp_get_group_id(const sdp_record_t *rec, uuid_t *uuid); +int sdp_get_record_state(const sdp_record_t *rec, uint32_t *svcRecState); +int sdp_get_service_avail(const sdp_record_t *rec, uint8_t *svcAvail); +int sdp_get_service_ttl(const sdp_record_t *rec, uint32_t *svcTTLInfo); +int sdp_get_database_state(const sdp_record_t *rec, uint32_t *svcDBState); + +static inline int sdp_get_service_name(const sdp_record_t *rec, char *str, + size_t len) +{ + return sdp_get_string_attr(rec, SDP_ATTR_SVCNAME_PRIMARY, str, len); +} + +static inline int sdp_get_service_desc(const sdp_record_t *rec, char *str, + size_t len) +{ + return sdp_get_string_attr(rec, SDP_ATTR_SVCDESC_PRIMARY, str, len); +} + +static inline int sdp_get_provider_name(const sdp_record_t *rec, char *str, + size_t len) +{ + return sdp_get_string_attr(rec, SDP_ATTR_PROVNAME_PRIMARY, str, len); +} + +static inline int sdp_get_doc_url(const sdp_record_t *rec, char *str, + size_t len) +{ + return sdp_get_string_attr(rec, SDP_ATTR_DOC_URL, str, len); +} + +static inline int sdp_get_clnt_exec_url(const sdp_record_t *rec, char *str, + size_t len) +{ + return sdp_get_string_attr(rec, SDP_ATTR_CLNT_EXEC_URL, str, len); +} + +static inline int sdp_get_icon_url(const sdp_record_t *rec, char *str, + size_t len) +{ + return sdp_get_string_attr(rec, SDP_ATTR_ICON_URL, str, len); +} + +/* + * Set the supported features + * sf should be a list of list with each feature data + * Returns 0 on success -1 on fail + */ +int sdp_set_supp_feat(sdp_record_t *rec, const sdp_list_t *sf); + +/* + * Get the supported features + * seqp is set to a list of list with each feature data + * Returns 0 on success, if an error occurred -1 is returned and errno is set + */ +int sdp_get_supp_feat(const sdp_record_t *rec, sdp_list_t **seqp); + +sdp_record_t *sdp_extract_pdu(const uint8_t *pdata, int bufsize, int *scanned); +sdp_record_t *sdp_copy_record(sdp_record_t *rec); + +void sdp_data_print(sdp_data_t *data); +void sdp_print_service_attr(sdp_list_t *alist); + +int sdp_attrid_comp_func(const void *key1, const void *key2); + +void sdp_set_seq_len(uint8_t *ptr, uint32_t length); +void sdp_set_attrid(sdp_buf_t *pdu, uint16_t id); +void sdp_append_to_pdu(sdp_buf_t *dst, sdp_data_t *d); +void sdp_append_to_buf(sdp_buf_t *dst, uint8_t *data, uint32_t len); + +int sdp_gen_pdu(sdp_buf_t *pdu, sdp_data_t *data); +int sdp_gen_record_pdu(const sdp_record_t *rec, sdp_buf_t *pdu); + +int sdp_extract_seqtype(const uint8_t *buf, int bufsize, uint8_t *dtdp, int *size); + +sdp_data_t *sdp_extract_attr(const uint8_t *pdata, int bufsize, int *extractedLength, sdp_record_t *rec); + +void sdp_pattern_add_uuid(sdp_record_t *rec, uuid_t *uuid); +void sdp_pattern_add_uuidseq(sdp_record_t *rec, sdp_list_t *seq); + +int sdp_send_req_w4_rsp(sdp_session_t *session, uint8_t *req, uint8_t *rsp, uint32_t reqsize, uint32_t *rspsize); + +void sdp_add_lang_attr(sdp_record_t *rec); + +#ifdef __cplusplus +} +#endif + +#endif /* __SDP_LIB_H */ diff --git a/Sources/CBluetoothLinuxABI/include/bluetooth/uuid.h b/Sources/CBluetoothLinuxABI/include/bluetooth/uuid.h new file mode 100644 index 0000000..479986f --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/bluetooth/uuid.h @@ -0,0 +1,288 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * Copyright 2023 NXP + * + * + */ + +#ifndef __BLUETOOTH_UUID_H +#define __BLUETOOTH_UUID_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb" + +#define HSP_HS_UUID "00001108-0000-1000-8000-00805f9b34fb" +#define HSP_AG_UUID "00001112-0000-1000-8000-00805f9b34fb" + +#define HFP_HS_UUID "0000111e-0000-1000-8000-00805f9b34fb" +#define HFP_AG_UUID "0000111f-0000-1000-8000-00805f9b34fb" + +#define ADVANCED_AUDIO_UUID "0000110d-0000-1000-8000-00805f9b34fb" + +#define A2DP_SOURCE_UUID "0000110a-0000-1000-8000-00805f9b34fb" +#define A2DP_SINK_UUID "0000110b-0000-1000-8000-00805f9b34fb" + +#define AVRCP_REMOTE_UUID "0000110e-0000-1000-8000-00805f9b34fb" +#define AVRCP_TARGET_UUID "0000110c-0000-1000-8000-00805f9b34fb" + +#define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb" +#define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb" +#define GN_UUID "00001117-0000-1000-8000-00805f9b34fb" +#define BNEP_SVC_UUID "0000000f-0000-1000-8000-00805f9b34fb" + +#define PNPID_UUID "00002a50-0000-1000-8000-00805f9b34fb" +#define DEVICE_INFORMATION_UUID "0000180a-0000-1000-8000-00805f9b34fb" + +#define GATT_UUID "00001801-0000-1000-8000-00805f9b34fb" +#define IMMEDIATE_ALERT_UUID "00001802-0000-1000-8000-00805f9b34fb" +#define LINK_LOSS_UUID "00001803-0000-1000-8000-00805f9b34fb" +#define TX_POWER_UUID "00001804-0000-1000-8000-00805f9b34fb" +#define BATTERY_UUID "0000180f-0000-1000-8000-00805f9b34fb" +#define SCAN_PARAMETERS_UUID "00001813-0000-1000-8000-00805f9b34fb" + +#define SAP_UUID "0000112D-0000-1000-8000-00805f9b34fb" + +#define HEART_RATE_UUID "0000180d-0000-1000-8000-00805f9b34fb" +#define HEART_RATE_MEASUREMENT_UUID "00002a37-0000-1000-8000-00805f9b34fb" +#define BODY_SENSOR_LOCATION_UUID "00002a38-0000-1000-8000-00805f9b34fb" +#define HEART_RATE_CONTROL_POINT_UUID "00002a39-0000-1000-8000-00805f9b34fb" + +#define HEALTH_THERMOMETER_UUID "00001809-0000-1000-8000-00805f9b34fb" +#define TEMPERATURE_MEASUREMENT_UUID "00002a1c-0000-1000-8000-00805f9b34fb" +#define TEMPERATURE_TYPE_UUID "00002a1d-0000-1000-8000-00805f9b34fb" +#define INTERMEDIATE_TEMPERATURE_UUID "00002a1e-0000-1000-8000-00805f9b34fb" +#define MEASUREMENT_INTERVAL_UUID "00002a21-0000-1000-8000-00805f9b34fb" + +#define CYCLING_SC_UUID "00001816-0000-1000-8000-00805f9b34fb" +#define CSC_MEASUREMENT_UUID "00002a5b-0000-1000-8000-00805f9b34fb" +#define CSC_FEATURE_UUID "00002a5c-0000-1000-8000-00805f9b34fb" +#define SENSOR_LOCATION_UUID "00002a5d-0000-1000-8000-00805f9b34fb" +#define SC_CONTROL_POINT_UUID "00002a55-0000-1000-8000-00805f9b34fb" + +#define RFCOMM_UUID_STR "00000003-0000-1000-8000-00805f9b34fb" + +#define HDP_UUID "00001400-0000-1000-8000-00805f9b34fb" +#define HDP_SOURCE_UUID "00001401-0000-1000-8000-00805f9b34fb" +#define HDP_SINK_UUID "00001402-0000-1000-8000-00805f9b34fb" + +#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb" +#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb" + +#define DUN_GW_UUID "00001103-0000-1000-8000-00805f9b34fb" + +#define GAP_UUID "00001800-0000-1000-8000-00805f9b34fb" +#define PNP_UUID "00001200-0000-1000-8000-00805f9b34fb" + +#define SPP_UUID "00001101-0000-1000-8000-00805f9b34fb" + +#define OBEX_SYNC_UUID "00001104-0000-1000-8000-00805f9b34fb" +#define OBEX_OPP_UUID "00001105-0000-1000-8000-00805f9b34fb" +#define OBEX_FTP_UUID "00001106-0000-1000-8000-00805f9b34fb" +#define OBEX_PCE_UUID "0000112e-0000-1000-8000-00805f9b34fb" +#define OBEX_PSE_UUID "0000112f-0000-1000-8000-00805f9b34fb" +#define OBEX_PBAP_UUID "00001130-0000-1000-8000-00805f9b34fb" +#define OBEX_MAS_UUID "00001132-0000-1000-8000-00805f9b34fb" +#define OBEX_MNS_UUID "00001133-0000-1000-8000-00805f9b34fb" +#define OBEX_MAP_UUID "00001134-0000-1000-8000-00805f9b34fb" + +/* GATT UUIDs section */ +#define GATT_PRIM_SVC_UUID 0x2800 +#define GATT_SND_SVC_UUID 0x2801 +#define GATT_INCLUDE_UUID 0x2802 +#define GATT_CHARAC_UUID 0x2803 + +/* GATT Characteristic Types */ +#define GATT_CHARAC_DEVICE_NAME 0x2A00 +#define GATT_CHARAC_APPEARANCE 0x2A01 +#define GATT_CHARAC_PERIPHERAL_PRIV_FLAG 0x2A02 +#define GATT_CHARAC_RECONNECTION_ADDRESS 0x2A03 +#define GATT_CHARAC_PERIPHERAL_PREF_CONN 0x2A04 +#define GATT_CHARAC_SERVICE_CHANGED 0x2A05 +#define GATT_CHARAC_BATTERY_LEVEL 0x2A19 +#define GATT_CHARAC_SYSTEM_ID 0x2A23 +#define GATT_CHARAC_MODEL_NUMBER_STRING 0x2A24 +#define GATT_CHARAC_SERIAL_NUMBER_STRING 0x2A25 +#define GATT_CHARAC_FIRMWARE_REVISION_STRING 0x2A26 +#define GATT_CHARAC_HARDWARE_REVISION_STRING 0x2A27 +#define GATT_CHARAC_SOFTWARE_REVISION_STRING 0x2A28 +#define GATT_CHARAC_MANUFACTURER_NAME_STRING 0x2A29 +#define GATT_CHARAC_PNP_ID 0x2A50 +#define GATT_CHARAC_CAR 0x2AA6 + +/* GATT Characteristic Descriptors */ +#define GATT_CHARAC_EXT_PROPER_UUID 0x2900 +#define GATT_CHARAC_USER_DESC_UUID 0x2901 +#define GATT_CLIENT_CHARAC_CFG_UUID 0x2902 +#define GATT_SERVER_CHARAC_CFG_UUID 0x2903 +#define GATT_CHARAC_FMT_UUID 0x2904 +#define GATT_CHARAC_AGREG_FMT_UUID 0x2905 +#define GATT_CHARAC_VALID_RANGE_UUID 0x2906 +#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907 +#define GATT_REPORT_REFERENCE 0x2908 + +/* GATT Mesh Services */ +#define MESH_PROV_SVC_UUID "00001827-0000-1000-8000-00805f9b34fb" +#define MESH_PROXY_SVC_UUID "00001828-0000-1000-8000-00805f9b34fb" + +/* GATT Mesh Characteristic Types */ +#define MESH_PROVISIONING_DATA_IN 0x2ADB +#define MESH_PROVISIONING_DATA_OUT 0x2ADC +#define MESH_PROXY_DATA_IN 0x2ADD +#define MESH_PROXY_DATA_OUT 0x2ADE + +/* GATT Caching attributes */ +#define GATT_CHARAC_CLI_FEAT 0x2B29 +#define GATT_CHARAC_DB_HASH 0x2B2A + +/* GATT Server Supported features */ +#define GATT_CHARAC_SERVER_FEAT 0x2B3A + +/* TODO: Update these on final UUID is given */ +#define PACS_UUID 0x1850 +#define PAC_SINK_CHRC_UUID 0x2bc9 +#define PAC_SINK_UUID "00002bc9-0000-1000-8000-00805f9b34fb" +#define PAC_SINK_LOC_CHRC_UUID 0x2bca + +#define PAC_SOURCE_CHRC_UUID 0x2bcb +#define PAC_SOURCE_UUID "00002bcb-0000-1000-8000-00805f9b34fb" +#define PAC_SOURCE_LOC_CHRC_UUID 0x2bcc + +#define BCAA_SERVICE 0x1852 +#define BCAA_SERVICE_UUID "00001852-0000-1000-8000-00805f9b34fb" + +#define BAA_SERVICE 0x1851 +#define BAA_SERVICE_UUID "00001851-0000-1000-8000-00805f9b34fb" + +#define ASHA_SERVICE 0xFDF0 +#define ASHA_PROFILE_UUID "0000FDF0-0000-1000-8000-00805f9b34fb" + +#define PAC_CONTEXT 0x2bcd +#define PAC_SUPPORTED_CONTEXT 0x2bce + +#define ASCS_UUID 0x184e +#define ASE_SINK_UUID 0x2bc4 +#define ASE_SOURCE_UUID 0x2bc5 +#define ASE_CP_UUID 0x2bc6 + +#define BASS_UUID 0x184f +#define BCAST_AUDIO_SCAN_CP_UUID 0x2bc7 +#define BCAST_RECV_STATE_UUID 0x2bc8 + +#define VCS_UUID 0x1844 +#define VOL_OFFSET_CS_UUID 0x1845 +#define AUDIO_INPUT_CS_UUID 0x1843 +#define VOL_STATE_CHRC_UUID 0x2B7D +#define VOL_CP_CHRC_UUID 0x2B7E +#define VOL_FLAG_CHRC_UUID 0x2B7F + +#define VOCS_STATE_CHAR_UUID 0x2B80 +#define VOCS_AUDIO_LOC_CHRC_UUID 0x2B81 +#define VOCS_CP_CHRC_UUID 0x2B82 +#define VOCS_AUDIO_OP_DESC_CHAR_UUID 0x2B83 + +#define AICS_INPUT_STATE_CHAR_UUID 0x2B77 +#define AICS_GAIN_SETTING_PROP_CHAR_UUID 0x2B78 +#define AICS_AUDIO_INPUT_TYPE_CHAR_UUID 0x2B79 +#define AICS_INPUT_STATUS_CHAR_UUID 0X2B7A +#define AICS_AUDIO_INPUT_CP_CHRC_UUID 0X2B7B +#define AICS_INPUT_DESCR_CHAR_UUID 0X2B7C + +#define GMCS_UUID 0x1849 +#define MEDIA_PLAYER_NAME_CHRC_UUID 0x2b93 +#define MEDIA_TRACK_CHNGD_CHRC_UUID 0x2b96 +#define MEDIA_TRACK_TITLE_CHRC_UUID 0x2b97 +#define MEDIA_TRACK_DURATION_CHRC_UUID 0x2b98 +#define MEDIA_TRACK_POSTION_CHRC_UUID 0x2b99 +#define MEDIA_PLAYBACK_SPEED_CHRC_UUID 0x2b9a +#define MEDIA_SEEKING_SPEED_CHRC_UUID 0x2b9b +#define MEDIA_PLAYING_ORDER_CHRC_UUID 0x2ba1 +#define MEDIA_PLAY_ORDER_SUPPRTD_CHRC_UUID 0x2ba2 +#define MEDIA_STATE_CHRC_UUID 0x2ba3 +#define MEDIA_CP_CHRC_UUID 0x2ba4 +#define MEDIA_CP_OP_SUPPORTED_CHRC_UUID 0x2ba5 +#define MEDIA_CONTENT_CONTROL_ID_CHRC_UUID 0x2bba + +/* Coordinated Set Identification Profile(CSIP) */ +#define CSIS_UUID 0x1846 +#define CS_SIRK 0x2B84 +#define CS_SIZE 0x2B85 +#define CS_LOCK 0x2B86 +#define CS_RANK 0x2B87 + + +/* Microphone Control Service(MICS) */ +#define MICS_UUID 0x184D +#define MUTE_CHRC_UUID 0x2BC3 + +/* Call Control Service(TBS/CCS) */ +#define TBS_UUID 0x184B +#define GTBS_UUID 0x184C + +#define BEARER_PROVIDER_NAME_CHRC_UUID 0x2bb3 +#define BEARER_UCI_CHRC_UUID 0x2bb4 +#define BEARER_TECH_CHRC_UUID 0x2bb5 +#define BEARER_URI_SCHEME_CHRC_UUID 0x2bb6 +#define BEARER_SIGNAL_STR_CHRC_UUID 0x2bb7 +#define BEARER_SIGNAL_INTRVL_CHRC_UUID 0x2bb8 +#define CURR_CALL_LIST_CHRC_UUID 0x2bb9 +#define BEARER_CCID_CHRC_UUID 0x2bba +#define CALL_STATUS_FLAG_CHRC_UUID 0x2bbb +#define INCOM_CALL_TARGET_URI_CHRC_UUID 0x2bbc +#define CALL_STATE_CHRC_UUID 0x2bbd +#define CALL_CTRL_POINT_CHRC_UUID 0x2bbe +#define CALL_CTRL_POINT_OPT_OPCODE_CHRC_UUID 0x2bbf +#define TERMINATION_REASON_CHRC_UUID 0x2bc0 +#define INCOMING_CALL_CHRC_UUID 0x2bc1 +#define CALL_FRIENDLY_NAME_CHRC_UUID 0x2bc2 + +typedef struct { + enum { + BT_UUID_UNSPEC = 0, + BT_UUID16 = 16, + BT_UUID32 = 32, + BT_UUID128 = 128, + } type; + union { + uint16_t u16; + uint32_t u32; + uint128_t u128; + } value; +} bt_uuid_t; + +int bt_uuid_strcmp(const void *a, const void *b); + +int bt_uuid16_create(bt_uuid_t *btuuid, uint16_t value); +int bt_uuid32_create(bt_uuid_t *btuuid, uint32_t value); +int bt_uuid128_create(bt_uuid_t *btuuid, uint128_t value); + +int bt_uuid_cmp(const bt_uuid_t *uuid1, const bt_uuid_t *uuid2); +int bt_uuid16_cmp(const bt_uuid_t *uuid1, uint16_t uuid2); +void bt_uuid_to_uuid128(const bt_uuid_t *src, bt_uuid_t *dst); + +#define MAX_LEN_UUID_STR 37 + +int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n); +int bt_string_to_uuid(bt_uuid_t *uuid, const char *string); + +int bt_uuid_to_le(const bt_uuid_t *uuid, void *dst); + +static inline int bt_uuid_len(const bt_uuid_t *uuid) +{ + return uuid->type / 8; +} + +#ifdef __cplusplus +} +#endif + +#endif /* __BLUETOOTH_UUID_H */ diff --git a/Sources/CBluetoothLinuxABI/include/module.modulemap b/Sources/CBluetoothLinuxABI/include/module.modulemap new file mode 100644 index 0000000..d97d54d --- /dev/null +++ b/Sources/CBluetoothLinuxABI/include/module.modulemap @@ -0,0 +1,4 @@ +module CBluetoothLinuxABI { + umbrella header "CBluetoothLinuxABI.h" + export * +} diff --git a/Tests/BluetoothLinuxABITests/HCIDeviceTests.swift b/Tests/BluetoothLinuxABITests/HCIDeviceTests.swift new file mode 100644 index 0000000..50b1f73 --- /dev/null +++ b/Tests/BluetoothLinuxABITests/HCIDeviceTests.swift @@ -0,0 +1,92 @@ +// +// HCIDeviceTests.swift +// BluetoothLinuxABITests +// +// Covers only the parts of the HCI device management family that +// don't require a real or virtual Bluetooth adapter: input +// validation that returns before any socket is touched, and +// hci_send_cmd's wire format, verified over a plain pipe rather than +// an HCI socket. hci_devinfo/hci_devba/hci_for_each_dev/hci_get_route/ +// hci_send_req all need an actual AF_BLUETOOTH/BTPROTO_HCI device to +// exercise meaningfully and are covered by neither this file nor a +// conformance driver yet. +// + +import Testing +#if canImport(Glibc) +import Glibc +#elseif canImport(Darwin) +import Darwin +#endif +import CBluetoothLinuxABI +@testable import BluetoothLinuxABI + +@Suite("HCI Device") +struct HCIDeviceTests { + + @Test("hci_open_dev rejects a negative device id before opening a socket") + func openDevRejectsNegativeID() { + errno = 0 + #expect(BluetoothLinuxABI.hci_open_dev(-1) == -1) + #expect(errno == ENODEV) + } + + @Test("hci_close_dev on an invalid descriptor fails like close(2)") + func closeDevInvalidDescriptor() { + #expect(BluetoothLinuxABI.hci_close_dev(-1) == -1) + } + + @Test("hci_send_cmd writes the exact HCI command packet layout") + func sendCmdPacketLayout() { + var fds: [Int32] = [0, 0] + let pipeResult = fds.withUnsafeMutableBufferPointer { pipe($0.baseAddress) } + #expect(pipeResult == 0) + let readEnd = fds[0] + let writeEnd = fds[1] + defer { + close(readEnd) + close(writeEnd) + } + + var parameter: UInt8 = 0x42 + let result = withUnsafeMutableBytes(of: ¶meter) { buffer in + BluetoothLinuxABI.hci_send_cmd(writeEnd, 0x03, 0x0003, 1, buffer.baseAddress) + } + #expect(result == 0) + + var received = [UInt8](repeating: 0, count: 5) + let bytesRead = received.withUnsafeMutableBytes { read(readEnd, $0.baseAddress, $0.count) } + #expect(bytesRead == 5) + + // HCI_COMMAND_PKT + #expect(received[0] == 0x01) + // opcode, little-endian: ocf | (ogf << 10) = 0x0003 | (0x03 << 10) = 0x0C03 + #expect(received[1] == 0x03) + #expect(received[2] == 0x0C) + // plen + #expect(received[3] == 1) + // the single parameter byte + #expect(received[4] == 0x42) + } + + @Test("hci_send_cmd omits the parameter iovec when plen is 0") + func sendCmdNoParameter() { + var fds: [Int32] = [0, 0] + let pipeResult = fds.withUnsafeMutableBufferPointer { pipe($0.baseAddress) } + #expect(pipeResult == 0) + let readEnd = fds[0] + let writeEnd = fds[1] + defer { + close(readEnd) + close(writeEnd) + } + + let result = BluetoothLinuxABI.hci_send_cmd(writeEnd, 0x01, 0x0001, 0, nil) + #expect(result == 0) + + var received = [UInt8](repeating: 0, count: 4) + let bytesRead = received.withUnsafeMutableBytes { read(readEnd, $0.baseAddress, $0.count) } + #expect(bytesRead == 4) + #expect(received[3] == 0) + } +} diff --git a/Tests/BluetoothLinuxABITests/HCIStringsTests.swift b/Tests/BluetoothLinuxABITests/HCIStringsTests.swift new file mode 100644 index 0000000..cf3be29 --- /dev/null +++ b/Tests/BluetoothLinuxABITests/HCIStringsTests.swift @@ -0,0 +1,84 @@ +// +// HCIStringsTests.swift +// BluetoothLinuxABITests +// +// Round-trip and behavior tests for the HCI string converter family, +// asserting the reference (BlueZ `lib/bluetooth/hci.c`) behavior. +// + +import Testing +#if canImport(Glibc) +import Glibc +#elseif canImport(Darwin) +import Darwin +#endif +import CBluetoothLinuxABI +@testable import BluetoothLinuxABI + +@Suite("HCI Strings") +struct HCIStringsTests { + + @Test func bus() { + #expect(String(cString: BluetoothLinuxABI.hci_bustostr(Int32(HCI_USB))!) == "USB") + #expect(String(cString: BluetoothLinuxABI.hci_bustostr(99)!) == "Unknown") + #expect(String(cString: BluetoothLinuxABI.hci_dtypetostr(Int32(HCI_USB))!) == "USB") + } + + @Test func deviceType() { + #expect(String(cString: BluetoothLinuxABI.hci_typetostr(Int32(HCI_PRIMARY))!) == "Primary") + #expect(String(cString: BluetoothLinuxABI.hci_typetostr(Int32(HCI_AMP))!) == "AMP") + } + + @Test func deviceFlags() { + let down = BluetoothLinuxABI.hci_dflagstostr(0)! + #expect(String(cString: down) == "DOWN ") + free(down) + + // No "DOWN" prefix once HCI_UP is set, but "UP" itself is still + // printed — it's just another entry in the device flags table. + let up = BluetoothLinuxABI.hci_dflagstostr(1 << UInt32(HCI_UP))! + #expect(String(cString: up) == "UP ") + free(up) + } + + @Test func packetType() { + let str = BluetoothLinuxABI.hci_ptypetostr(UInt32(HCI_DM1) | UInt32(HCI_DH1))! + #expect(String(cString: str) == "DM1 DH1 ") + free(str) + + var value: UInt32 = 0 + let matched = "DM1,DH1".withCString { BluetoothLinuxABI.hci_strtoptype(UnsafeMutablePointer(mutating: $0), &value) } + #expect(matched == 1) + #expect(value == UInt32(HCI_DM1) | UInt32(HCI_DH1)) + } + + @Test func linkMode() { + // No "PERIPHERAL" prefix once HCI_LM_MASTER is set, but + // "CENTRAL" (the table entry HCI_LM_MASTER maps to) is still + // printed by the same bit-table loop. + let str = BluetoothLinuxABI.hci_lmtostr(UInt32(HCI_LM_MASTER))! + #expect(String(cString: str) == "CENTRAL ") + free(str) + + let peripheral = BluetoothLinuxABI.hci_lmtostr(0)! + #expect(String(cString: peripheral) == "PERIPHERAL ") + free(peripheral) + } + + @Test func commands() { + let str = BluetoothLinuxABI.hci_cmdtostr(0)! + #expect(String(cString: str) == "Inquiry") + free(str) + } + + @Test func version() { + let str = BluetoothLinuxABI.hci_vertostr(0x09)! + #expect(String(cString: str) == "5.0") + free(str) + + var ver: UInt32 = 0 + let matched = "5.0".withCString { BluetoothLinuxABI.hci_strtover(UnsafeMutablePointer(mutating: $0), &ver) } + #expect(matched == 1) + #expect(ver == 0x09) + } +} diff --git a/cmake/bluez.pc.in b/cmake/bluez.pc.in new file mode 100644 index 0000000..8b1da7a --- /dev/null +++ b/cmake/bluez.pc.in @@ -0,0 +1,10 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: bluez +Description: Bluetooth protocol stack for Linux +Version: @LIBBLUETOOTH_VERSION@ +Libs: -L${libdir} -lbluetooth +Cflags: -I${includedir} diff --git a/cmake/empty.c b/cmake/empty.c new file mode 100644 index 0000000..782b15f --- /dev/null +++ b/cmake/empty.c @@ -0,0 +1,9 @@ +/* + * Placeholder translation unit. + * + * CMake requires a shared library target to have at least one source of + * its own; every exported symbol actually arrives from the Swift and C + * static archives, linked whole (see CMakeLists.txt). + */ + +/* Deliberately empty. */ diff --git a/cmake/libbluetooth.map b/cmake/libbluetooth.map new file mode 100644 index 0000000..1c741c3 --- /dev/null +++ b/cmake/libbluetooth.map @@ -0,0 +1,245 @@ +/* + * Generated by scripts/gen_symbols.py — do not edit. + * + * The export surface of libbluetooth.so.3. Regenerate after + * editing scripts/symbols.txt: + * scripts/gen_symbols.py + */ + +LIBBLUETOOTH_5 { +global: + /* The reference export surface (218) */ + ba2oui; + ba2str; + ba2strlc; + bachk; + bafprintf; + baprintf; + basnprintf; + basprintf; + baswap; + batostr; + bt_compidtostr; + bt_error; + bt_free; + bt_malloc; + bt_malloc0; + hci_authenticate_link; + hci_bustostr; + hci_change_link_key; + hci_close_dev; + hci_cmdtostr; + hci_commandstostr; + hci_create_connection; + hci_delete_stored_link_key; + hci_devba; + hci_devid; + hci_devinfo; + hci_dflagstostr; + hci_disconnect; + hci_dtypetostr; + hci_encrypt_link; + hci_exit_park_mode; + hci_for_each_dev; + hci_get_route; + hci_inquiry; + hci_le_add_resolving_list; + hci_le_add_white_list; + hci_le_clear_resolving_list; + hci_le_clear_white_list; + hci_le_conn_update; + hci_le_create_conn; + hci_le_read_remote_features; + hci_le_read_resolving_list_size; + hci_le_read_white_list_size; + hci_le_rm_resolving_list; + hci_le_rm_white_list; + hci_le_set_address_resolution_enable; + hci_le_set_advertise_enable; + hci_le_set_scan_enable; + hci_le_set_scan_parameters; + hci_lmtostr; + hci_lptostr; + hci_open_dev; + hci_park_mode; + hci_ptypetostr; + hci_read_afh_map; + hci_read_afh_mode; + hci_read_bd_addr; + hci_read_class_of_dev; + hci_read_clock; + hci_read_clock_offset; + hci_read_current_iac_lap; + hci_read_ext_inquiry_response; + hci_read_inq_response_tx_power_level; + hci_read_inquiry_mode; + hci_read_inquiry_scan_type; + hci_read_inquiry_transmit_power_level; + hci_read_link_policy; + hci_read_link_quality; + hci_read_link_supervision_timeout; + hci_read_local_commands; + hci_read_local_ext_features; + hci_read_local_features; + hci_read_local_name; + hci_read_local_oob_data; + hci_read_local_version; + hci_read_remote_ext_features; + hci_read_remote_features; + hci_read_remote_name; + hci_read_remote_name_cancel; + hci_read_remote_name_with_clock_offset; + hci_read_remote_version; + hci_read_rssi; + hci_read_simple_pairing_mode; + hci_read_stored_link_key; + hci_read_transmit_power_level; + hci_read_voice_setting; + hci_scoptypetostr; + hci_send_cmd; + hci_send_req; + hci_set_afh_classification; + hci_strtolm; + hci_strtolp; + hci_strtoptype; + hci_strtoscoptype; + hci_strtover; + hci_switch_role; + hci_typetostr; + hci_vertostr; + hci_write_afh_mode; + hci_write_class_of_dev; + hci_write_current_iac_lap; + hci_write_ext_inquiry_response; + hci_write_inquiry_mode; + hci_write_inquiry_scan_type; + hci_write_inquiry_transmit_power_level; + hci_write_link_policy; + hci_write_link_supervision_timeout; + hci_write_local_name; + hci_write_simple_pairing_mode; + hci_write_stored_link_key; + hci_write_voice_setting; + lmp_featurestostr; + lmp_strtover; + lmp_vertostr; + pal_strtover; + pal_vertostr; + sdp_add_lang_attr; + sdp_append_to_buf; + sdp_append_to_pdu; + sdp_attr_add; + sdp_attr_add_new; + sdp_attr_remove; + sdp_attr_replace; + sdp_attrid_comp_func; + sdp_close; + sdp_connect; + sdp_copy_record; + sdp_create; + sdp_data_alloc; + sdp_data_alloc_with_length; + sdp_data_free; + sdp_data_get; + sdp_device_record_register; + sdp_device_record_register_binary; + sdp_device_record_unregister; + sdp_device_record_unregister_binary; + sdp_device_record_update; + sdp_device_record_update_binary; + sdp_extract_attr; + sdp_extract_pdu; + sdp_extract_seqtype; + sdp_gen_pdu; + sdp_gen_record_pdu; + sdp_gen_tid; + sdp_general_inquiry; + sdp_get_access_protos; + sdp_get_add_access_protos; + sdp_get_database_state; + sdp_get_error; + sdp_get_group_id; + sdp_get_int_attr; + sdp_get_lang_attr; + sdp_get_profile_descs; + sdp_get_proto_desc; + sdp_get_proto_port; + sdp_get_record_state; + sdp_get_server_ver; + sdp_get_service_avail; + sdp_get_service_id; + sdp_get_service_ttl; + sdp_get_socket; + sdp_get_string_attr; + sdp_get_supp_feat; + sdp_get_uuidseq_attr; + sdp_list_append; + sdp_list_free; + sdp_list_insert_sorted; + sdp_list_remove; + sdp_pattern_add_uuid; + sdp_pattern_add_uuidseq; + sdp_process; + sdp_profile_uuid2strn; + sdp_proto_uuid2strn; + sdp_record_alloc; + sdp_record_free; + sdp_record_print; + sdp_record_register; + sdp_record_unregister; + sdp_record_update; + sdp_send_req_w4_rsp; + sdp_seq_alloc; + sdp_seq_alloc_with_length; + sdp_seq_append; + sdp_service_attr_async; + sdp_service_attr_req; + sdp_service_search_async; + sdp_service_search_attr_async; + sdp_service_search_attr_req; + sdp_service_search_req; + sdp_set_access_protos; + sdp_set_add_access_protos; + sdp_set_attrid; + sdp_set_group_id; + sdp_set_info_attr; + sdp_set_lang_attr; + sdp_set_notify; + sdp_set_profile_descs; + sdp_set_seq_len; + sdp_set_service_id; + sdp_set_supp_feat; + sdp_set_url_attr; + sdp_set_uuidseq_attr; + sdp_svclass_uuid2strn; + sdp_uuid128_cmp; + sdp_uuid128_create; + sdp_uuid128_to_uuid; + sdp_uuid16_cmp; + sdp_uuid16_create; + sdp_uuid16_to_uuid128; + sdp_uuid2strn; + sdp_uuid32_create; + sdp_uuid32_to_uuid128; + sdp_uuid_cmp; + sdp_uuid_extract; + sdp_uuid_to_proto; + sdp_uuid_to_uuid128; + str2ba; + strtoba; + + /* Implemented by PureSwift but not exported by the reference (10) */ + bt_string_to_uuid; + bt_uuid128_create; + bt_uuid16_cmp; + bt_uuid16_create; + bt_uuid32_create; + bt_uuid_cmp; + bt_uuid_strcmp; + bt_uuid_to_le; + bt_uuid_to_string; + bt_uuid_to_uuid128; + +local: + *; +}; diff --git a/scripts/check-exports.sh b/scripts/check-exports.sh new file mode 100755 index 0000000..4c72fd9 --- /dev/null +++ b/scripts/check-exports.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# +# Assert a library's exported symbols against an expected list. +# +# Both directions fail: a symbol in the list but not in the library +# (something was never implemented, or the version script does not +# match), and a symbol in the library but not in the list (an internal +# detail leaked into the ABI surface). +# +# Usage: Scripts/check-exports.sh [symbols.txt] +# +set -euo pipefail + +LIBRARY="${1:?usage: check-exports.sh [symbols.txt]}" +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +EXPECTED_FILE="${2:-${ROOT}/scripts/exported.txt}" + +if [[ ! -f "${LIBRARY}" ]]; then + echo "error: ${LIBRARY} not found" >&2 + exit 1 +fi + +if [[ ! -f "${EXPECTED_FILE}" ]]; then + echo "error: ${EXPECTED_FILE} not found" >&2 + exit 1 +fi + +work="$(mktemp -d)" +trap 'rm -rf "${work}"' EXIT + +# Defined, exported functions and data — not undefined imports. +nm --dynamic --defined-only --format=posix "${LIBRARY}" \ + | awk '$2 ~ /^[TDBRWi]$/ { print $1 }' \ + | sed 's/@.*//' \ + | sort -u > "${work}/actual" + +grep -vE '^\s*(#|$)' "${EXPECTED_FILE}" | sort -u > "${work}/expected" + +comm -13 "${work}/actual" "${work}/expected" > "${work}/missing" +comm -23 "${work}/actual" "${work}/expected" > "${work}/extra" + +status=0 + +if [[ -s "${work}/missing" ]]; then + echo "error: $(wc -l < "${work}/missing") expected symbol(s) not exported by ${LIBRARY}:" >&2 + sed 's/^/ - /' "${work}/missing" >&2 + status=1 +fi + +if [[ -s "${work}/extra" ]]; then + echo "error: $(wc -l < "${work}/extra") unexpected symbol(s) exported by ${LIBRARY}:" >&2 + sed 's/^/ + /' "${work}/extra" >&2 + status=1 +fi + +if [[ "${status}" -eq 0 ]]; then + echo "exports: $(wc -l < "${work}/expected") symbols match $(basename "${EXPECTED_FILE}")" +fi + +exit "${status}" diff --git a/scripts/exported.txt b/scripts/exported.txt new file mode 100644 index 0000000..a1e3552 --- /dev/null +++ b/scripts/exported.txt @@ -0,0 +1,232 @@ +# Generated by scripts/gen_symbols.py — do not edit. +# +# Every symbol cmake/libbluetooth.map exports, flat, for +# scripts/check-exports.sh. +ba2oui +ba2str +ba2strlc +bachk +bafprintf +baprintf +basnprintf +basprintf +baswap +batostr +bt_compidtostr +bt_error +bt_free +bt_malloc +bt_malloc0 +bt_string_to_uuid +bt_uuid128_create +bt_uuid16_cmp +bt_uuid16_create +bt_uuid32_create +bt_uuid_cmp +bt_uuid_strcmp +bt_uuid_to_le +bt_uuid_to_string +bt_uuid_to_uuid128 +hci_authenticate_link +hci_bustostr +hci_change_link_key +hci_close_dev +hci_cmdtostr +hci_commandstostr +hci_create_connection +hci_delete_stored_link_key +hci_devba +hci_devid +hci_devinfo +hci_dflagstostr +hci_disconnect +hci_dtypetostr +hci_encrypt_link +hci_exit_park_mode +hci_for_each_dev +hci_get_route +hci_inquiry +hci_le_add_resolving_list +hci_le_add_white_list +hci_le_clear_resolving_list +hci_le_clear_white_list +hci_le_conn_update +hci_le_create_conn +hci_le_read_remote_features +hci_le_read_resolving_list_size +hci_le_read_white_list_size +hci_le_rm_resolving_list +hci_le_rm_white_list +hci_le_set_address_resolution_enable +hci_le_set_advertise_enable +hci_le_set_scan_enable +hci_le_set_scan_parameters +hci_lmtostr +hci_lptostr +hci_open_dev +hci_park_mode +hci_ptypetostr +hci_read_afh_map +hci_read_afh_mode +hci_read_bd_addr +hci_read_class_of_dev +hci_read_clock +hci_read_clock_offset +hci_read_current_iac_lap +hci_read_ext_inquiry_response +hci_read_inq_response_tx_power_level +hci_read_inquiry_mode +hci_read_inquiry_scan_type +hci_read_inquiry_transmit_power_level +hci_read_link_policy +hci_read_link_quality +hci_read_link_supervision_timeout +hci_read_local_commands +hci_read_local_ext_features +hci_read_local_features +hci_read_local_name +hci_read_local_oob_data +hci_read_local_version +hci_read_remote_ext_features +hci_read_remote_features +hci_read_remote_name +hci_read_remote_name_cancel +hci_read_remote_name_with_clock_offset +hci_read_remote_version +hci_read_rssi +hci_read_simple_pairing_mode +hci_read_stored_link_key +hci_read_transmit_power_level +hci_read_voice_setting +hci_scoptypetostr +hci_send_cmd +hci_send_req +hci_set_afh_classification +hci_strtolm +hci_strtolp +hci_strtoptype +hci_strtoscoptype +hci_strtover +hci_switch_role +hci_typetostr +hci_vertostr +hci_write_afh_mode +hci_write_class_of_dev +hci_write_current_iac_lap +hci_write_ext_inquiry_response +hci_write_inquiry_mode +hci_write_inquiry_scan_type +hci_write_inquiry_transmit_power_level +hci_write_link_policy +hci_write_link_supervision_timeout +hci_write_local_name +hci_write_simple_pairing_mode +hci_write_stored_link_key +hci_write_voice_setting +lmp_featurestostr +lmp_strtover +lmp_vertostr +pal_strtover +pal_vertostr +sdp_add_lang_attr +sdp_append_to_buf +sdp_append_to_pdu +sdp_attr_add +sdp_attr_add_new +sdp_attr_remove +sdp_attr_replace +sdp_attrid_comp_func +sdp_close +sdp_connect +sdp_copy_record +sdp_create +sdp_data_alloc +sdp_data_alloc_with_length +sdp_data_free +sdp_data_get +sdp_device_record_register +sdp_device_record_register_binary +sdp_device_record_unregister +sdp_device_record_unregister_binary +sdp_device_record_update +sdp_device_record_update_binary +sdp_extract_attr +sdp_extract_pdu +sdp_extract_seqtype +sdp_gen_pdu +sdp_gen_record_pdu +sdp_gen_tid +sdp_general_inquiry +sdp_get_access_protos +sdp_get_add_access_protos +sdp_get_database_state +sdp_get_error +sdp_get_group_id +sdp_get_int_attr +sdp_get_lang_attr +sdp_get_profile_descs +sdp_get_proto_desc +sdp_get_proto_port +sdp_get_record_state +sdp_get_server_ver +sdp_get_service_avail +sdp_get_service_id +sdp_get_service_ttl +sdp_get_socket +sdp_get_string_attr +sdp_get_supp_feat +sdp_get_uuidseq_attr +sdp_list_append +sdp_list_free +sdp_list_insert_sorted +sdp_list_remove +sdp_pattern_add_uuid +sdp_pattern_add_uuidseq +sdp_process +sdp_profile_uuid2strn +sdp_proto_uuid2strn +sdp_record_alloc +sdp_record_free +sdp_record_print +sdp_record_register +sdp_record_unregister +sdp_record_update +sdp_send_req_w4_rsp +sdp_seq_alloc +sdp_seq_alloc_with_length +sdp_seq_append +sdp_service_attr_async +sdp_service_attr_req +sdp_service_search_async +sdp_service_search_attr_async +sdp_service_search_attr_req +sdp_service_search_req +sdp_set_access_protos +sdp_set_add_access_protos +sdp_set_attrid +sdp_set_group_id +sdp_set_info_attr +sdp_set_lang_attr +sdp_set_notify +sdp_set_profile_descs +sdp_set_seq_len +sdp_set_service_id +sdp_set_supp_feat +sdp_set_url_attr +sdp_set_uuidseq_attr +sdp_svclass_uuid2strn +sdp_uuid128_cmp +sdp_uuid128_create +sdp_uuid128_to_uuid +sdp_uuid16_cmp +sdp_uuid16_create +sdp_uuid16_to_uuid128 +sdp_uuid2strn +sdp_uuid32_create +sdp_uuid32_to_uuid128 +sdp_uuid_cmp +sdp_uuid_extract +sdp_uuid_to_proto +sdp_uuid_to_uuid128 +str2ba +strtoba diff --git a/scripts/gen_stubs.py b/scripts/gen_stubs.py new file mode 100755 index 0000000..4a0e917 --- /dev/null +++ b/scripts/gen_stubs.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +""" +Generate a loudly-failing stub for every exported symbol that is not yet +implemented. + +This is what makes the port incremental. From the first commit the +library exports all 218 names the reference does, so it loads and +resolves against any consumer; every call that is not yet implemented +aborts with a message naming the symbol rather than silently returning +garbage. Each phase moves names from `symbols.txt` into +`implemented.txt` and the stub disappears. + +Getting that bookkeeping wrong is a build failure, not a subtle bug: a +name listed as implemented that is not produces an undefined symbol at +link time, and one left here after being implemented produces a +duplicate symbol. + +The signatures come from the vendored BlueZ headers, so each stub is +type-checked against the declaration it satisfies. + +Usage: + scripts/gen_stubs.py [output.c] + +Defaults to Sources/CBluetoothLinuxABI/gen/cbt_stubs.c. +""" + +import os +import re +import sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +INCLUDE = os.path.join( + ROOT, "Sources", "CBluetoothLinuxABI", "include", "bluetooth" +) +SYMBOLS = os.path.join(ROOT, "scripts", "symbols.txt") +IMPLEMENTED = os.path.join(ROOT, "scripts", "implemented.txt") +DEFAULT_OUTPUT = os.path.join( + ROOT, "Sources", "CBluetoothLinuxABI", "gen", "cbt_stubs.c" +) + +HEADERS = [ + "bluetooth.h", + "hci.h", + "hci_lib.h", + "sdp.h", + "sdp_lib.h", +] + +PROLOGUE = '''/* + * Generated by scripts/gen_stubs.py — do not edit. + * + * One stub per exported symbol that PureSwift has not implemented yet. + * Each aborts with the symbol name, so an unported call site is + * immediately identifiable rather than silently wrong. + * + * Regenerate after editing scripts/implemented.txt: + * scripts/gen_stubs.py + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +__attribute__((noreturn)) +static void cbt_unimplemented(const char *symbol) +{ +\tfprintf(stderr, +\t\t"libbluetooth (PureSwift): %s is not implemented yet.\\n", +\t\tsymbol); +\tabort(); +} + +''' + + +def read_list(path): + names = [] + with open(path, encoding="utf-8") as handle: + for line in handle: + line = line.split("#", 1)[0].strip() + if line: + names.append(line) + return names + + +def read_declarations(): + """Map symbol name -> (return type, parameter list) from the headers.""" + text = [] + for name in HEADERS: + path = os.path.join(INCLUDE, name) + with open(path, encoding="utf-8") as handle: + text.append(handle.read()) + source = "\n".join(text) + + # Strip comments so a declaration inside one is not picked up. + source = re.sub(r"/\*.*?\*/", " ", source, flags=re.DOTALL) + source = re.sub(r"//[^\n]*", " ", source) + + declarations = {} + # A non-static, non-inline function declaration ending in `;`. + pattern = re.compile( + r"(? 1 else DEFAULT_OUTPUT + + exported = read_list(SYMBOLS) + implemented = set(read_list(IMPLEMENTED)) + declarations = read_declarations() + + todo = [name for name in exported if name not in implemented] + + chunks = [PROLOGUE] + missing = [] + for name in todo: + declaration = declarations.get(name) + if declaration is None: + missing.append(name) + continue + return_type, parameters = declaration + separator = "" if return_type.endswith("*") else " " + chunks.append( + "%s%s%s(%s)\n{\n%s\n}\n\n" + % (return_type, separator, name, parameters, stub_body(name, return_type)) + ) + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + with open(output_path, "w", encoding="utf-8") as handle: + handle.write("".join(chunks)) + + print( + "wrote %s (%d stubs, %d implemented, %d exported)" + % ( + os.path.relpath(output_path, ROOT), + len(todo) - len(missing), + len(implemented), + len(exported), + ) + ) + + if missing: + # A symbol the reference exports but no public header declares + # cannot be stubbed with a checked signature. Report rather than + # guess: an unchecked stub would defeat the point of vendoring + # the headers. + print( + "error: %d exported symbol(s) have no declaration in the " + "vendored headers:" % len(missing), + file=sys.stderr, + ) + for name in missing: + print(" %s" % name, file=sys.stderr) + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/gen_symbols.py b/scripts/gen_symbols.py new file mode 100755 index 0000000..960b973 --- /dev/null +++ b/scripts/gen_symbols.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +""" +Generate the linker version script from symbols.txt. + +Upstream libbluetooth exports every symbol it defines and carries no +version script. We pin the list instead, so that the Swift runtime's +symbols and our own internals stay local and the export surface is +asserted rather than incidental. + +That is new behavior relative to the reference, and the risk it carries +is real: a consumer bound to something incidental that upstream happened +to export would stop resolving. Before this list is treated as final, +Debian's Contents index should be scanned for binaries with an undefined +symbol satisfied by libbluetooth.so.3. + +The `bt_uuid_*` family is added on top of symbols.txt: the reference +keeps those in libbluetooth-internal.a and exports none of them, but +PureSwift implements them and there is no reason to hide them. + +Usage: + scripts/gen_symbols.py [output.map] + +Defaults to cmake/libbluetooth.map. +""" + +import os +import sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +SYMBOLS = os.path.join(ROOT, "scripts", "symbols.txt") +DEFAULT_OUTPUT = os.path.join(ROOT, "cmake", "libbluetooth.map") + +# The flat list of everything the version script exports, for +# scripts/check-exports.sh to assert the built library against. +EXPORTED = os.path.join(ROOT, "scripts", "exported.txt") + +# Implemented by PureSwift/Bluetooth and exported deliberately, though +# the reference library does not export them. +EXTRA_SYMBOLS = [ + "bt_string_to_uuid", + "bt_uuid128_create", + "bt_uuid16_cmp", + "bt_uuid16_create", + "bt_uuid32_create", + "bt_uuid_cmp", + "bt_uuid_strcmp", + "bt_uuid_to_le", + "bt_uuid_to_string", + "bt_uuid_to_uuid128", +] + +VERSION_NODE = "LIBBLUETOOTH_5" + + +def read_list(path): + names = [] + with open(path, encoding="utf-8") as handle: + for line in handle: + line = line.split("#", 1)[0].strip() + if line: + names.append(line) + return names + + +def main(): + output_path = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_OUTPUT + + exported = read_list(SYMBOLS) + extra = [name for name in EXTRA_SYMBOLS if name not in exported] + + lines = [ + "/*", + " * Generated by scripts/gen_symbols.py — do not edit.", + " *", + " * The export surface of libbluetooth.so.3. Regenerate after", + " * editing scripts/symbols.txt:", + " * scripts/gen_symbols.py", + " */", + "", + "%s {" % VERSION_NODE, + "global:", + " /* The reference export surface (%d) */" % len(exported), + ] + lines += [" %s;" % name for name in sorted(exported)] + lines += [ + "", + " /* Implemented by PureSwift but not exported by the reference (%d) */" + % len(extra), + ] + lines += [" %s;" % name for name in sorted(extra)] + lines += [ + "", + "local:", + " *;", + "};", + "", + ] + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + with open(output_path, "w", encoding="utf-8") as handle: + handle.write("\n".join(lines)) + + with open(EXPORTED, "w", encoding="utf-8") as handle: + handle.write( + "# Generated by scripts/gen_symbols.py — do not edit.\n" + "#\n" + "# Every symbol cmake/libbluetooth.map exports, flat, for\n" + "# scripts/check-exports.sh.\n" + ) + handle.write("\n".join(sorted(exported + extra)) + "\n") + + print( + "wrote %s (%d symbols: %d reference + %d extra)" + % ( + os.path.relpath(output_path, ROOT), + len(exported) + len(extra), + len(exported), + len(extra), + ) + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/generate-hci-tables.py b/scripts/generate-hci-tables.py new file mode 100755 index 0000000..1d9d256 --- /dev/null +++ b/scripts/generate-hci-tables.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +""" +Generate Sources/BluetoothLinuxABI/gen/HCITables.swift from BlueZ's +lib/bluetooth/hci.c and lib/bluetooth/hci.h. + +The `hci_*tostr`/`hci_strto*` family (plus `lmp_*`/`pal_*`) are all +table lookups over BlueZ's `hci_map` arrays — name/bit-or-value pairs. +Hand-transcribing the largest of these (`commands_map`, 232 entries; +`lmp_features_map`, 8x9 entries) risks a silent transcription error +that would only surface as a wrong string for one specific bit. Since +every entry is mechanical (a literal name and a `#define`d value), +generating them from the vendored source is both faster and exact. + +Usage: + scripts/generate-hci-tables.py /lib/bluetooth/hci.c + +`hci.c` itself is not vendored into this repository — only the headers +are (see Sources/CBluetoothLinuxABI/README.md) — so this script takes +the reference source as an argument rather than reading a shipped copy. +Re-run after re-vendoring a newer hci.h from the same BlueZ release and +commit the generated result. +""" + +import os +import re +import sys + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +HCI_H = os.path.join(ROOT, "Sources", "CBluetoothLinuxABI", "include", "bluetooth", "hci.h") +OUTPUT = os.path.join(ROOT, "Sources", "BluetoothLinuxABI", "gen", "HCITables.swift") + +HEADER = '''// +// HCITables.swift +// BluetoothLinux +// +// Generated by scripts/generate-hci-tables.py — do not edit. +// +// Source: BlueZ lib/bluetooth/hci.c's `hci_map` tables, with `#define` +// values resolved against lib/bluetooth/hci.h. +// + +''' + + +def load_defines(): + text = open(HCI_H, encoding="utf-8").read() + defines = {} + + # `#define NAME value` where value is a simple integer literal + # (decimal or hex), skipping function-like macros and multi-token + # expressions. + for match in re.finditer( + r"^#define\s+([A-Za-z_][A-Za-z0-9_]*)\s+(0[xX][0-9A-Fa-f]+|\d+)\s*$", + text, + re.MULTILINE, + ): + name, value = match.groups() + defines[name] = int(value, 0) + + # `enum { A, B, C };` — unnamed enums with no explicit values, used + # for the HCI device flag bit positions (hci_test_bit indices, not + # bitmasks). Comments and blank lines inside the braces are allowed. + for match in re.finditer(r"enum\s*\{([^}]*)\}\s*;", text, re.DOTALL): + body = re.sub(r"/\*.*?\*/", "", match.group(1), flags=re.DOTALL) + names = [n.strip() for n in body.split(",")] + value = 0 + for name in names: + if not name or not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", name): + continue + defines.setdefault(name, value) + value += 1 + + return defines + + +def resolve(token, defines): + token = token.strip() + if re.fullmatch(r"0[xX][0-9A-Fa-f]+|\d+", token): + return int(token, 0) + if token in defines: + return defines[token] + raise ValueError("cannot resolve %r" % token) + + +def extract_map(source, name): + match = re.search( + r"static const hci_map %s\[\] = \{(.*?)\n\};" % re.escape(name), + source, + re.DOTALL, + ) + if not match: + raise ValueError("map not found: %s" % name) + body = match.group(1) + entries = re.findall(r'\{\s*"((?:[^"\\]|\\.)*)"\s*,\s*([^}]+?)\s*\}', body) + return entries + + +def extract_features_map(source): + match = re.search( + r"static const hci_map lmp_features_map\[8\]\[9\] = \{(.*?)\n\};", + source, + re.DOTALL, + ) + body = match.group(1) + bytes_ = re.findall(r"\{(.*?)\}(?:,|\s*$)", body, re.DOTALL) + # The outer split also grabs each byte's own `{ ... }` entries as + # nested matches; re-split more carefully by top-level byte blocks. + byte_blocks = re.findall(r"\{\s*/\* Byte \d+ \*/(.*?)\n\t\},?", body, re.DOTALL) + rows = [] + for block in byte_blocks: + entries = re.findall(r'\{\s*"((?:[^"\\]|\\.)*)"\s*,\s*([^}]+?)\s*\}', block) + rows.append(entries) + return rows + + +def swift_escape(text): + return text.replace("\\", "\\\\").replace('"', '\\"') + + +def format_table(name, entries, defines, comment): + lines = ["/// %s" % comment] + lines.append("internal let %s: [(name: String, value: UInt32)] = [" % name) + for s, v in entries: + value = resolve(v, defines) + lines.append(' ("%s", 0x%x),' % (swift_escape(s), value)) + lines.append("]") + return "\n".join(lines) + "\n\n" + + +def main(): + if len(sys.argv) != 2: + print("usage: generate-hci-tables.py ", file=sys.stderr) + return 1 + source = open(sys.argv[1], encoding="utf-8").read() + defines = load_defines() + + chunks = [HEADER] + + tables = [ + ("hciDeviceFlagsMap", "dev_flags_map", "HCI device flags (bit tests)."), + ("hciPacketTypeMap", "pkt_type_map", "ACL packet types (bitmask)."), + ("hciSCOPacketTypeMap", "sco_ptype_map", "SCO packet types (bitmask)."), + ("hciLinkPolicyMap", "link_policy_map", "Link policy settings (bitmask)."), + ("hciLinkModeMap", "link_mode_map", "Link mode settings (bitmask)."), + ("hciVersionMap", "ver_map", "Core specification version (exact match)."), + ("hciPALVersionMap", "pal_map", "802.11 PAL version (exact match)."), + ] + for swift_name, c_name, comment in tables: + entries = extract_map(source, c_name) + chunks.append(format_table(swift_name, entries, defines, comment)) + + commands = extract_map(source, "commands_map") + chunks.append( + format_table( + "hciCommandsMap", + commands, + defines, + "Supported Commands bit -> name (%d entries)." % len(commands), + ) + ) + + feature_rows = extract_features_map(source) + chunks.append("/// LMP Features page 0, one array per byte (8 bytes x up to 9 bits).\n") + chunks.append("internal let lmpFeaturesMap: [[(name: String, value: UInt8)]] = [\n") + for row in feature_rows: + chunks.append(" [\n") + for s, v in row: + value = resolve(v, defines) + chunks.append(' ("%s", 0x%02x),\n' % (swift_escape(s), value)) + chunks.append(" ],\n") + chunks.append("]\n") + + os.makedirs(os.path.dirname(OUTPUT), exist_ok=True) + with open(OUTPUT, "w", encoding="utf-8") as handle: + handle.write("".join(chunks)) + + print( + "wrote %s (%d command entries, %d feature bytes)" + % (os.path.relpath(OUTPUT, ROOT), len(commands), len(feature_rows)) + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/implemented.txt b/scripts/implemented.txt new file mode 100644 index 0000000..0a95975 --- /dev/null +++ b/scripts/implemented.txt @@ -0,0 +1,233 @@ +# Symbols already implemented, and therefore NOT stubbed. +# +# scripts/gen_stubs.py subtracts this list from symbols.txt. Moving a +# name here without providing an implementation is a link error at the +# next build (undefined symbol); leaving one here after implementing it +# is a duplicate-symbol error. Either way the mistake is loud. +# +# Each phase of the port moves names from stubbed to implemented and +# watches the conformance diff shrink. + +# --- Phase 1: PureSwift/Bluetooth, Sources/BluetoothABI + Sources/CBluetooth --- +# The bluetooth.c entry points (17). +ba2oui +ba2str +ba2strlc +bachk +bafprintf +baprintf +basnprintf +basprintf +baswap +batostr +bt_compidtostr +bt_error +bt_free +bt_malloc +bt_malloc0 +str2ba +strtoba + +# The bt_uuid_* family (10). Note these are NOT in symbols.txt: the +# reference keeps them in libbluetooth-internal.a and exports none of +# them. We export them anyway — see cmake/libbluetooth.map. +bt_string_to_uuid +bt_uuid128_create +bt_uuid16_cmp +bt_uuid16_create +bt_uuid32_create +bt_uuid_cmp +bt_uuid_strcmp +bt_uuid_to_le +bt_uuid_to_string +bt_uuid_to_uuid128 + +# The pure (non-socket) sdp.c symbols (75), implemented in +# PureSwift/Bluetooth's Sources/BluetoothSDP. +sdp_add_lang_attr +sdp_append_to_buf +sdp_append_to_pdu +sdp_attr_add +sdp_attr_add_new +sdp_attr_remove +sdp_attr_replace +sdp_attrid_comp_func +sdp_copy_record +sdp_data_alloc +sdp_data_alloc_with_length +sdp_data_free +sdp_data_get +sdp_extract_attr +sdp_extract_pdu +sdp_extract_seqtype +sdp_gen_pdu +sdp_gen_record_pdu +sdp_get_access_protos +sdp_get_add_access_protos +sdp_get_database_state +sdp_get_group_id +sdp_get_int_attr +sdp_get_lang_attr +sdp_get_profile_descs +sdp_get_proto_desc +sdp_get_proto_port +sdp_get_record_state +sdp_get_server_ver +sdp_get_service_avail +sdp_get_service_id +sdp_get_service_ttl +sdp_get_string_attr +sdp_get_supp_feat +sdp_get_uuidseq_attr +sdp_list_append +sdp_list_free +sdp_list_insert_sorted +sdp_list_remove +sdp_pattern_add_uuid +sdp_pattern_add_uuidseq +sdp_profile_uuid2strn +sdp_proto_uuid2strn +sdp_record_alloc +sdp_record_free +sdp_record_print +sdp_seq_alloc +sdp_seq_alloc_with_length +sdp_seq_append +sdp_set_access_protos +sdp_set_add_access_protos +sdp_set_attrid +sdp_set_group_id +sdp_set_info_attr +sdp_set_lang_attr +sdp_set_profile_descs +sdp_set_seq_len +sdp_set_service_id +sdp_set_supp_feat +sdp_set_url_attr +sdp_set_uuidseq_attr +sdp_svclass_uuid2strn +sdp_uuid128_cmp +sdp_uuid128_create +sdp_uuid128_to_uuid +sdp_uuid16_cmp +sdp_uuid16_create +sdp_uuid16_to_uuid128 +sdp_uuid2strn +sdp_uuid32_create +sdp_uuid32_to_uuid128 +sdp_uuid_cmp +sdp_uuid_extract +sdp_uuid_to_proto +sdp_uuid_to_uuid128 + +# HCI string converters (21), implemented in +# Sources/BluetoothLinuxABI/HCIStrings.swift. +hci_bustostr +hci_cmdtostr +hci_commandstostr +hci_dflagstostr +hci_dtypetostr +hci_lmtostr +hci_lptostr +hci_ptypetostr +hci_scoptypetostr +hci_strtolm +hci_strtolp +hci_strtoptype +hci_strtoscoptype +hci_strtover +hci_typetostr +hci_vertostr +lmp_featurestostr +lmp_strtover +lmp_vertostr +pal_strtover +pal_vertostr + +# HCI device management (9), implemented in +# Sources/BluetoothLinuxABI/HCIDevice.swift. +hci_open_dev +hci_close_dev +hci_devid +hci_devinfo +hci_devba +hci_for_each_dev +hci_get_route +hci_send_cmd +hci_send_req + +# HCI command wrappers (71), implemented in +# Sources/BluetoothLinuxABI/HCICommandsConnection.swift, +# HCICommandsRemote.swift, HCICommandsInfo.swift, HCICommandsConfig.swift, +# HCICommandsLE.swift, and HCIInquiry.swift. +hci_authenticate_link +hci_change_link_key +hci_create_connection +hci_delete_stored_link_key +hci_disconnect +hci_encrypt_link +hci_exit_park_mode +hci_inquiry +hci_le_add_resolving_list +hci_le_add_white_list +hci_le_clear_resolving_list +hci_le_clear_white_list +hci_le_conn_update +hci_le_create_conn +hci_le_read_remote_features +hci_le_read_resolving_list_size +hci_le_read_white_list_size +hci_le_rm_resolving_list +hci_le_rm_white_list +hci_le_set_address_resolution_enable +hci_le_set_advertise_enable +hci_le_set_scan_enable +hci_le_set_scan_parameters +hci_park_mode +hci_read_afh_map +hci_read_afh_mode +hci_read_bd_addr +hci_read_class_of_dev +hci_read_clock +hci_read_clock_offset +hci_read_current_iac_lap +hci_read_ext_inquiry_response +hci_read_inq_response_tx_power_level +hci_read_inquiry_mode +hci_read_inquiry_scan_type +hci_read_inquiry_transmit_power_level +hci_read_link_policy +hci_read_link_quality +hci_read_link_supervision_timeout +hci_read_local_commands +hci_read_local_ext_features +hci_read_local_features +hci_read_local_name +hci_read_local_oob_data +hci_read_local_version +hci_read_remote_ext_features +hci_read_remote_features +hci_read_remote_name +hci_read_remote_name_cancel +hci_read_remote_name_with_clock_offset +hci_read_remote_version +hci_read_rssi +hci_read_simple_pairing_mode +hci_read_stored_link_key +hci_read_transmit_power_level +hci_read_voice_setting +hci_set_afh_classification +hci_switch_role +hci_write_afh_mode +hci_write_class_of_dev +hci_write_current_iac_lap +hci_write_ext_inquiry_response +hci_write_inquiry_mode +hci_write_inquiry_scan_type +hci_write_inquiry_transmit_power_level +hci_write_link_policy +hci_write_link_supervision_timeout +hci_write_local_name +hci_write_simple_pairing_mode +hci_write_stored_link_key +hci_write_voice_setting diff --git a/scripts/ownership.md b/scripts/ownership.md new file mode 100644 index 0000000..b8d45f9 --- /dev/null +++ b/scripts/ownership.md @@ -0,0 +1,74 @@ +# Ownership audit + +What each exported symbol returns, who frees it, and how long it lives. + +This table has to be filled in **before** a symbol is implemented, not +after: the return-value conventions in `libbluetooth` are not uniform, +and guessing produces leaks or double frees that no type checker +catches. Three conventions are already known to coexist: + +- Caller-owned heap: `batostr` returns an 18-byte `bt_malloc` buffer the + caller releases with `bt_free`; `strtoba` likewise. +- Caller-provided buffer: `ba2str`, `ba2strlc`, `ba2oui` and + `bt_uuid_to_string` write into a buffer the caller supplies and return + a length (or, for the last, a status). +- Static lifetime: `bt_compidtostr` returns a pointer into static + storage that must never be freed. The `hci_*tostr` family is mixed — + each one needs checking individually. + +And two distinct error conventions: + +- The UUID family returns `0` on success and `-EINVAL` on failure, with + `errno` untouched. `bt_uuid16_cmp` inverts even that: `1` for equal, + `0` for not equal *and* for a NULL or non-16-bit argument. +- The socket family returns `-1` and sets `errno`. + +## Status + +| Family | Symbols | Audited | +|---|---:|---| +| `bluetooth.c` | 17 | ✅ complete — implemented in PureSwift/Bluetooth | +| `bt_uuid_*` | 10 | ✅ complete — implemented in PureSwift/Bluetooth | +| `hci.c` | 101 | ❌ not started | +| `sdp.c` | 100 | ❌ not started | + +## bluetooth.c + +| Symbol | Returns | Who frees | Lifetime | +|---|---|---|---| +| `baswap` | `void` | — | writes through `dst` | +| `batostr` | `char *` (18 bytes) | caller, via `bt_free` | heap | +| `strtoba` | `bdaddr_t *` | caller, via `bt_free` | heap | +| `ba2str` | `int` (always 17) | — | writes into caller buffer, ≥18 bytes | +| `ba2strlc` | `int` (always 17) | — | writes into caller buffer, ≥18 bytes | +| `str2ba` | `int` (`0` / `-1`) | — | writes through `ba`; zeroes it on failure | +| `ba2oui` | `int` (always 8) | — | writes into caller buffer, ≥9 bytes | +| `bachk` | `int` (`0` / `-1`) | — | — | +| `baprintf` | `int` (chars written) | — | — | +| `bafprintf` | `int` (chars written) | — | — | +| `basprintf` | `int` (chars written) | — | writes into caller buffer, unbounded | +| `basnprintf` | `int` (chars *needed*) | — | writes into caller buffer, bounded | +| `bt_malloc` | `void *` | caller, via `bt_free` | heap | +| `bt_malloc0` | `void *` (zeroed) | caller, via `bt_free` | heap | +| `bt_free` | `void` | — | accepts NULL | +| `bt_error` | `int` (errno value) | — | — | +| `bt_compidtostr` | `const char *` | **nobody** | static | + +Note `basprintf` writes without a bound (the reference passes +`(~0U) >> 1` as the size) and `basnprintf` returns the length the +formatted string *would* have needed, not the length written — both +inherited straight from `vsnprintf`. + +## hci.c + +Not yet audited. 101 symbols. The `hci_*tostr` family is the part that +needs the most care: the return lifetimes are mixed within it. + +## sdp.c + +Not yet audited. 100 symbols, and the highest-risk group in the port: +`sdp_data_alloc` / `sdp_data_free` / `sdp_seq_alloc` and the intrusive +`sdp_list_t` are not really an API but an ownership contract callers +depend on, including recursive-free semantics. A defensible fallback is +to keep the reference allocator and list code as vendored C and move +only the codec to Swift. diff --git a/scripts/symbols.txt b/scripts/symbols.txt new file mode 100644 index 0000000..8315d12 --- /dev/null +++ b/scripts/symbols.txt @@ -0,0 +1,233 @@ +# The complete export surface of libbluetooth.so.3, taken from the +# reference library (BlueZ 5.82, soname libbluetooth.so.3). +# +# scripts/check-exports.sh asserts the built library against this list; +# both a missing and an extra symbol fail. scripts/gen_stubs.py emits a +# loudly-failing stub for every name here that is not yet in +# implemented.txt, so the library is droppable before it is finished. +# +# `#` comments and blank lines are ignored. + +# --- bluetooth.c (17) — implemented in PureSwift/Bluetooth --- +ba2oui +ba2str +ba2strlc +bachk +bafprintf +baprintf +basnprintf +basprintf +baswap +batostr +bt_compidtostr +bt_error +bt_free +bt_malloc +bt_malloc0 +str2ba +strtoba + +# --- hci.c (101) --- +hci_authenticate_link +hci_bustostr +hci_change_link_key +hci_close_dev +hci_cmdtostr +hci_commandstostr +hci_create_connection +hci_delete_stored_link_key +hci_devba +hci_devid +hci_devinfo +hci_dflagstostr +hci_disconnect +hci_dtypetostr +hci_encrypt_link +hci_exit_park_mode +hci_for_each_dev +hci_get_route +hci_inquiry +hci_le_add_resolving_list +hci_le_add_white_list +hci_le_clear_resolving_list +hci_le_clear_white_list +hci_le_conn_update +hci_le_create_conn +hci_le_read_remote_features +hci_le_read_resolving_list_size +hci_le_read_white_list_size +hci_le_rm_resolving_list +hci_le_rm_white_list +hci_le_set_address_resolution_enable +hci_le_set_advertise_enable +hci_le_set_scan_enable +hci_le_set_scan_parameters +hci_lmtostr +hci_lptostr +hci_open_dev +hci_park_mode +hci_ptypetostr +hci_read_afh_map +hci_read_afh_mode +hci_read_bd_addr +hci_read_class_of_dev +hci_read_clock +hci_read_clock_offset +hci_read_current_iac_lap +hci_read_ext_inquiry_response +hci_read_inq_response_tx_power_level +hci_read_inquiry_mode +hci_read_inquiry_scan_type +hci_read_inquiry_transmit_power_level +hci_read_link_policy +hci_read_link_quality +hci_read_link_supervision_timeout +hci_read_local_commands +hci_read_local_ext_features +hci_read_local_features +hci_read_local_name +hci_read_local_oob_data +hci_read_local_version +hci_read_remote_ext_features +hci_read_remote_features +hci_read_remote_name +hci_read_remote_name_cancel +hci_read_remote_name_with_clock_offset +hci_read_remote_version +hci_read_rssi +hci_read_simple_pairing_mode +hci_read_stored_link_key +hci_read_transmit_power_level +hci_read_voice_setting +hci_scoptypetostr +hci_send_cmd +hci_send_req +hci_set_afh_classification +hci_strtolm +hci_strtolp +hci_strtoptype +hci_strtoscoptype +hci_strtover +hci_switch_role +hci_typetostr +hci_vertostr +hci_write_afh_mode +hci_write_class_of_dev +hci_write_current_iac_lap +hci_write_ext_inquiry_response +hci_write_inquiry_mode +hci_write_inquiry_scan_type +hci_write_inquiry_transmit_power_level +hci_write_link_policy +hci_write_link_supervision_timeout +hci_write_local_name +hci_write_simple_pairing_mode +hci_write_stored_link_key +hci_write_voice_setting +lmp_featurestostr +lmp_strtover +lmp_vertostr +pal_strtover +pal_vertostr + +# --- sdp.c (100) --- +sdp_add_lang_attr +sdp_append_to_buf +sdp_append_to_pdu +sdp_attr_add +sdp_attr_add_new +sdp_attr_remove +sdp_attr_replace +sdp_attrid_comp_func +sdp_close +sdp_connect +sdp_copy_record +sdp_create +sdp_data_alloc +sdp_data_alloc_with_length +sdp_data_free +sdp_data_get +sdp_device_record_register +sdp_device_record_register_binary +sdp_device_record_unregister +sdp_device_record_unregister_binary +sdp_device_record_update +sdp_device_record_update_binary +sdp_extract_attr +sdp_extract_pdu +sdp_extract_seqtype +sdp_gen_pdu +sdp_gen_record_pdu +sdp_gen_tid +sdp_general_inquiry +sdp_get_access_protos +sdp_get_add_access_protos +sdp_get_database_state +sdp_get_error +sdp_get_group_id +sdp_get_int_attr +sdp_get_lang_attr +sdp_get_profile_descs +sdp_get_proto_desc +sdp_get_proto_port +sdp_get_record_state +sdp_get_server_ver +sdp_get_service_avail +sdp_get_service_id +sdp_get_service_ttl +sdp_get_socket +sdp_get_string_attr +sdp_get_supp_feat +sdp_get_uuidseq_attr +sdp_list_append +sdp_list_free +sdp_list_insert_sorted +sdp_list_remove +sdp_pattern_add_uuid +sdp_pattern_add_uuidseq +sdp_process +sdp_profile_uuid2strn +sdp_proto_uuid2strn +sdp_record_alloc +sdp_record_free +sdp_record_print +sdp_record_register +sdp_record_unregister +sdp_record_update +sdp_send_req_w4_rsp +sdp_seq_alloc +sdp_seq_alloc_with_length +sdp_seq_append +sdp_service_attr_async +sdp_service_attr_req +sdp_service_search_async +sdp_service_search_attr_async +sdp_service_search_attr_req +sdp_service_search_req +sdp_set_access_protos +sdp_set_add_access_protos +sdp_set_attrid +sdp_set_group_id +sdp_set_info_attr +sdp_set_lang_attr +sdp_set_notify +sdp_set_profile_descs +sdp_set_seq_len +sdp_set_service_id +sdp_set_supp_feat +sdp_set_url_attr +sdp_set_uuidseq_attr +sdp_svclass_uuid2strn +sdp_uuid128_cmp +sdp_uuid128_create +sdp_uuid128_to_uuid +sdp_uuid16_cmp +sdp_uuid16_create +sdp_uuid16_to_uuid128 +sdp_uuid2strn +sdp_uuid32_create +sdp_uuid32_to_uuid128 +sdp_uuid_cmp +sdp_uuid_extract +sdp_uuid_to_proto +sdp_uuid_to_uuid128