freebsd-netgraph: FreeBSD port that accesses ng_hci node

This commit is contained in:
Matthias Ringwald 2023-11-07 09:57:44 +01:00
parent 33c74df1cc
commit 22029648db
6 changed files with 1003 additions and 0 deletions

View File

@ -0,0 +1,206 @@
cmake_minimum_required (VERSION 3.18)
project(BTstack-posix-h4)
SET(BTSTACK_ROOT ${CMAKE_SOURCE_DIR}/../..)
# extra compiler warnings
if ("${CMAKE_C_COMPILER_ID}" MATCHES ".*Clang.*")
# using Clang
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-variable -Wswitch-default -Werror")
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
# using GCC
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-but-set-variable -Wunused-variable -Wswitch-default -Werror")
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel")
# using Intel C++
elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
# using Visual Studio C++
endif()
# to generate .h from .gatt files
find_package (Python REQUIRED COMPONENTS Interpreter)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# local dir for btstack_config.h after build dir to avoid using .h from Makefile
include_directories(.)
include_directories(${BTSTACK_ROOT}/3rd-party/micro-ecc)
include_directories(${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/include)
include_directories(${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/include)
include_directories(${BTSTACK_ROOT}/3rd-party/lc3-google/include)
include_directories(${BTSTACK_ROOT}/3rd-party/md5)
include_directories(${BTSTACK_ROOT}/3rd-party/hxcmod-player)
include_directories(${BTSTACK_ROOT}/3rd-party/hxcmod-player/mod)
include_directories(${BTSTACK_ROOT}/3rd-party/lwip/core/src/include)
include_directories(${BTSTACK_ROOT}/3rd-party/lwip/dhcp-server)
include_directories(${BTSTACK_ROOT}/3rd-party/rijndael)
include_directories(${BTSTACK_ROOT}/3rd-party/yxml)
include_directories(${BTSTACK_ROOT}/3rd-party/tinydir)
include_directories(${BTSTACK_ROOT}/src)
include_directories(${BTSTACK_ROOT}/chipset/bcm)
include_directories(${BTSTACK_ROOT}/chipset/csr)
include_directories(${BTSTACK_ROOT}/chipset/cc256x)
include_directories(${BTSTACK_ROOT}/chipset/em9301)
include_directories(${BTSTACK_ROOT}/chipset/realtek)
include_directories(${BTSTACK_ROOT}/chipset/tc3566x)
include_directories(${BTSTACK_ROOT}/chipset/stlc2500d)
include_directories(${BTSTACK_ROOT}/chipset/zephyr)
include_directories(${BTSTACK_ROOT}/platform/embedded)
include_directories(${BTSTACK_ROOT}/platform/lwip)
include_directories(${BTSTACK_ROOT}/platform/lwip/port)
include_directories(${BTSTACK_ROOT}/platform/posix)
file(GLOB SOURCES_SRC "${BTSTACK_ROOT}/src/*.c" "${BTSTACK_ROOT}/example/sco_demo_util.c")
file(GLOB SOURCES_BLE "${BTSTACK_ROOT}/src/ble/*.c")
file(GLOB SOURCES_BLUEDROID "${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/srce/*.c" "${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/srce/*.c")
file(GLOB SOURCES_CLASSIC "${BTSTACK_ROOT}/src/classic/*.c")
file(GLOB SOURCES_MESH "${BTSTACK_ROOT}/src/mesh/*.c")
file(GLOB SOURCES_GATT "${BTSTACK_ROOT}/src/ble/gatt-service/*.c")
file(GLOB SOURCES_UECC "${BTSTACK_ROOT}/3rd-party/micro-ecc/uECC.c")
file(GLOB SOURCES_HXCMOD "${BTSTACK_ROOT}/3rd-party/hxcmod-player/*.c" "${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods/*.c")
file(GLOB SOURCES_MD5 "${BTSTACK_ROOT}/3rd-party/md5/md5.c")
file(GLOB SOURCES_RIJNDAEL "${BTSTACK_ROOT}/3rd-party/rijndael/rijndael.c")
file(GLOB SOURCES_YXML "${BTSTACK_ROOT}/3rd-party/yxml/yxml.c")
file(GLOB SOURCES_POSIX "${BTSTACK_ROOT}/platform/posix/*.c")
file(GLOB SOURCES_BCM "${BTSTACK_ROOT}/chipset/bcm/*.c")
file(GLOB SOURCES_CSR "${BTSTACK_ROOT}/chipset/csr/*.c")
file(GLOB SOURCES_EM9301 "${BTSTACK_ROOT}/chipset/em9301/*.c")
file(GLOB SOURCES_STLC2500D "${BTSTACK_ROOT}/chipset/stlc2500d/*.c")
file(GLOB SOURCES_TC2566X "${BTSTACK_ROOT}/chipset/tc3566x/*.c")
file(GLOB SOURCES_REALTEK "${BTSTACK_ROOT}/chipset/realtek/*.c")
file(GLOB SOURCES_ZEPHYR "${BTSTACK_ROOT}/chipset/zephyr/*.c")
file(GLOB SOURCES_LC3_GOOGLE "${BTSTACK_ROOT}/3rd-party/lc3-google/src/*.c")
file(GLOB SOURCES_PORT "*.c")
set(LWIP_CORE_SRC
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/def.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/inet_chksum.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/init.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ip.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/mem.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/memp.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/netif.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/pbuf.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/tcp.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/tcp_in.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/tcp_out.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/timeouts.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/udp.c
)
set (LWIP_IPV4_SRC
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv4/acd.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv4/dhcp.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv4/etharp.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv4/icmp.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv4/ip4.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv4/ip4_addr.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv4/ip4_frag.c
)
set (LWIP_NETIF_SRC
${BTSTACK_ROOT}/3rd-party/lwip/core/src/netif/ethernet.c
)
set (LWIP_HTTPD
${BTSTACK_ROOT}/3rd-party/lwip/core/src/apps/http/altcp_proxyconnect.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/apps/http/fs.c
${BTSTACK_ROOT}/3rd-party/lwip/core/src/apps/http/httpd.c
)
set (LWIP_DHCPD
${BTSTACK_ROOT}/3rd-party/lwip/dhcp-server/dhserver.c
)
set (LWIP_PORT
${BTSTACK_ROOT}/platform/lwip/port/sys_arch.c
${BTSTACK_ROOT}//platform/lwip/bnep_lwip.c
)
set (SOURCES_LWIP ${LWIP_CORE_SRC} ${LWIP_IPV4_SRC} ${LWIP_NETIF_SRC} ${LWIP_HTTPD} ${LWIP_DHCPD} ${LWIP_PORT})
file(GLOB SOURCES_BLE_OFF "${BTSTACK_ROOT}/src/ble/le_device_db_memory.c")
list(REMOVE_ITEM SOURCES_BLE ${SOURCES_BLE_OFF})
file(GLOB SOURCES_POSIX_OFF "${BTSTACK_ROOT}/platform/posix/le_device_db_fs.c")
list(REMOVE_ITEM SOURCES_POSIX ${SOURCES_POSIX_OFF})
set(SOURCES
${SOURCES_BLE}
${SOURCES_BCM}
${SOURCES_BLUEDROID}
${SOURCES_CLASSIC}
${SOURCES_CSR}
${SOURCES_EM9301}
${SOURCES_GATT}
${SOURCES_HXCMOD}
${SOURCES_HXCMOD}
${SOURCES_LIBUSB}
${SOURCES_MD5}
${SOURCES_MESH}
${SOURCES_PORT}
${SOURCES_REALTEK}
${SOURCES_RIJNDAEL}
${SOURCES_SRC}
${SOURCES_CSR}
${SOURCES_STLC2500D}
${SOURCES_TC2566X}
${SOURCES_UECC}
${SOURCES_POSIX}
${SOURCES_YXML}
${SOURCES_ZEPHYR}
)
list(SORT SOURCES)
# create static lib
add_library(btstack STATIC ${SOURCES})
# pkgconfig
find_package(PkgConfig)
# portaudio
if (PkgConfig_FOUND)
pkg_check_modules(PORTAUDIO portaudio-2.0)
if(PORTAUDIO_FOUND)
message("HAVE_PORTAUDIO")
include_directories(${PORTAUDIO_INCLUDE_DIRS})
link_directories(${PORTAUDIO_LIBRARY_DIRS})
link_libraries(${PORTAUDIO_LIBRARIES})
add_compile_definitions(HAVE_PORTAUDIO)
endif()
endif()
# pthread
find_package(Threads)
link_libraries(${CMAKE_THREAD_LIBS_INIT})
# Add CC256x Support and specify init script
# set (CC256X_INIT_SCRIPT bluetooth_init_cc2564C_1.5.c)
# include(${BTSTACK_ROOT}/chipset/cc256x/cc256x.cmake)
# Add BCM Support and download PatchRAM files
# include(${BTSTACK_ROOT}/chipset/bcm/bcm.cmake)
# get list of examples, skipping mesh_node_demo
include(../../example/CMakeLists.txt)
set (EXAMPLES ${EXAMPLES_GENERAL} ${EXAMPLES_CLASSIC_ONLY} ${EXAMPLES_LE_ONLY} ${EXAMPLES_DUAL_MODE})
list(REMOVE_DUPLICATES EXAMPLES)
list(REMOVE_ITEM EXAMPLES "mesh_node_demo")
# create targets
foreach(EXAMPLE ${EXAMPLES})
# get c file
set (SOURCES_EXAMPLE ${BTSTACK_ROOT}/example/${EXAMPLE}.c)
# add GATT DB creation
if ( "${EXAMPLES_GATT_FILES}" MATCHES ${EXAMPLE} )
message("example ${EXAMPLE} -- with GATT DB")
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE}.h
DEPENDS ${BTSTACK_ROOT}/example/${EXAMPLE}.gatt
COMMAND ${Python_EXECUTABLE}
ARGS ${BTSTACK_ROOT}/tool/compile_gatt.py ${BTSTACK_ROOT}/example/${EXAMPLE}.gatt ${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE}.h
)
list(APPEND SOURCES_EXAMPLE ${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE}.h)
else()
message("example ${EXAMPLE}")
endif()
add_executable(${EXAMPLE} ${SOURCES_EXAMPLE})
target_link_libraries(${EXAMPLE} btstack pthread netgraph)
endforeach(EXAMPLE)

View File

@ -0,0 +1,44 @@
# BTstack Port for FreeBSD Systems
## Overview
This port assumes that FreeBSD provides an ng_hci netgraph node for a connected Bluetooth Controller.
In most cases, these are Bluetooth USB dongles or built-in Bluetooth Controller connected via USB.
For Bluetooth Controllers connected via UART, the POSX-H4 port might be a better option als
## Implementation details
In FreeBSD 13.2, the hci node is connected to a l2cap node and a btsock_hci_raw node. In order to take control, this
port create a custom netgraph ng_socket node and connect to the 'acl' and 'raw' hooks of the hci node. The OS Bluetooth
functionality will be interrupted.
## Compilation
BTstack's FeeeBSD port does not have additional dependencies. To compile the cmake project with make
mkdir build
cd build
cmake ..
make
or using Ninja:
mkdir ninja
cd ninja
cmake ..
ninja
## Running the examples
As the port needs to reconfigure the Bluetooth netgraph node, it needs to run with root privileges.
It tries to connect to 'ubt0hci' by default. If your Bluetooth Controller is different, you can select it with '-u node'
On start, BTstack prints the path to the packet log and prints the information on the detected Buetooth Controller.
$ sudo gatt_counter
Packet Log: /tmp/hci_dump.pklg
BTstack counter 0001
BTstack up and running on 00:1A:7D:DA:71:13.
## ToDO
- drop privileges after startup
- auto-detect ng_hci node
- support for profiles that require SCO: HFP & HSP

View File

@ -0,0 +1,67 @@
//
// btstack_config.h for generic POSIX H4 port
//
// Documentation: https://bluekitchen-gmbh.com/btstack/#how_to/
//
#ifndef BTSTACK_CONFIG_H
#define BTSTACK_CONFIG_H
// Port related features
#define HAVE_ASSERT
#define HAVE_BTSTACK_STDIN
#define HAVE_EM9304_PATCH_CONTAINER
#define HAVE_MALLOC
#define HAVE_POSIX_FILE_IO
#define HAVE_POSIX_TIME
// BTstack features that can be enabled
#define ENABLE_ATT_DELAYED_RESPONSE
#define ENABLE_AVRCP_COVER_ART
#define ENABLE_BLE
#define ENABLE_CLASSIC
#define ENABLE_CROSS_TRANSPORT_KEY_DERIVATION
#define ENABLE_GOEP_L2CAP
#define ENABLE_HFP_WIDE_BAND_SPEECH
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
#define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE
#define ENABLE_LE_CENTRAL
#define ENABLE_LE_DATA_LENGTH_EXTENSION
#define ENABLE_LE_PERIPHERAL
#define ENABLE_LE_PRIVACY_ADDRESS_RESOLUTION
#define ENABLE_LE_SECURE_CONNECTIONS
#define ENABLE_LOG_ERROR
#define ENABLE_LOG_INFO
#define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS
#define ENABLE_PRINTF_HEXDUMP
#define ENABLE_SCO_OVER_HCI
#define ENABLE_SDP_DES_DUMP
#define ENABLE_SOFTWARE_AES128
// BTstack configuration. buffers, sizes, ...
#define HCI_ACL_PAYLOAD_SIZE (1691 + 4)
#define HCI_INCOMING_PRE_BUFFER_SIZE 14 // sizeof benep heade, avoid memcpy
#define NVM_NUM_DEVICE_DB_ENTRIES 16
#define NVM_NUM_LINK_KEYS 16
// Mesh Configuration
#define ENABLE_MESH
#define ENABLE_MESH_ADV_BEARER
#define ENABLE_MESH_GATT_BEARER
#define ENABLE_MESH_PB_ADV
#define ENABLE_MESH_PB_GATT
#define ENABLE_MESH_PROVISIONER
#define ENABLE_MESH_PROXY_SERVER
#define MAX_NR_MESH_SUBNETS 2
#define MAX_NR_MESH_TRANSPORT_KEYS 16
#define MAX_NR_MESH_VIRTUAL_ADDRESSES 16
// allow for one NetKey update
#define MAX_NR_MESH_NETWORK_KEYS (MAX_NR_MESH_SUBNETS+1)
#define HCI_RESET_RESEND_TIMEOUT_MS 1000
#endif

View File

@ -0,0 +1,350 @@
/*
* Copyright (C) 2023 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "hci_transport_netgraph.c"
#include "hci_transport_netgraph.h"
#include "btstack_bool.h"
#include "btstack_config.h"
#include "btstack_debug.h"
#include "btstack_event.h"
#include "btstack_run_loop.h"
#include "hci_transport.h"
#include "hci.h"
#include <err.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/bitstring.h>
#include <sys/types.h>
#include <sys/socket.h>
// avoid warning by /usr/include/netgraph/bluetooth/include/ng_btsocket.h
#define L2CAP_SOCKET_CHECKED
#include <bluetooth.h>
#include <netgraph.h>
#include <netgraph/bluetooth/include/ng_hci.h>
#include <netgraph/bluetooth/include/ng_l2cap.h>
#include <netgraph/bluetooth/include/ng_btsocket.h>
// hci packet handler
static void (*hci_transport_netgraph_packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size);
// data source for integration with BTstack Runloop
static btstack_data_source_t hci_transport_netgraph_data_source_hci_raw;
// block write
static uint8_t hci_transport_netgraph_write_packet_type;
static int hci_transport_netgraph_write_bytes_len;
static const uint8_t * hci_transport_netgraph_write_bytes_data;
// assert pre-buffer for packet type is available
#if !defined(HCI_OUTGOING_PRE_BUFFER_SIZE) || (HCI_OUTGOING_PRE_BUFFER_SIZE == 0)
#error HCI_OUTGOING_PRE_BUFFER_SIZE not defined. Please update hci.h
#endif
typedef enum {
TX_OFF,
TX_IDLE,
TX_W4_PACKET_SENT,
} TX_STATE;
// write state
static TX_STATE hci_transport_netgraph_tx_state;
// incoming packet buffer
static uint8_t hci_packet_with_pre_buffer[HCI_INCOMING_PRE_BUFFER_SIZE + HCI_INCOMING_PACKET_BUFFER_SIZE + 1]; // packet type + max(acl header + acl payload, event header + event data)
static uint8_t * hci_packet = &hci_packet_with_pre_buffer[HCI_INCOMING_PRE_BUFFER_SIZE];
// Netgraph node
const char * hci_node_name = "ubt0hci";
// Control and Data socket for BTstack Netgraph node
static int hci_transport_netgraph_hci_raw_control_socket;
static int hci_transport_netgraph_hci_raw_data_socket;
// Track HCI Netgraph Initialization
#define HCI_TODO_READ_BD_ADDR 1
#define HCI_TODO_READ_LOCAL_SUPPORTED_FEATURES 2
#define HCI_TODO_READ_BUFFER_SIZE 4
static uint8_t hci_transport_netgraph_hci_todo_init;
static void hci_transport_netgraph_process_write(btstack_data_source_t *ds) {
if (hci_transport_netgraph_write_bytes_len == 0) return;
const char * hook = NULL;
switch (hci_transport_netgraph_write_packet_type){
case HCI_COMMAND_DATA_PACKET:
hook = "raw";
break;
case HCI_ACL_DATA_PACKET:
hook = "acl";
break;
default:
btstack_unreachable();
break;
}
int res = NgSendData(ds->source.fd, hook, hci_transport_netgraph_write_bytes_data, hci_transport_netgraph_write_bytes_len);
if (res < 0){
log_error("send data via hook %s returned error %d", hook, errno);
btstack_run_loop_enable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
return;
}
btstack_run_loop_disable_data_source_callbacks(ds, DATA_SOURCE_CALLBACK_WRITE);
hci_transport_netgraph_tx_state = TX_IDLE;
hci_transport_netgraph_write_bytes_len = 0;
hci_transport_netgraph_write_bytes_data = NULL;
// notify upper stack that it can send again
static const uint8_t packet_sent_event[] = { HCI_EVENT_TRANSPORT_PACKET_SENT, 0};
hci_transport_netgraph_packet_handler(HCI_EVENT_PACKET, (uint8_t *) &packet_sent_event[0], sizeof(packet_sent_event));
}
static void hci_transport_netgraph_process_read(btstack_data_source_t * ds) {
// read complete HCI packet
char hook[NG_NODESIZ];
int bytes_read = NgRecvData(ds->source.fd, (u_char *) hci_packet, HCI_INCOMING_PACKET_BUFFER_SIZE + 1, hook);
if (bytes_read < 0){
log_error("Read failed %d", (int) bytes_read);
} else {
// ignore 'echo' - only accept hci events from 'raw' hook and acl packet from 'acl' hook
// - HCI CMDs that are received over 'raw' hook are sent back via 'raw' hook
if (strcmp(hook, "raw") == 0){
if (hci_packet[0] != HCI_EVENT_PACKET){
return;
}
}
// - ACL Packets that are received from driver are also sent via 'raw' hook
if (strcmp(hook, "acl") == 0){
if (hci_packet[0] != HCI_ACL_DATA_PACKET){
return;
}
}
// track HCI Event Complete for init commands
if (hci_packet[0] == HCI_EVENT_PACKET){
if (hci_event_packet_get_type(&hci_packet[1]) == HCI_EVENT_COMMAND_COMPLETE){
uint8_t todos_before = hci_transport_netgraph_hci_todo_init;
switch (hci_event_command_complete_get_command_opcode(&hci_packet[1])){
case HCI_OPCODE_HCI_RESET:
hci_transport_netgraph_hci_todo_init = HCI_TODO_READ_BD_ADDR | HCI_TODO_READ_LOCAL_SUPPORTED_FEATURES | HCI_TODO_READ_BUFFER_SIZE;
break;
case HCI_OPCODE_HCI_READ_BD_ADDR:
hci_transport_netgraph_hci_todo_init &= ~HCI_TODO_READ_BD_ADDR;
break;
case HCI_OPCODE_HCI_READ_LOCAL_SUPPORTED_FEATURES:
hci_transport_netgraph_hci_todo_init &= ~HCI_TODO_READ_LOCAL_SUPPORTED_FEATURES;
break;
case HCI_OPCODE_HCI_READ_BUFFER_SIZE:
hci_transport_netgraph_hci_todo_init &= ~HCI_TODO_READ_BUFFER_SIZE;
break;
default:
break;
}
if ((todos_before != 0) && (hci_transport_netgraph_hci_todo_init == 0)){
// tell ubt0hci to disconnect acl hook to ubt0l2cap
char path[NG_NODESIZ];
snprintf(path, sizeof(path), "%s:", hci_node_name);
if (NgSendAsciiMsg(hci_transport_netgraph_hci_raw_control_socket, path, "%s", "init") < 0) {
log_error("Failed to send init to %s", path);
}
}
}
}
uint16_t packet_len = bytes_read-1u;
hci_transport_netgraph_packet_handler(hci_packet[0], &hci_packet[1], packet_len);
}
}
static void hci_transport_netgraph_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type) {
if (ds->source.fd < 0) return;
switch (callback_type){
case DATA_SOURCE_CALLBACK_READ:
hci_transport_netgraph_process_read(ds);
break;
case DATA_SOURCE_CALLBACK_WRITE:
hci_transport_netgraph_process_write(ds);
break;
default:
break;
}
}
// TODO: could pass device name
static void hci_transport_netgraph_init (const void *transport_config){
log_info("init");
btstack_assert(transport_config != NULL);
// extract netgraph device name
hci_transport_config_netgraph_t * hci_transport_config_netgraph = (hci_transport_config_netgraph_t*) transport_config;
hci_node_name = hci_transport_config_netgraph->device_name;
// set state to off
hci_transport_netgraph_tx_state = TX_OFF;
}
static int hci_transport_netgraph_open(void) {
log_info("open(%s)", hci_node_name);
int res;
struct ngm_rmhook rmh;
struct ngm_connect con;
char path[NG_NODESIZ];
char btstack_node_hci_raw_name[NG_NODESIZ];
// create hci_raw netgraph node
snprintf(btstack_node_hci_raw_name, sizeof(btstack_node_hci_raw_name), "btstack");
if (NgMkSockNode(btstack_node_hci_raw_name, &hci_transport_netgraph_hci_raw_control_socket, &hci_transport_netgraph_hci_raw_data_socket) < 0){
log_error("Cannot create netgraph node '%s'", btstack_node_hci_raw_name);
return -1;
}
// tell hci node to disconnect raw hook to btsock_hci_raw (ignore result)
snprintf(path, sizeof(path), "%s:", hci_node_name);
strncpy(rmh.ourhook, "raw", sizeof(rmh.ourhook));
res = NgSendMsg(hci_transport_netgraph_hci_raw_control_socket, path, NGM_GENERIC_COOKIE,
NGM_RMHOOK, &rmh, sizeof(rmh));
log_info("Remove HCI RAW Hook: %d", res);
// tell hci node to disconnect acl hook to ubt0l2cap (ignore result)
snprintf(path, sizeof(path), "%s:", hci_node_name);
strncpy(rmh.ourhook, "acl", sizeof(rmh.ourhook));
res = NgSendMsg(hci_transport_netgraph_hci_raw_control_socket, path, NGM_GENERIC_COOKIE,
NGM_RMHOOK, &rmh, sizeof(rmh));
log_info("Remove ACL Hook: %d", res);
// connect ubth0hci/raw hook
snprintf(path, sizeof(path), "%s:", btstack_node_hci_raw_name);
snprintf(con.path, sizeof(con.path), "%s:", hci_node_name);
strncpy(con.ourhook, "raw", sizeof(con.ourhook));
strncpy(con.peerhook, "raw", sizeof(con.peerhook));
if (NgSendMsg(hci_transport_netgraph_hci_raw_control_socket, path, NGM_GENERIC_COOKIE,
NGM_CONNECT, &con, sizeof(con)) < 0) {
log_error("Cannot connect %s%s to %s%s", path, con.ourhook, con.path, con.peerhook);
return -1;
}
// connect ubth0hci/acl hook
snprintf(path, sizeof(path), "%s:", btstack_node_hci_raw_name);
snprintf(con.path, sizeof(con.path), "%s:", hci_node_name);
strncpy(con.ourhook, "acl", sizeof(con.ourhook));
strncpy(con.peerhook, "acl", sizeof(con.peerhook));
if (NgSendMsg(hci_transport_netgraph_hci_raw_control_socket, path, NGM_GENERIC_COOKIE,
NGM_CONNECT, &con, sizeof(con)) < 0) {
log_error("Cannot connect %s%s to %s%s", path, con.ourhook, con.path, con.peerhook);
return -1;
}
// set up HCI RAW data_source
btstack_run_loop_set_data_source_fd(&hci_transport_netgraph_data_source_hci_raw, hci_transport_netgraph_hci_raw_data_socket);
btstack_run_loop_set_data_source_handler(&hci_transport_netgraph_data_source_hci_raw, &hci_transport_netgraph_process);
btstack_run_loop_add_data_source(&hci_transport_netgraph_data_source_hci_raw);
btstack_run_loop_enable_data_source_callbacks(&hci_transport_netgraph_data_source_hci_raw, DATA_SOURCE_CALLBACK_READ);
// init tx state machines
hci_transport_netgraph_tx_state = TX_IDLE;
return 0;
}
static int hci_transport_netgraph_close(void){
// first remove run loop handler
btstack_run_loop_remove_data_source(&hci_transport_netgraph_data_source_hci_raw);
// then close device
close(hci_transport_netgraph_data_source_hci_raw.source.fd);
hci_transport_netgraph_data_source_hci_raw.source.fd = -1;
return 0;
}
static void hci_transport_netgraph_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
hci_transport_netgraph_packet_handler = handler;
}
static int hci_transport_netgraph_can_send_now(uint8_t packet_type){
UNUSED(packet_type);
return hci_transport_netgraph_tx_state == TX_IDLE;
}
static int hci_transport_netgraph_send_packet(uint8_t packet_type, uint8_t * packet, int size) {
btstack_assert(hci_transport_netgraph_write_bytes_len == 0);
// store packet type before actual data and increase size
uint8_t * buffer = &packet[-1];
uint32_t buffer_size = size + 1;
buffer[0] = packet_type;
// setup async write
hci_transport_netgraph_write_bytes_data = buffer;
hci_transport_netgraph_write_bytes_len = buffer_size;
hci_transport_netgraph_write_packet_type = packet_type;
hci_transport_netgraph_tx_state = TX_W4_PACKET_SENT;
btstack_run_loop_enable_data_source_callbacks(&hci_transport_netgraph_data_source_hci_raw, DATA_SOURCE_CALLBACK_WRITE);
return 0;
}
static const hci_transport_t hci_transport_netgraph = {
.name = "Netgraph",
.init = &hci_transport_netgraph_init,
.open = &hci_transport_netgraph_open,
.close = &hci_transport_netgraph_close,
.register_packet_handler = &hci_transport_netgraph_register_packet_handler,
.can_send_packet_now = &hci_transport_netgraph_can_send_now,
.send_packet = &hci_transport_netgraph_send_packet
};
const hci_transport_t * hci_transport_netgraph_instance(void) {
return &hci_transport_netgraph;
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (C) 2023 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
/**
* @brief TODO
*/
#ifndef HCI_TRANSPORT_NETGRAPH_H
#define HCI_TRANSPORT_NETGRAPH_H
#include "hci_transport.h"
#if defined __cplusplus
extern "C" {
#endif
typedef struct {
hci_transport_config_type_t type;
const char *device_name;
} hci_transport_config_netgraph_t;
const hci_transport_t * hci_transport_netgraph_instance(void);
#if defined __cplusplus
}
#endif
#endif // HCI_TRANSPORT_NETGRAPH_H

View File

@ -0,0 +1,275 @@
/*
* Copyright (C) 2014 BlueKitchen GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* 4. Any redistribution, use, or modification is done solely for
* personal benefit and not for any commercial purpose or for
* monetary gain.
*
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
* GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Please inquire about commercial licensing options at
* contact@bluekitchen-gmbh.com
*
*/
#define BTSTACK_FILE__ "main.c"
// *****************************************************************************
//
// minimal setup for HCI code
//
// *****************************************************************************
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <getopt.h>
#include "btstack_config.h"
#include "ble/le_device_db_tlv.h"
#include "bluetooth_company_id.h"
#include "btstack_audio.h"
#include "btstack_debug.h"
#include "btstack_event.h"
#include "btstack_memory.h"
#include "btstack_run_loop.h"
#include "btstack_run_loop_posix.h"
#include "btstack_signal.h"
#include "btstack_stdin.h"
#include "btstack_tlv_posix.h"
#include "btstack_uart.h"
#include "classic/btstack_link_key_db_tlv.h"
#include "hci.h"
#include "hci_dump.h"
#include "hci_dump_posix_fs.h"
#include "hci_dump_posix_stdout.h"
#include "hci_transport_netgraph.h"
#define TLV_DB_PATH_PREFIX "/tmp/btstack_"
#define TLV_DB_PATH_POSTFIX ".tlv"
static char tlv_db_path[100];
static bool tlv_reset;
static const btstack_tlv_t * tlv_impl;
static btstack_tlv_posix_t tlv_context;
static bd_addr_t local_addr;
static hci_transport_config_netgraph_t config;
// shutdown
static bool shutdown_triggered;
int btstack_main(int argc, const char * argv[]);
static void local_version_information_handler(uint8_t * packet);
static btstack_packet_callback_registration_t hci_event_callback_registration;
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
if (packet_type != HCI_EVENT_PACKET) return;
switch (hci_event_packet_get_type(packet)){
case BTSTACK_EVENT_STATE:
switch(btstack_event_state_get_state(packet)){
case HCI_STATE_WORKING:
gap_local_bd_addr(local_addr);
printf("BTstack up and running on %s.\n", bd_addr_to_str(local_addr));
btstack_strcpy(tlv_db_path, sizeof(tlv_db_path), TLV_DB_PATH_PREFIX);
btstack_strcat(tlv_db_path, sizeof(tlv_db_path), bd_addr_to_str_with_delimiter(local_addr, '-'));
btstack_strcat(tlv_db_path, sizeof(tlv_db_path), TLV_DB_PATH_POSTFIX);
printf("TLV path: %s", tlv_db_path);
if (tlv_reset){
int rc = unlink(tlv_db_path);
if (rc == 0) {
printf(", reset ok");
} else {
printf(", reset failed with result = %d", rc);
}
}
printf("\n");
tlv_impl = btstack_tlv_posix_init_instance(&tlv_context, tlv_db_path);
btstack_tlv_set_instance(tlv_impl, &tlv_context);
#ifdef ENABLE_CLASSIC
hci_set_link_key_db(btstack_link_key_db_tlv_get_instance(tlv_impl, &tlv_context));
#endif
#ifdef ENABLE_BLE
le_device_db_tlv_configure(tlv_impl, &tlv_context);
#endif
break;
case HCI_STATE_OFF:
btstack_tlv_posix_deinit(&tlv_context);
if (!shutdown_triggered) break;
// reset stdin
btstack_stdin_reset();
log_info("Good bye, see you.\n");
exit(0);
break;
default:
break;
}
break;
case HCI_EVENT_COMMAND_COMPLETE:
if (hci_event_command_complete_get_command_opcode(packet) == HCI_OPCODE_HCI_READ_LOCAL_VERSION_INFORMATION){
local_version_information_handler(packet);
}
break;
default:
break;
}
}
static void trigger_shutdown(void){
printf("CTRL-C - SIGINT received, shutting down..\n");
log_info("sigint_handler: shutting down");
shutdown_triggered = true;
hci_power_control(HCI_POWER_OFF);
}
static int led_state = 0;
void hal_led_toggle(void){
led_state = 1 - led_state;
printf("LED State %u\n", led_state);
}
static void local_version_information_handler(uint8_t * packet){
printf("Local version information:\n");
uint16_t hci_version = packet[6];
uint16_t hci_revision = little_endian_read_16(packet, 7);
uint16_t lmp_version = packet[9];
uint16_t manufacturer = little_endian_read_16(packet, 10);
uint16_t lmp_subversion = little_endian_read_16(packet, 12);
printf("- HCI Version 0x%04x\n", hci_version);
printf("- HCI Revision 0x%04x\n", hci_revision);
printf("- LMP Version 0x%04x\n", lmp_version);
printf("- LMP Subversion 0x%04x\n", lmp_subversion);
printf("- Manufacturer 0x%04x\n", manufacturer);
}
static char short_options[] = "hu:l:r";
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"logfile", required_argument, NULL, 'l'},
{"reset-tlv", no_argument, NULL, 'r'},
{"node", required_argument, NULL, 'u'},
{0, 0, 0, 0}
};
static char *help_options[] = {
"print (this) help.",
"set file to store debug output and HCI trace.",
"reset bonding information stored in TLV.",
"set name of netgraph HCI node. Default: ubt0hci",
};
static char *option_arg_name[] = {
"",
"LOGFILE",
"",
"NODE",
};
static void usage(const char *name){
unsigned int i;
printf( "usage:\n\t%s [options]\n", name );
printf("valid options:\n");
for( i=0; long_options[i].name != 0; i++) {
printf("--%-10s| -%c %-10s\t\t%s\n", long_options[i].name, long_options[i].val, option_arg_name[i], help_options[i] );
}
}
int main(int argc, const char * argv[]){
const char * log_file_path = NULL;
// set default device path
config.device_name = "ubt0hci";
// parse command line parameters
while(true){
int c = getopt_long( argc, (char* const *)argv, short_options, long_options, NULL );
if (c < 0) {
break;
}
if (c == '?'){
break;
}
switch (c) {
case 'l':
log_file_path = optarg;
break;
case 'u':
config.device_name = optarg;
break;
case 'r':
tlv_reset = true;
break;
case 'h':
default:
usage(argv[0]);
return EXIT_FAILURE;
}
}
/// GET STARTED with BTstack ///
btstack_memory_init();
btstack_run_loop_init(btstack_run_loop_posix_get_instance());
// log into file using HCI_DUMP_PACKETLOGGER format
if (log_file_path == NULL){
log_file_path = "/tmp/hci_dump.pklg";
}
hci_dump_posix_fs_open(log_file_path, HCI_DUMP_PACKETLOGGER);
const hci_dump_t * hci_dump_impl = hci_dump_posix_fs_get_instance();
hci_dump_init(hci_dump_impl);
printf("Packet Log: %s\n", log_file_path);
// init HCI
const hci_transport_t * transport = hci_transport_netgraph_instance();
config.type = HCI_TRANSPORT_CONFIG_USB;
hci_init(transport, &config);
#ifdef HAVE_PORTAUDIO
btstack_audio_sink_set_instance(btstack_audio_portaudio_sink_get_instance());
btstack_audio_source_set_instance(btstack_audio_portaudio_source_get_instance());
#endif
// inform about BTstack state
hci_event_callback_registration.callback = &packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
// register callback for CTRL-c
btstack_signal_register_callback(SIGINT, &trigger_shutdown);
// setup app
btstack_main(argc, argv);
// go
btstack_run_loop_execute();
return 0;
}