~aleteoryx/muditaos

305d43a1763d40bd804e215ede7780ac680c69fa — Bartek Cichocki 5 years ago 0744fe9
[EGD-3773] added A2DP playback backend
35 files changed, 1256 insertions(+), 200 deletions(-)

M changelog.md
D link_key_db
M module-apps/CMakeLists.txt
M module-apps/application-antenna/windows/ScanModesWindow.cpp
M module-apps/application-settings/ApplicationSettings.cpp
M module-apps/application-settings/windows/BtScanWindow.hpp
M module-apps/application-settings/windows/BtWindow.cpp
M module-audio/Audio/decoder/taglib
M module-bluetooth/Bluetooth/BluetoothWorker.cpp
M module-bluetooth/Bluetooth/BluetoothWorker.hpp
M module-bluetooth/Bluetooth/BtstackWorker.cpp
M module-bluetooth/Bluetooth/Device.hpp
R module-bluetooth/Bluetooth/{interface/profiles => }/btstack_config.h
M module-bluetooth/Bluetooth/glucode/HCITRANS.cpp
M module-bluetooth/Bluetooth/glucode/hal_time_ms.c
A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp
A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp
A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp
A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp
A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp
A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp
A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp
M module-bluetooth/Bluetooth/interface/profiles/GAP.cpp
M module-bluetooth/Bluetooth/interface/profiles/PAN.cpp
A module-bluetooth/Bluetooth/interface/profiles/Profile.hpp
M module-bluetooth/CMakeLists.txt
M module-bluetooth/lib/btstack.cmake
M module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp
M module-bsp/bsp/bluetooth/Bluetooth.cpp
M module-bsp/bsp/bluetooth/Bluetooth.hpp
M module-db/Interface/ThreadRecord.cpp
M module-services/service-bluetooth/ServiceBluetooth.cpp
M module-services/service-bluetooth/ServiceBluetooth.hpp
M module-services/service-bluetooth/messages/BluetoothMessage.hpp
M source/MessageType.hpp
M changelog.md => changelog.md +2 -0
@@ 5,6 5,8 @@
### Added

* `[gui]` Dynamic Windows building and handling implemented
* `[audio]` Added A2DP playback backend


### Fixed


D link_key_db => link_key_db +0 -0
M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +4 -4
@@ 14,13 14,13 @@ endif()
set( SOURCES 
    "Application.cpp"
    "GuiTimer.cpp"
    "UiCommonActions.cpp" 
    "WindowsFactory.cpp" 
    "UiCommonActions.cpp"
    "WindowsFactory.cpp"
    "windows/AppWindow.cpp" 
    "windows/OptionWindow.cpp"
    "windows/Options.cpp" 
    "windows/OptionsWindowOption.cpp" 
    "windows/Dialog.cpp" 
    "windows/OptionsWindowOption.cpp"
    "windows/Dialog.cpp"
    "windows/NoEvents.cpp"
    "widgets/SearchBox.cpp"
    "windows/OptionSetting.cpp"

M module-apps/application-antenna/windows/ScanModesWindow.cpp => module-apps/application-antenna/windows/ScanModesWindow.cpp +1 -1
@@ 72,7 72,7 @@ namespace gui
                }
                auto result = CellularServiceAPI::SetScanMode(this->application, mode);
                if (result) {
                    commandResult->setText("Succes!");
                    commandResult->setText("Success!");
                }
                else {
                    commandResult->setText("Failure!");

M module-apps/application-settings/ApplicationSettings.cpp => module-apps/application-settings/ApplicationSettings.cpp +2 -4
@@ 9,10 9,8 @@
#include "Application.hpp"

#include "MessageType.hpp"
#include "windows/SettingsMainWindow.hpp"
#include "windows/LanguageWindow.hpp"
#include "windows/BtWindow.hpp"
#include "windows/BtScanWindow.hpp"
#include "windows/BtWindow.hpp"
#include "windows/DateTimeWindow.hpp"
#include "windows/FotaWindow.hpp"
#include "windows/Info.hpp"


@@ 32,9 30,9 @@
#include "windows/CellularPassthroughWindow.hpp"
#include "windows/SettingsChange.hpp"

#include <i18/i18.hpp>
#include <module-services/service-evtmgr/api/EventManagerServiceAPI.hpp>
#include <service-bluetooth/messages/BluetoothMessage.hpp>
#include <i18/i18.hpp>

namespace app
{

M module-apps/application-settings/windows/BtScanWindow.hpp => module-apps/application-settings/windows/BtScanWindow.hpp +1 -1
@@ 7,8 7,8 @@
#include "gui/widgets/Image.hpp"
#include "gui/widgets/Label.hpp"
#include "gui/widgets/Window.hpp"
#include <memory>
#include <BoxLayout.hpp>
#include <memory>
#include <service-bluetooth/messages/BluetoothMessage.hpp>

namespace gui

M module-apps/application-settings/windows/BtWindow.cpp => module-apps/application-settings/windows/BtWindow.cpp +15 -3
@@ 62,7 62,7 @@ namespace gui
        setTitle(utils::localize.get("app_settings_bt"));

        LOG_INFO("Create box layout");
        box = new gui::VBox(this, 0, title->offset_h(), style::window_width, 5 * style::window::label::default_h);
        box = new gui::VBox(this, 0, title->offset_h(), style::window_width, 7 * style::window::label::default_h);
        box->setPenWidth(style::window::default_border_no_focus_w);

        // TODO WIP: it's just for usability now


@@ 91,12 91,24 @@ namespace gui
            return true;
        });

        add_box_label(box, "  -> Visibility", [=](Item &) {
            LOG_DEBUG("Callback visibility");
        add_box_label(box, "  -> Set visible", [=](Item &) {
            LOG_DEBUG("Callback set visibility");
            message_bt(application, BluetoothMessage::Request::Visible);
            return true;
        });

        add_box_label(box, "  -> Play", [=](Item &) {
            LOG_DEBUG("Start playback");
            message_bt(application, BluetoothMessage::Request::Play);
            return true;
        });

        add_box_label(box, "  -> Stop", [=](Item &) {
            LOG_DEBUG("Stop playback");
            message_bt(application, BluetoothMessage::Request::Stop);
            return true;
        });

        // hide all elements except button for `bluetooth on off` - this would cause infinite loop
        std::for_each(std::next(box->children.begin()), box->children.end(), [](auto &el) { el->visible = false; });
        setFocusItem(box);

M module-audio/Audio/decoder/taglib => module-audio/Audio/decoder/taglib +1 -1
@@ 1,1 1,1 @@
Subproject commit fa37896f615f3d347a41f0db711e23746774b15f
Subproject commit b2a6e50aedf0cfe1f808eb23dc9f572a848ddffe

M module-bluetooth/Bluetooth/BluetoothWorker.cpp => module-bluetooth/Bluetooth/BluetoothWorker.cpp +37 -5
@@ 1,6 1,7 @@
#include "BluetoothWorker.hpp"
#include "log/log.hpp"
#include "BtCommand.hpp"
#include "log/log.hpp"
#include "module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp"

extern "C"
{


@@ 24,7 25,7 @@ const char *c_str(Bt::Error::Code code)
    return "";
}

BluetoothWorker::BluetoothWorker(sys::Service *service) : Worker(service)
BluetoothWorker::BluetoothWorker(sys::Service *service) : Worker(service), currentProfile(std::make_shared<Bt::A2DP>())
{
    init({
        {"qBtIO", sizeof(Bt::Message), 10},


@@ 34,7 35,9 @@ BluetoothWorker::BluetoothWorker(sys::Service *service) : Worker(service)

BluetoothWorker::~BluetoothWorker()
{
    if (this->bt_worker_task != nullptr) {}
    if (this->bt_worker_task != nullptr) {
        vTaskDelete(this->bt_worker_task);
    }
    LOG_INFO("Worker removed");
}



@@ 182,8 185,37 @@ bool BluetoothWorker::handleMessage(uint32_t queueID)

    return true;
}
void BluetoothWorker::set_addr(uint8_t *addr)

void BluetoothWorker::initAudioBT()
{}

bool BluetoothWorker::play_audio()
{
    auto profile = dynamic_cast<Bt::A2DP *>(currentProfile.get());
    if (profile == nullptr) {
        return false;
    }

    profile->init();
    profile->setOwnerService(service);
    profile->start();
    return true;
}
bool BluetoothWorker::stop_audio()
{
    auto profile = dynamic_cast<Bt::A2DP *>(currentProfile.get());
    if (profile == nullptr) {
        return false;
    }

    profile->stop();
    return true;
}
void BluetoothWorker::set_addr(bd_addr_t addr)
{
    Bt::GAP::do_pairing(addr);
    LOG_INFO("ADDRESS %s SET!", bd_addr_to_str(addr));
    auto profile = dynamic_cast<Bt::A2DP *>(currentProfile.get());
    if (profile != nullptr) {
        profile->setDeviceAddress(addr);
    }
}

M module-bluetooth/Bluetooth/BluetoothWorker.hpp => module-bluetooth/Bluetooth/BluetoothWorker.hpp +12 -5
@@ 1,14 1,14 @@
#pragma once

#include "Device.hpp"
#include "Service/Worker.hpp"
#include "interface/profiles/Profile.hpp"
#include <FreeRTOS.h>
#include <task.h>
#include <bsp/bluetooth/Bluetooth.hpp>
#include <memory>
#include <task.h>
#include <vector>

#include "Device.hpp"
#include "Service/Worker.hpp"

struct HCI;

/// debug option for HCI (uart) commands debugging


@@ 93,9 93,16 @@ class BluetoothWorker : private sys::Worker

    bool start_pan();

    bool play_audio();

    bool stop_audio();

    Error aud_init();
    /// bluetooth stack id in use
    unsigned long active_features;
    void stop_scan();
    void set_addr(uint8_t *);
    void set_addr(bd_addr_t addr);
    void initAudioBT();

    std::shared_ptr<Bt::Profile> currentProfile;
};

M module-bluetooth/Bluetooth/BtstackWorker.cpp => module-bluetooth/Bluetooth/BtstackWorker.cpp +4 -4
@@ 1,16 1,16 @@
#include <log/log.hpp>
#include <cstdlib>
#include <log/log.hpp>

extern "C"
{
#include <btstack_event.h>

#include <bluetooth_company_id.h>
#include <btstack_memory.h>
#include <btstack_run_loop.h>
#include <bluetooth_company_id.h>
#include <btstack_stdin.h>
#include <hci.h>
#include <hci_dump.h>
#include <btstack_stdin.h>

#include <btstack_chipset_cc256x.h>
#include <btstack_link_key_db_memory.h>


@@ 27,9 27,9 @@ extern "C"
}
#endif

#include "BtKeysStorage.hpp"
#include <Error.hpp>
#include <functional>
#include "BtKeysStorage.hpp"

// #define TLV_DB_PATH_PREFIX "/tmp/btstack_"
// #define TLV_DB_PATH_POSTFIX ".tlv"

M module-bluetooth/Bluetooth/Device.hpp => module-bluetooth/Bluetooth/Device.hpp +17 -2
@@ 1,8 1,9 @@
#pragma once
#include <string>
#include <array>
#include <cstring>
#include <utility>
#include <module-bluetooth/lib/btstack/src/bluetooth.h>
#include <string>
#include <utility>

struct Device
{


@@ 36,3 37,17 @@ struct Devicei : public Device
        memcpy(&address, addr, sizeof address);
    }
};

struct DeviceMetadata_t
{
    unsigned int sampleRate;
    unsigned short channels;
    unsigned int samplesPerFrame;
};

constexpr unsigned int DATA_BUFFER_SIZE = 256 * 2;

struct AudioData_t
{
    std::array<int16_t, DATA_BUFFER_SIZE> data;
};

R module-bluetooth/Bluetooth/interface/profiles/btstack_config.h => module-bluetooth/Bluetooth/btstack_config.h +16 -3
@@ 11,9 11,8 @@
#define HAVE_POSIX_FILE_IO
#endif
#define HAVE_BTSTACK_STDIN
// #define HAVE_POSIX_TIME
#define HAVE_POSIX_TIME
#define HAVE_EM9304_PATCH_CONTAINER

// BTstack features that can be enabled
// #define ENABLE_BLE
#define ENABLE_CLASSIC


@@ 30,7 29,7 @@
#define ENABLE_LOG_ERROR
#define ENABLE_LOG_INFO
#define ENABLE_LOG_WARNING
// #define ENABLE_LOG_DEBUG
#define ENABLE_LOG_DEBUG
#define ENABLE_SCO_OVER_HCI
#define ENABLE_SDP_DES_DUMP
// #define ENABLE_EHCILL


@@ 39,6 38,20 @@
#define HCI_INCOMING_PRE_BUFFER_SIZE 14 // sizeof benep heade, avoid memcpy
#define HCI_ACL_PAYLOAD_SIZE         (1691 + 4)

#define ENABLE_GATT_CLIENT_PAIRING
#define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE
#define ENABLE_SEGGER_RTT
#define ENABLE_CC256X_BAUDRATE_CHANGE_FLOWCONTROL_BUG_WORKAROUND
//#define HAVE_EMBEDDED_TIME_MS

#define MAX_NR_HCI_CONNECTIONS                    3
#define MAX_NR_L2CAP_SERVICES                     4
#define MAX_NR_L2CAP_CHANNELS                     10
#define MAX_NR_RFCOMM_MULTIPLEXERS                2
#define MAX_NR_RFCOMM_SERVICES                    4
#define MAX_NR_RFCOMM_CHANNELS                    4
#define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 10

// As an option - much slower (according to docs)
// HCI Controller to Host Flow Control
// #define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL

M module-bluetooth/Bluetooth/glucode/HCITRANS.cpp => module-bluetooth/Bluetooth/glucode/HCITRANS.cpp +0 -123
@@ 1,123 0,0 @@
#include <bsp/bluetooth/Bluetooth.hpp>
#include "log/log.hpp"

using namespace bsp;

extern "C"
{

#include "HCITRANS.h"
#define TRANSPORT_ID 1

    typedef void(BTPSAPI *HCITR_COMDataCallback_t)(unsigned int HCITransportID,
                                                   unsigned int DataLength,
                                                   unsigned char *DataBuffer,
                                                   unsigned long CallbackParameter);
    void RxThread(void *Param);

    int BTPSAPI HCITR_COMOpen(HCI_COMMDriverInformation_t *COMMDriverInformation,
                              HCITR_COMDataCallback_t COMDataCallback,
                              unsigned long CallbackParameter)
    {

        int ret = TRANSPORT_ID;
        LOG_INFO("COM OPEN!");
        Bluetopia *bt = Bluetopia::getInstance();
        bt->set_reset(true);
        bt->open();
        bt->com_cb       = COMDataCallback;
        bt->com_cb_param = CallbackParameter;
        bt->rx_thread    = xTaskCreate(RxThread, "RxThread", 4096, NULL, 3, &bt->thandle);
        if (bt->rx_thread != pdPASS) {
            ret = ErrorBtGeneric;
            LOG_ERROR("COM OPEN failure: %d", bt->rx_thread);
        }
        LOG_INFO("COM OPEN! done");
        return ret;
    }

    void BTPSAPI HCITR_COMClose(unsigned int HCITransportID)
    {
        LOG_ERROR("COM CLOSE");
        Bluetopia *dev = Bluetopia::getInstance();
        if (dev->thandle != NULL) {
            // vTaskDelete(bt->thandle);
        }
        dev->close();
        dev->set_reset(false);
        if (dev->com_cb) {
            dev->com_cb(TRANSPORT_ID, 0, NULL, dev->com_cb_param);
        }
        LOG_ERROR("COM CLOSED");
    }

    void BTPSAPI HCITR_COMReconfigure(unsigned int HCITransportID, HCI_Driver_Reconfigure_Data_t *DriverReconfigureData)
    {
        HCI_COMMReconfigureInformation_t *ReconfigureInformation;
        Bluetopia *dev = Bluetopia::getInstance();
        if ((HCITransportID == TRANSPORT_ID) && dev->is_open && (DriverReconfigureData)) {
            if ((DriverReconfigureData->ReconfigureCommand ==
                 HCI_COMM_DRIVER_RECONFIGURE_DATA_COMMAND_CHANGE_COMM_PARAMETERS) &&
                (DriverReconfigureData->ReconfigureData)) {
                ReconfigureInformation = (HCI_COMMReconfigureInformation_t *)(DriverReconfigureData->ReconfigureData);
                if (ReconfigureInformation->ReconfigureFlags &
                    HCI_COMM_RECONFIGURE_INFORMATION_RECONFIGURE_FLAGS_CHANGE_BAUDRATE) {
                    LOG_INFO("Set baudrate to: %d", ReconfigureInformation->BaudRate);
                    dev->set_irq(false);
                    dev->set_baudrate(ReconfigureInformation->BaudRate);
                    dev->set_irq(true);
                }
            }
        }
    }

    int BTPSAPI HCITR_COMWrite(unsigned int HCITransportID, unsigned int Length, unsigned char *Buffer)
    {
        int ret = 0;
        // LOG_INFO("DATA -> [%d]", Length);
        if (HCITransportID) {
            ret = Bluetopia::getInstance()->write_blocking(reinterpret_cast<char *>(Buffer), Length);
        }
        else {
            ret = -1;
        }
        return ret;
    }

    int BTPSAPI HCITR_COMSuspend(unsigned int HCITransportID)
    {
        LOG_ERROR("Not implemented!");
        int ret = -1;
        if (HCITransportID) {}
        else {
            ret = -1;
        }
        return ret;
    }

    int BTPSAPI HCITR_EnableDebugLogging(Boolean_t Enable)
    {
        return 0;
    }

    void RxThread(void *Param)
    {
        LOG_INFO("BT RxThread created");
        Bluetopia *dev = Bluetopia::getInstance();
        while (dev->is_open) {
            dev->wait_data();
            dev->set_irq(false);
            // LOG_INFO("DATA [%d]<--", dev->in.len);
            // for(int i=0; i<dev->in.len; ++i) {
            //     LOG_PRINTF("0x%X ", dev->in.buff[i]);
            // }
            // LOG_PRINTF("\n");
            if (dev->com_cb) {
                dev->com_cb(TRANSPORT_ID, dev->in.len, (unsigned char *)dev->in.buff, dev->com_cb_param);
            }
            dev->in.flush();
            dev->set_irq(true);
            dev->set_rts(true);
        }
    }
};

M module-bluetooth/Bluetooth/glucode/hal_time_ms.c => module-bluetooth/Bluetooth/glucode/hal_time_ms.c +2 -1
@@ 1,6 1,7 @@
#include "btstack_debug.h"
#include <FreeRTOS.h>
#include <task.h>
#include <stdint.h>
#include <task.h>

uint32_t hal_time_ms(void)
{

A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.cpp +577 -0
@@ 0,0 1,577 @@
//
// Created by bartek on 14.09.2020.
//

#include "A2DP.hpp"
#include "A2DPImpl.hpp"
#include "AVDTP.hpp"
#include "AVRCP.hpp"
#include <Bluetooth/Device.hpp>
#include <Bluetooth/Error.hpp>
#include <log/log.hpp>
#include <module-sys/Service/Bus.hpp>
#include <service-bluetooth/messages/BluetoothMessage.hpp>
extern "C"
{
#include "module-bluetooth/lib/btstack/src/btstack.h"
#include <btstack_defines.h>
}

namespace Bt
{
    A2DP::A2DP() : pimpl(std::make_unique<A2DPImpl>(A2DPImpl()))
    {}

    A2DP::~A2DP() = default;

    A2DP::A2DP(A2DP &other) : pimpl(new A2DPImpl(*other.pimpl))
    {}

    auto A2DP::operator=(A2DP rhs) -> A2DP &
    {
        swap(pimpl, rhs.pimpl);
        return *this;
    }

    A2DP::A2DP(A2DP &&other) noexcept : pimpl(std::move(other.pimpl))
    {
        other.pimpl = nullptr;
    }

    A2DP &A2DP::operator=(A2DP &&other) noexcept
    {
        if (&other == this)
            return *this;

        pimpl       = std::move(other.pimpl);
        other.pimpl = nullptr;

        return *this;
    }

    auto A2DP::init() -> Error
    {
        return pimpl->init();
    }
    void A2DP::start()
    {
        pimpl->start();
    }
    void A2DP::stop()
    {
        pimpl->stop();
    }
    void A2DP::setDeviceAddress(uint8_t *addr)
    {
        pimpl->setDeviceAddress(addr);
    }
    void A2DP::setOwnerService(sys::Service *service)
    {
        pimpl->setOwnerService(service);
    }

    sys::Service *A2DP::A2DPImpl::ownerService;
    QueueHandle_t A2DP::A2DPImpl::sourceQueue = nullptr;
    QueueHandle_t A2DP::A2DPImpl::sinkQueue   = nullptr;
    bd_addr_t A2DP::A2DPImpl::deviceAddr;
    btstack_packet_callback_registration_t A2DP::A2DPImpl::hciEventCallbackRegistration;
    std::array<uint8_t, 150> A2DP::A2DPImpl::sdpSourceServiceBuffer;
    std::array<uint8_t, 4> A2DP::A2DPImpl::mediaSbcCodecCapabilities = {
        (AVDTP_SBC_44100 << 4) | AVDTP_SBC_STEREO,
        0xFF, //(AVDTP_SBC_BLOCK_LENGTH_16 << 4) | (AVDTP_SBC_SUBBANDS_8 << 2) | AVDTP_SBC_ALLOCATION_METHOD_LOUDNESS,
        2,
        53};

    /* LISTING_START(MainConfiguration): Setup Audio Source and AVRCP Target services */

    auto A2DP::A2DPImpl::init() -> Error
    {
        // request role change on reconnecting headset to always use them in slave mode
        hci_set_master_slave_policy(0);

        l2cap_init();
        // Initialize  A2DP Source.
        a2dp_source_init();
        a2dp_source_register_packet_handler(&sourcePacketHandler);

        // Create stream endpoint.
        avdtp_stream_endpoint_t *local_stream_endpoint =
            a2dp_source_create_stream_endpoint(AVDTP_AUDIO,
                                               AVDTP_CODEC_SBC,
                                               mediaSbcCodecCapabilities.data(),
                                               mediaSbcCodecCapabilities.size(),
                                               AVDTP::sbcCodecConfiguration.data(),
                                               AVDTP::sbcCodecConfiguration.size());
        if (local_stream_endpoint == nullptr) {
            LOG_INFO("A2DP Source: not enough memory to create local stream endpoint\n");
            return Error(Bt::Error::SystemError);
        }
        AVRCP::mediaTracker.local_seid = avdtp_local_seid(local_stream_endpoint);
        avdtp_source_register_delay_reporting_category(AVRCP::mediaTracker.local_seid);

        AVRCP::init();
        // Initialize SDP,
        sdp_init();

        // Create  A2DP Source service record and register it with SDP.
        sdpSourceServiceBuffer.fill(0);
        a2dp_source_create_sdp_record(
            sdpSourceServiceBuffer.data(), 0x10001, AVDTP_SOURCE_FEATURE_MASK_PLAYER, nullptr, nullptr);
        sdp_register_service(sdpSourceServiceBuffer.data());

        // Create AVRCP target service record and register it with SDP.
        AVRCP::sdpTargetServiceBuffer.fill(0);
        uint16_t supportedFeatures = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER;
#ifdef AVRCP_BROWSING_ENABLED
        supported_features |= AVRCP_FEATURE_MASK_BROWSING;
#endif
        avrcp_target_create_sdp_record(
            AVRCP::sdpTargetServiceBuffer.data(), 0x10002, supportedFeatures, nullptr, nullptr);
        sdp_register_service(AVRCP::sdpTargetServiceBuffer.data());

        // setup AVRCP Controller
        AVRCP::sdpControllerServiceBuffer.fill(0);
        uint16_t controllerSupportedFeatures = AVRCP_FEATURE_MASK_CATEGORY_PLAYER_OR_RECORDER;
        avrcp_controller_create_sdp_record(
            AVRCP::sdpControllerServiceBuffer.data(), 0x10003, controllerSupportedFeatures, nullptr, nullptr);
        sdp_register_service(AVRCP::sdpControllerServiceBuffer.data());

        // Set local name with a template Bluetooth address, that will be automatically
        // replaced with a actual address once it is available, i.e. when BTstack boots
        // up and starts talking to a Bluetooth module.
        gap_set_local_name("PurePhone");
        gap_discoverable_control(1);
        gap_set_class_of_device(0x200408);

        // Register for HCI events.
        hciEventCallbackRegistration.callback = &hciPacketHandler;
        hci_add_event_handler(&hciEventCallbackRegistration);

        LOG_INFO("Init done!");

        return Error();
    }

    void A2DP::A2DPImpl::sendMediaPacket()
    {
        int numBytesInFrame = btstack_sbc_encoder_sbc_buffer_length();
        int bytesInStorage  = AVRCP::mediaTracker.sbc_storage_count;
        uint8_t numFrames   = bytesInStorage / numBytesInFrame;
        a2dp_source_stream_send_media_payload(AVRCP::mediaTracker.a2dp_cid,
                                              AVRCP::mediaTracker.local_seid,
                                              AVRCP::mediaTracker.sbc_storage,
                                              bytesInStorage,
                                              numFrames,
                                              0);
        AVRCP::mediaTracker.sbc_storage_count = 0;
        AVRCP::mediaTracker.sbc_ready_to_send = 0;
    }

    auto A2DP::A2DPImpl::fillSbcAudioBuffer(MediaContext *context) -> int
    {
        // perform sbc encodin
        int totalNumBytesRead                    = 0;
        unsigned int numAudioSamplesPerSbcBuffer = btstack_sbc_encoder_num_audio_frames();
        while (context->samples_ready >= numAudioSamplesPerSbcBuffer &&
               (context->max_media_payload_size - context->sbc_storage_count) >=
                   btstack_sbc_encoder_sbc_buffer_length()) {

            AudioData_t audioData;

            if (sourceQueue != nullptr) {
                if (xQueueReceive(sourceQueue, &audioData, 10) != pdPASS) {
                    audioData.data.fill(0);
                    LOG_ERROR("Failed to receive!");
                }
            }
            else {
                audioData.data.fill(0);
                LOG_ERROR("queue is nullptr!");
            }

            btstack_sbc_encoder_process_data(audioData.data.data());

            uint16_t sbcFrameSize = btstack_sbc_encoder_sbc_buffer_length();
            uint8_t *sbcFrame     = btstack_sbc_encoder_sbc_buffer();

            totalNumBytesRead += numAudioSamplesPerSbcBuffer;
            memcpy(&context->sbc_storage[context->sbc_storage_count], sbcFrame, sbcFrameSize);
            context->sbc_storage_count += sbcFrameSize;
            context->samples_ready -= numAudioSamplesPerSbcBuffer;
        }
        return totalNumBytesRead;
    }

    void A2DP::A2DPImpl::audioTimeoutHandler(btstack_timer_source_t *timer)
    {
        auto *context = static_cast<MediaContext *>(btstack_run_loop_get_timer_context(timer));
        btstack_run_loop_set_timer(&context->audio_timer, AUDIO_TIMEOUT_MS);
        btstack_run_loop_add_timer(&context->audio_timer);
        uint32_t now = btstack_run_loop_get_time_ms();

        uint32_t updatePeriodMs = AUDIO_TIMEOUT_MS;
        if (context->time_audio_data_sent_in_ms > 0) {
            updatePeriodMs = now - context->time_audio_data_sent_in_ms;
        }

        uint32_t numSamples = (updatePeriodMs * AVDTP::sampleRate) / 1000;
        context->acc_num_missed_samples += (updatePeriodMs * AVDTP::sampleRate) % 1000;

        while (context->acc_num_missed_samples >= 1000) {
            numSamples++;
            context->acc_num_missed_samples -= 1000;
        }
        context->time_audio_data_sent_in_ms = now;
        context->samples_ready += numSamples;

        if (context->sbc_ready_to_send != 0u) {
            return;
        }

        fillSbcAudioBuffer(context);

        if ((context->sbc_storage_count + btstack_sbc_encoder_sbc_buffer_length()) > context->max_media_payload_size) {
            // schedule sending
            context->sbc_ready_to_send = 1;
            a2dp_source_stream_endpoint_request_can_send_now(context->a2dp_cid, context->local_seid);
        }
    }

    void A2DP::A2DPImpl::startTimer(MediaContext *context)
    {
        LOG_DEBUG("Timer start");

        context->max_media_payload_size =
            btstack_min(a2dp_max_media_payload_size(context->a2dp_cid, context->local_seid), SBC_STORAGE_SIZE);
        context->sbc_storage_count = 0;
        context->sbc_ready_to_send = 0;
        context->streaming         = 1;
        btstack_run_loop_remove_timer(&context->audio_timer);
        btstack_run_loop_set_timer_handler(&context->audio_timer, audioTimeoutHandler);
        btstack_run_loop_set_timer_context(&context->audio_timer, context);
        btstack_run_loop_set_timer(&context->audio_timer, A2DP::A2DPImpl::AUDIO_TIMEOUT_MS);
        btstack_run_loop_add_timer(&context->audio_timer);
    }

    void A2DP::A2DPImpl::stopTimer(MediaContext *context)
    {
        LOG_DEBUG("Timer stop");

        context->time_audio_data_sent_in_ms = 0;
        context->acc_num_missed_samples     = 0;
        context->samples_ready              = 0;
        context->streaming                  = 1;
        context->sbc_storage_count          = 0;
        context->sbc_ready_to_send          = 0;
        btstack_run_loop_remove_timer(&context->audio_timer);
    }

    void A2DP::A2DPImpl::hciPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size)
    {
        if (packetType != HCI_EVENT_PACKET) {
            return;
        }

        if (hci_event_packet_get_type(packet) == HCI_EVENT_PIN_CODE_REQUEST) {
            bd_addr_t address;
            LOG_INFO("Pin code request - using '0000'\n");
            hci_event_pin_code_request_get_bd_addr(packet, address);
            gap_pin_code_response(address, "0000");
        }
    }

    void A2DP::A2DPImpl::sourcePacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size)
    {
        uint8_t status;
        uint8_t local_seid;
        bd_addr_t address;
        uint16_t cid;

        if (packetType != HCI_EVENT_PACKET) {
            return;
        }
        if (hci_event_packet_get_type(packet) != HCI_EVENT_A2DP_META) {
            return;
        }

        switch (hci_event_a2dp_meta_get_subevent_code(packet)) {
        case A2DP_SUBEVENT_SIGNALING_CONNECTION_ESTABLISHED:
            a2dp_subevent_signaling_connection_established_get_bd_addr(packet, address);
            cid    = a2dp_subevent_signaling_connection_established_get_a2dp_cid(packet);
            status = a2dp_subevent_signaling_connection_established_get_status(packet);

            if (status != ERROR_CODE_SUCCESS) {
                LOG_INFO("A2DP Source: Connection failed, status 0x%02x, cid 0x%02x, a2dp_cid 0x%02x \n",
                         status,
                         cid,
                         AVRCP::mediaTracker.a2dp_cid);
                AVRCP::mediaTracker.a2dp_cid = 0;
                break;
            }
            AVRCP::mediaTracker.a2dp_cid = cid;
            AVRCP::mediaTracker.volume   = 64;

            LOG_INFO("A2DP Source: Connected to address %s, a2dp cid 0x%02x, local seid %d.\n",
                     bd_addr_to_str(address),
                     AVRCP::mediaTracker.a2dp_cid,
                     AVRCP::mediaTracker.local_seid);
            break;

        case A2DP_SUBEVENT_SIGNALING_MEDIA_CODEC_SBC_CONFIGURATION: {
            cid = avdtp_subevent_signaling_media_codec_sbc_configuration_get_avdtp_cid(packet);
            if (cid != AVRCP::mediaTracker.a2dp_cid) {
                return;
            }
            AVRCP::mediaTracker.remote_seid =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_acp_seid(packet);

            AVDTP::sbcConfig.reconfigure =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_reconfigure(packet);
            AVDTP::sbcConfig.numChannels =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_num_channels(packet);
            AVDTP::sbcConfig.samplingFrequency =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_sampling_frequency(packet);
            AVDTP::sbcConfig.channelMode =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_channel_mode(packet);
            AVDTP::sbcConfig.blockLength =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_block_length(packet);
            AVDTP::sbcConfig.subbands = a2dp_subevent_signaling_media_codec_sbc_configuration_get_subbands(packet);
            AVDTP::sbcConfig.allocationMethod =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_allocation_method(packet);
            AVDTP::sbcConfig.minBitpoolValue =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_min_bitpool_value(packet);
            AVDTP::sbcConfig.maxBitpoolValue =
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_max_bitpool_value(packet);
            AVDTP::sbcConfig.framesPerBuffer = AVDTP::sbcConfig.subbands * AVDTP::sbcConfig.blockLength;
            LOG_INFO(
                "A2DP Source: Received SBC codec configuration, sampling frequency %u, a2dp_cid 0x%02x, local seid %d "
                "(expected %d), remote seid %d .\n",
                AVDTP::sbcConfig.samplingFrequency,
                cid,
                a2dp_subevent_signaling_media_codec_sbc_configuration_get_int_seid(packet),
                AVRCP::mediaTracker.local_seid,
                AVRCP::mediaTracker.remote_seid);

            // Adapt Bluetooth spec definition to SBC Encoder expected input
            AVDTP::sbcConfig.allocationMethod -= 1;
            AVDTP::sbcConfig.numChannels = 2;
            switch (AVDTP::sbcConfig.channelMode) {
            case AVDTP_SBC_JOINT_STEREO:
                AVDTP::sbcConfig.channelMode = 3;
                break;
            case AVDTP_SBC_STEREO:
                AVDTP::sbcConfig.channelMode = 2;
                break;
            case AVDTP_SBC_DUAL_CHANNEL:
                AVDTP::sbcConfig.channelMode = 1;
                break;
            case AVDTP_SBC_MONO:
                AVDTP::sbcConfig.channelMode = 0;
                AVDTP::sbcConfig.numChannels = 1;
                break;
            }
            AVDTP::dumpSbcConfiguration();

            btstack_sbc_encoder_init(&AVDTP::sbcEncoderState,
                                     SBC_MODE_STANDARD,
                                     AVDTP::sbcConfig.blockLength,
                                     AVDTP::sbcConfig.subbands,
                                     AVDTP::sbcConfig.allocationMethod,
                                     AVDTP::sbcConfig.samplingFrequency,
                                     AVDTP::sbcConfig.maxBitpoolValue,
                                     AVDTP::sbcConfig.channelMode);

            DeviceMetadata_t metadata{
                .sampleRate      = static_cast<unsigned int>(AVDTP::sbcConfig.samplingFrequency),
                .channels        = static_cast<unsigned short>(AVDTP::sbcConfig.numChannels),
                .samplesPerFrame = static_cast<unsigned int>(btstack_sbc_encoder_num_audio_frames()),
            };

            auto msg = std::make_shared<BluetoothDeviceMetadataMessage>(std::move(metadata));
            sys::Bus::SendUnicast(std::move(msg), "ServiceBluetooth", ownerService);

            break;
        }

        case A2DP_SUBEVENT_SIGNALING_DELAY_REPORTING_CAPABILITY:
            LOG_INFO("A2DP Source: remote supports delay report, remote seid %d\n",
                     avdtp_subevent_signaling_delay_reporting_capability_get_remote_seid(packet));
            break;
        case A2DP_SUBEVENT_SIGNALING_CAPABILITIES_DONE:
            LOG_INFO("A2DP Source: All capabilities reported, remote seid %d\n",
                     avdtp_subevent_signaling_capabilities_done_get_remote_seid(packet));
            break;

        case A2DP_SUBEVENT_SIGNALING_DELAY_REPORT:
            LOG_INFO("A2DP Source: Received delay report of %d.%0d ms, local seid %d\n",
                     avdtp_subevent_signaling_delay_report_get_delay_100us(packet) / 10,
                     avdtp_subevent_signaling_delay_report_get_delay_100us(packet) % 10,
                     avdtp_subevent_signaling_delay_report_get_local_seid(packet));
            break;

        case A2DP_SUBEVENT_STREAM_ESTABLISHED:
            a2dp_subevent_stream_established_get_bd_addr(packet, address);
            status = a2dp_subevent_stream_established_get_status(packet);
            if (status != 0u) {
                LOG_INFO("A2DP Source: Stream failed, status 0x%02x.\n", status);
                break;
            }

            local_seid = a2dp_subevent_stream_established_get_local_seid(packet);
            cid        = a2dp_subevent_stream_established_get_a2dp_cid(packet);
            LOG_INFO("A2DP_SUBEVENT_STREAM_ESTABLISHED:  a2dp_cid [expected 0x%02x, received 0x%02x], local_seid %d "
                     "(expected %d), remote_seid %d (expected %d)\n",
                     AVRCP::mediaTracker.a2dp_cid,
                     cid,
                     local_seid,
                     AVRCP::mediaTracker.local_seid,
                     a2dp_subevent_stream_established_get_remote_seid(packet),
                     AVRCP::mediaTracker.remote_seid);

            if (local_seid != AVRCP::mediaTracker.local_seid) {
                LOG_INFO("A2DP Source: Stream failed, wrong local seid %d, expected %d.\n",
                         local_seid,
                         AVRCP::mediaTracker.local_seid);
                break;
            }
            LOG_INFO("A2DP Source: Stream established, address %s, a2dp cid 0x%02x, local seid %d, remote seid %d.\n",
                     bd_addr_to_str(address),
                     AVRCP::mediaTracker.a2dp_cid,
                     AVRCP::mediaTracker.local_seid,
                     a2dp_subevent_stream_established_get_remote_seid(packet));

            sourceQueue = xQueueCreate(5, sizeof(AudioData_t));
            sinkQueue   = nullptr;
            if (sourceQueue != nullptr) {
                auto msg = std::make_shared<BluetoothAudioRegisterMessage>(sourceQueue, sinkQueue);
                sys::Bus::SendUnicast(std::move(msg), "ServiceBluetooth", ownerService);
            }
            else {
                LOG_ERROR("failed to create queue!");
            }

            AVRCP::mediaTracker.stream_opened = 1;
            a2dp_source_start_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid);
            break;

        case A2DP_SUBEVENT_STREAM_RECONFIGURED:
            status     = a2dp_subevent_stream_reconfigured_get_status(packet);
            local_seid = a2dp_subevent_stream_reconfigured_get_local_seid(packet);
            cid        = a2dp_subevent_stream_reconfigured_get_a2dp_cid(packet);

            LOG_INFO("A2DP Source: Reconfigured: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, "
                     "received %d]\n",
                     AVRCP::mediaTracker.a2dp_cid,
                     cid,
                     AVRCP::mediaTracker.local_seid,
                     local_seid);
            LOG_INFO("Status 0x%02x\n", status);
            break;

        case A2DP_SUBEVENT_STREAM_STARTED:
            local_seid = a2dp_subevent_stream_started_get_local_seid(packet);
            cid        = a2dp_subevent_stream_started_get_a2dp_cid(packet);

            AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_PLAYING;
            if (AVRCP::mediaTracker.avrcp_cid != 0u) {
                avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP_PLAYBACK_STATUS_PLAYING);
            }
            startTimer(&AVRCP::mediaTracker);
            LOG_INFO(
                "A2DP Source: Stream started: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, "
                "received %d]\n",
                AVRCP::mediaTracker.a2dp_cid,
                cid,
                AVRCP::mediaTracker.local_seid,
                local_seid);
            break;

        case A2DP_SUBEVENT_STREAMING_CAN_SEND_MEDIA_PACKET_NOW:
            local_seid = a2dp_subevent_streaming_can_send_media_packet_now_get_local_seid(packet);
            cid        = a2dp_subevent_signaling_media_codec_sbc_configuration_get_a2dp_cid(packet);
            // LOG_INFO("A2DP Source: can send media packet: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid
            // [expected %d, received %d]\n", AVRCP::mediaTracker.a2dp_cid, cid, AVRCP::mediaTracker.local_seid,
            // local_seid);
            sendMediaPacket();
            break;

        case A2DP_SUBEVENT_STREAM_SUSPENDED:
            local_seid = a2dp_subevent_stream_suspended_get_local_seid(packet);
            cid        = a2dp_subevent_stream_suspended_get_a2dp_cid(packet);

            AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_PAUSED;
            if (AVRCP::mediaTracker.avrcp_cid != 0u) {
                avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP_PLAYBACK_STATUS_PAUSED);
            }
            LOG_INFO(
                "A2DP Source: Stream paused: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, "
                "received %d]\n",
                AVRCP::mediaTracker.a2dp_cid,
                cid,
                AVRCP::mediaTracker.local_seid,
                local_seid);

            stopTimer(&AVRCP::mediaTracker);
            break;

        case A2DP_SUBEVENT_STREAM_RELEASED:
            AVRCP::playInfo.status = AVRCP_PLAYBACK_STATUS_STOPPED;
            cid                    = a2dp_subevent_stream_released_get_a2dp_cid(packet);
            local_seid             = a2dp_subevent_stream_released_get_local_seid(packet);

            LOG_INFO(
                "A2DP Source: Stream released: a2dp_cid [expected 0x%02x, received 0x%02x], local_seid [expected %d, "
                "received %d]\n",
                AVRCP::mediaTracker.a2dp_cid,
                cid,
                AVRCP::mediaTracker.local_seid,
                local_seid);

            if (cid == AVRCP::mediaTracker.a2dp_cid) {
                AVRCP::mediaTracker.stream_opened = 0;
                LOG_INFO("A2DP Source: Stream released.\n");
            }
            if (AVRCP::mediaTracker.avrcp_cid != 0u) {

                avrcp_target_set_playback_status(AVRCP::mediaTracker.avrcp_cid, AVRCP_PLAYBACK_STATUS_STOPPED);
            }

            stopTimer(&AVRCP::mediaTracker);
            break;
        case A2DP_SUBEVENT_SIGNALING_CONNECTION_RELEASED:
            cid = a2dp_subevent_signaling_connection_released_get_a2dp_cid(packet);
            if (cid == AVRCP::mediaTracker.a2dp_cid) {
                AVRCP::mediaTracker.avrcp_cid = 0;
                AVRCP::mediaTracker.a2dp_cid  = 0;
                LOG_INFO("A2DP Source: Signaling released.\n\n");
            }
            break;
        default:
            break;
        }
    }

    void A2DP::A2DPImpl::start()
    {
        LOG_INFO("Starting playback to %s", bd_addr_to_str(deviceAddr));
        a2dp_source_establish_stream(deviceAddr, AVRCP::mediaTracker.local_seid, &AVRCP::mediaTracker.a2dp_cid);
    }

    void A2DP::A2DPImpl::stop()
    {
        LOG_INFO("Stopping playback");
        a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid);
        l2cap_unregister_service(1);
    };

    void A2DP::A2DPImpl::setDeviceAddress(bd_addr_t addr)
    {
        bd_addr_copy(deviceAddr, addr);
        LOG_INFO("Address set!");
    }
    void A2DP::A2DPImpl::setOwnerService(sys::Service *service)
    {
        ownerService = service;
    }

} // namespace Bt

A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DP.hpp +31 -0
@@ 0,0 1,31 @@
#pragma once

#include "FreeRTOS.h"
#include "Profile.hpp"
#include "Service/Service.hpp"
#include "queue.h"
#include <memory>
namespace Bt
{
    class A2DP : public Profile
    {
      public:
        A2DP();
        ~A2DP() override;

        A2DP(A2DP &other);
        auto operator=(A2DP rhs) -> A2DP &;
        A2DP(A2DP &&other) noexcept;
        auto operator=(A2DP &&other) noexcept -> A2DP &;

        auto init() -> Error override;
        void setDeviceAddress(uint8_t *addr) override;
        void setOwnerService(sys::Service *service);
        void start();
        void stop();

      private:
        class A2DPImpl;
        std::unique_ptr<A2DPImpl> pimpl;
    };
} // namespace Bt

A module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/A2DPImpl.hpp +72 -0
@@ 0,0 1,72 @@
#pragma once
#include "A2DP.hpp"
#include <Bluetooth/Device.hpp>
#include <Bluetooth/Error.hpp>
#include <BtCommand.hpp>
#include <log/log.hpp>
extern "C"
{
#include <btstack.h>
#include <btstack_defines.h>
}

namespace Bt
{
    static constexpr int SBC_STORAGE_SIZE = 1030;
    struct MediaContext
    {
        uint16_t a2dp_cid;
        uint8_t local_seid;
        uint8_t remote_seid;
        uint8_t stream_opened;
        uint16_t avrcp_cid;

        uint32_t time_audio_data_sent_in_ms;
        uint32_t acc_num_missed_samples;
        uint32_t samples_ready;
        btstack_timer_source_t audio_timer;
        uint8_t streaming;
        int max_media_payload_size;

        uint8_t sbc_storage[SBC_STORAGE_SIZE];
        uint16_t sbc_storage_count;
        uint8_t sbc_ready_to_send;

        uint16_t volume;
    };

    class AVRCP;
    class A2DP::A2DPImpl
    {

        static constexpr int NUM_CHANNELS           = 2;
        static constexpr int BYTES_PER_AUDIO_SAMPLE = (2 * NUM_CHANNELS);
        static constexpr int AUDIO_TIMEOUT_MS       = 10;
        static constexpr int TABLE_SIZE_441HZ       = 100;
        static constexpr int SDP_BUFFER_LENGTH      = 150;
        static constexpr int MEDIA_CAP_SIZE         = 4;

        static std::array<uint8_t, SDP_BUFFER_LENGTH> sdpSourceServiceBuffer;
        static bd_addr_t deviceAddr;
        static btstack_packet_callback_registration_t hciEventCallbackRegistration;
        static std::array<uint8_t, MEDIA_CAP_SIZE> mediaSbcCodecCapabilities;
        static sys::Service *ownerService;
        static QueueHandle_t sourceQueue;
        static QueueHandle_t sinkQueue;

        static void startTimer(MediaContext *context);
        static void stopTimer(MediaContext *context);
        static void audioTimeoutHandler(btstack_timer_source_t *timer);
        static void sourcePacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void hciPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void sendMediaPacket();
        static auto fillSbcAudioBuffer(MediaContext *context) -> int;

      public:
        auto init() -> Error;
        void start();
        void stop();
        void setDeviceAddress(bd_addr_t addr);
        void setOwnerService(sys::Service *service);
    };
} // namespace Bt

A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.cpp +21 -0
@@ 0,0 1,21 @@
#include "AVDTP.hpp"

namespace Bt
{
    AVDTP::SbcConfiguration AVDTP::sbcConfig;
    btstack_sbc_encoder_state_t AVDTP::sbcEncoderState;
    std::array<uint8_t, 4> AVDTP::sbcCodecConfiguration;
    int AVDTP::sampleRate = AVDTP::defaultSampleRate;

    void AVDTP::dumpSbcConfiguration()
    {
        LOG_INFO("Received media codec configuration:");
        LOG_INFO("    - numChannels: %d", sbcConfig.numChannels);
        LOG_INFO("    - samplingFrequency: %d", sbcConfig.samplingFrequency);
        LOG_INFO("    - channelMode: %d", sbcConfig.channelMode);
        LOG_INFO("    - blockLength: %d", sbcConfig.blockLength);
        LOG_INFO("    - subbands: %d", sbcConfig.subbands);
        LOG_INFO("    - allocationMethod: %d", sbcConfig.allocationMethod);
        LOG_INFO("    - bitpool_value [%d, %d] ", sbcConfig.minBitpoolValue, sbcConfig.maxBitpoolValue);
    }
} // namespace Bt

A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVDTP.hpp +37 -0
@@ 0,0 1,37 @@
#pragma once

#include "A2DPImpl.hpp"
extern "C"
{
#include <btstack.h>
#include <btstack_defines.h>
#include <classic/avdtp.h>
}
namespace Bt
{

    class AVDTP
    {
      public:
        struct SbcConfiguration
        {
            int reconfigure;
            int numChannels;
            int samplingFrequency;
            int channelMode;
            int blockLength;
            int subbands;
            int allocationMethod;
            int minBitpoolValue;
            int maxBitpoolValue;
            int framesPerBuffer;
        };
        static SbcConfiguration sbcConfig;
        static btstack_sbc_encoder_state_t sbcEncoderState;
        static std::array<uint8_t, 4> sbcCodecConfiguration;
        static constexpr int defaultSampleRate = 44100;
        static int sampleRate;

        static void dumpSbcConfiguration();
    };
} // namespace Bt

A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.cpp +189 -0
@@ 0,0 1,189 @@
#include "AVRCP.hpp"

namespace Bt
{

    AVRCP::PlaybackStatusInfo AVRCP::playInfo;
    int AVRCP::currentTrackIndex;
    MediaContext AVRCP::mediaTracker;
    std::array<uint8_t, 200> AVRCP::sdpTargetServiceBuffer;
    std::array<uint8_t, 200> AVRCP::sdpControllerServiceBuffer;

    void AVRCP::packetHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size)
    {
        bd_addr_t event_addr;
        uint16_t local_cid;
        uint8_t status = ERROR_CODE_SUCCESS;

        if (packetType != HCI_EVENT_PACKET) {
            return;
        }
        if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) {
            return;
        }

        switch (packet[2]) {
        case AVRCP_SUBEVENT_CONNECTION_ESTABLISHED:
            local_cid = avrcp_subevent_connection_established_get_avrcp_cid(packet);
            status    = avrcp_subevent_connection_established_get_status(packet);
            if (status != ERROR_CODE_SUCCESS) {
                LOG_INFO("AVRCP: Connection failed, local cid 0x%02x, status 0x%02x\n", local_cid, status);
                return;
            }
            AVRCP::mediaTracker.avrcp_cid = local_cid;
            avrcp_subevent_connection_established_get_bd_addr(packet, event_addr);

            avrcp_target_set_now_playing_info(
                AVRCP::mediaTracker.avrcp_cid, NULL, sizeof(AVRCP::tracks) / sizeof(avrcp_track_t));
            avrcp_target_set_unit_info(AVRCP::mediaTracker.avrcp_cid, AVRCP_SUBUNIT_TYPE_AUDIO, AVRCP::companyId);
            avrcp_target_set_subunit_info(AVRCP::mediaTracker.avrcp_cid,
                                          AVRCP_SUBUNIT_TYPE_AUDIO,
                                          (uint8_t *)AVRCP::subunitInfo,
                                          sizeof(AVRCP::subunitInfo));

            avrcp_controller_get_supported_events(AVRCP::mediaTracker.avrcp_cid);

            LOG_INFO("AVRCP: Channel successfully opened:  A2DP::mediaTracker.avrcp_cid 0x%02x\n",
                     AVRCP::mediaTracker.avrcp_cid);
            return;

        case AVRCP_SUBEVENT_CONNECTION_RELEASED:
            LOG_INFO("AVRCP Target: Disconnected, avrcp_cid 0x%02x\n",
                     avrcp_subevent_connection_released_get_avrcp_cid(packet));
            AVRCP::mediaTracker.avrcp_cid = 0;
            return;
        default:
            break;
        }

        if (status != ERROR_CODE_SUCCESS) {
            LOG_INFO("Responding to event 0x%02x failed with status 0x%02x\n", packet[2], status);
        }
    }

    void AVRCP::targetPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size)
    {
        UNUSED(channel);
        UNUSED(size);
        uint8_t status = ERROR_CODE_SUCCESS;

        if (packetType != HCI_EVENT_PACKET) {
            return;
        }
        if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) {
            return;
        }

        switch (packet[2]) {
        case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED:
            AVRCP::mediaTracker.volume = avrcp_subevent_notification_volume_changed_get_absolute_volume(packet);
            LOG_INFO("AVRCP Target: Volume set to %d%% (%d)\n",
                     AVRCP::mediaTracker.volume * 127 / 100,
                     AVRCP::mediaTracker.volume);
            break;
        case AVRCP_SUBEVENT_EVENT_IDS_QUERY:
            status = avrcp_target_supported_events(AVRCP::mediaTracker.avrcp_cid,
                                                   AVRCP::eventsNum,
                                                   const_cast<uint8_t *>(AVRCP::events),
                                                   sizeof(AVRCP::events));
            break;
        case AVRCP_SUBEVENT_COMPANY_IDS_QUERY:
            status = avrcp_target_supported_companies(AVRCP::mediaTracker.avrcp_cid,
                                                      AVRCP::companiesNum,
                                                      const_cast<uint8_t *>(AVRCP::companies),
                                                      sizeof(AVRCP::companies));
            break;
        case AVRCP_SUBEVENT_PLAY_STATUS_QUERY:
            status = avrcp_target_play_status(AVRCP::mediaTracker.avrcp_cid,
                                              AVRCP::playInfo.song_length_ms,
                                              AVRCP::playInfo.song_position_ms,
                                              AVRCP::playInfo.status);
            break;
            // case AVRCP_SUBEVENT_NOW_PLAYING_INFO_QUERY:
            //     status = avrcp_target_now_playing_info(avrcp_cid);
            //     break;
        case AVRCP_SUBEVENT_OPERATION: {
            auto operation_id = (avrcp_operation_id_t)avrcp_subevent_operation_get_operation_id(packet);
            switch (operation_id) {
            case AVRCP_OPERATION_ID_PLAY:
                LOG_INFO("AVRCP Target: PLAY\n");
                status = a2dp_source_start_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid);
                break;
            case AVRCP_OPERATION_ID_PAUSE:
                LOG_INFO("AVRCP Target: PAUSE\n");
                status = a2dp_source_pause_stream(AVRCP::mediaTracker.a2dp_cid, AVRCP::mediaTracker.local_seid);
                break;
            case AVRCP_OPERATION_ID_STOP:
                LOG_INFO("AVRCP Target: STOP\n");
                status = a2dp_source_disconnect(AVRCP::mediaTracker.a2dp_cid);
                break;
            default:
                LOG_INFO("AVRCP Target: operation 0x%2x is not handled\n", operation_id);
                return;
            }
            break;
        }

        default:
            break;
        }

        if (status != ERROR_CODE_SUCCESS) {
            LOG_INFO("Responding to event 0x%02x failed with status 0x%02x\n", packet[2], status);
        }
    }

    void AVRCP::controllerPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size)
    {
        UNUSED(channel);
        UNUSED(size);
        uint8_t status = 0xFF;

        if (packetType != HCI_EVENT_PACKET) {
            return;
        }
        if (hci_event_packet_get_type(packet) != HCI_EVENT_AVRCP_META) {
            return;
        }

        status = packet[5];
        if (AVRCP::mediaTracker.avrcp_cid == 0u) {
            return;
        }

        // ignore INTERIM status
        if (status == AVRCP_CTYPE_RESPONSE_INTERIM) {
            return;
        }

        switch (packet[2]) {
        case AVRCP_SUBEVENT_NOTIFICATION_VOLUME_CHANGED:
            LOG_INFO("AVRCP Controller: notification absolute volume changed %d %%\n",
                     avrcp_subevent_notification_volume_changed_get_absolute_volume(packet) * 100 / 127);
            break;
        case AVRCP_SUBEVENT_GET_CAPABILITY_EVENT_ID:
            LOG_INFO("Remote supports EVENT_ID 0x%02x\n", avrcp_subevent_get_capability_event_id_get_event_id(packet));
            break;
        case AVRCP_SUBEVENT_GET_CAPABILITY_EVENT_ID_DONE:
            LOG_INFO("automatically enable notifications\n");
            avrcp_controller_enable_notification(AVRCP::mediaTracker.avrcp_cid,
                                                 AVRCP_NOTIFICATION_EVENT_VOLUME_CHANGED);
            break;
        default:
            break;
        }
    }
    void AVRCP::init()
    {
        // Initialize AVRCP Service.
        avrcp_init();
        avrcp_register_packet_handler(&packetHandler);
        // Initialize AVRCP Target.
        avrcp_target_init();
        avrcp_target_register_packet_handler(&targetPacketHandler);
        // Initialize AVRCP Controller
        avrcp_controller_init();
        avrcp_controller_register_packet_handler(&controllerPacketHandler);
    }

} // namespace Bt

A module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp => module-bluetooth/Bluetooth/interface/profiles/A2DP/AVRCP.hpp +57 -0
@@ 0,0 1,57 @@
#pragma once
#include "A2DPImpl.hpp"
extern "C"
{
#include "classic/avrcp.h"
#include "classic/avrcp_browsing_controller.h"
#include "classic/avrcp_browsing_target.h"
#include "classic/avrcp_controller.h"
#include "classic/avrcp_media_item_iterator.h"
#include "classic/avrcp_target.h"
#include <btstack.h>
#include <btstack_defines.h>
}

namespace Bt
{
    class AVRCP
    {
      public:
        constexpr static const uint8_t subunitInfo[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
                                                        4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7};
        constexpr static uint32_t companyId          = 0x112233;
        constexpr static uint8_t companiesNum        = 1;
        constexpr static uint8_t companies[]         = {
            0x00, 0x19, 0x58 // BT SIG registered CompanyID
        };

        constexpr static uint8_t eventsNum     = 6;
        constexpr static uint8_t events[]      = {AVRCP_NOTIFICATION_EVENT_PLAYBACK_STATUS_CHANGED,
                                             AVRCP_NOTIFICATION_EVENT_TRACK_CHANGED,
                                             AVRCP_NOTIFICATION_EVENT_PLAYER_APPLICATION_SETTING_CHANGED,
                                             AVRCP_NOTIFICATION_EVENT_NOW_PLAYING_CONTENT_CHANGED,
                                             AVRCP_NOTIFICATION_EVENT_AVAILABLE_PLAYERS_CHANGED,
                                             AVRCP_NOTIFICATION_EVENT_ADDRESSED_PLAYER_CHANGED};
        static constexpr int SDP_BUFFER_LENGTH = 200;
        struct PlaybackStatusInfo
        {
            uint8_t track_id[8];
            uint32_t song_length_ms;
            avrcp_playback_status_t status;
            uint32_t song_position_ms; // 0xFFFFFFFF if not supported
        };

        static std::array<uint8_t, SDP_BUFFER_LENGTH> sdpTargetServiceBuffer;
        static std::array<uint8_t, SDP_BUFFER_LENGTH> sdpControllerServiceBuffer;

        static avrcp_track_t tracks[3];
        static int currentTrackIndex;
        static PlaybackStatusInfo playInfo;
        static MediaContext mediaTracker;

        static void packetHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void targetPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void controllerPacketHandler(uint8_t packetType, uint16_t channel, uint8_t *packet, uint16_t size);
        static void init();
    };
} // namespace Bt

M module-bluetooth/Bluetooth/interface/profiles/GAP.cpp => module-bluetooth/Bluetooth/interface/profiles/GAP.cpp +5 -5
@@ 1,11 1,11 @@
#include <Bluetooth/Device.hpp>
#include <log/log.hpp>
#include "BluetoothWorker.hpp"
#include "Device.hpp"
#include "Service/Bus.hpp"
#include <vector>
#include <Bluetooth/Device.hpp>
#include <Bluetooth/Error.hpp>
#include <log/log.hpp>
#include <module-services/service-bluetooth/messages/BluetoothMessage.hpp>
#include "BluetoothWorker.hpp"
#include "Device.hpp"
#include <vector>

extern "C"
{

M module-bluetooth/Bluetooth/interface/profiles/PAN.cpp => module-bluetooth/Bluetooth/interface/profiles/PAN.cpp +6 -6
@@ 2,30 2,30 @@

extern "C"
{
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include "btstack_config.h"
#include "module-bluetooth/Bluetooth/btstack_config.h"

#include <btstack_event.h>
// #include <btstack_link_key_db_fs.h>
#include <bluetooth_company_id.h>
#include <btstack_memory.h>
#include <btstack_run_loop.h>
#include <btstack_run_loop_freertos.h>
#include <bluetooth_company_id.h>
#include <btstack_stdin.h>
#include <hci.h>
#include <hci_dump.h>
#include <btstack_stdin.h>
    // #include <btstack_tlv_posix.h>

#include <bluetooth_sdp.h>
#include <bnep_lwip.h>
#include <btstack_chipset_cc256x.h>
#include <pan.h>
#include <sdp_util.h>
#include <bnep_lwip.h>
#include <bluetooth_sdp.h>
};

#include <BtCommand.hpp>

A module-bluetooth/Bluetooth/interface/profiles/Profile.hpp => module-bluetooth/Bluetooth/interface/profiles/Profile.hpp +15 -0
@@ 0,0 1,15 @@
#pragma once

#include "Error.hpp"

namespace Bt
{
    class Profile
    {
      public:
        virtual ~Profile()                           = default;
        virtual auto init() -> Error                 = 0;
        virtual void setDeviceAddress(uint8_t *addr) = 0;
    };

} // namespace Bt

M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +10 -4
@@ 6,9 6,12 @@ set(CMAKE_CXX_STANDARD 17)

set(SOURCES
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BluetoothWorker.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtstackWorker.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtKeysStorage.cpp
)
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtstackWorker.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/BtKeysStorage.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/A2DP.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVRCP.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVDTP.cpp
    )

message("Build with BlueKitchen")
include(lib/btstack.cmake)


@@ 39,13 42,16 @@ target_compile_options(${PROJECT_NAME}
)
target_link_options(${PROJECT_NAME} PUBLIC ${TARGET_LINK_OPTIONS})


target_include_directories(
    ${PROJECT_NAME}
    PUBLIC
    ${BOARD_DIR_INCLUDES}
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${TARGET_LIBRARIES_INCLUDES}
    ${BT_STACK_ROOT}/src
    ${BT_STACK_ROOT}/src/classic


)

target_link_libraries(

M module-bluetooth/lib/btstack.cmake => module-bluetooth/lib/btstack.cmake +64 -16
@@ 2,7 2,6 @@ set(BT_GLU "${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/glucode/")
set(BT_INT "${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/")
set(BT_STACK_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/lib/btstack")


set(BT_CORE
    ${BT_STACK_ROOT}/src/btstack_memory.c
    ${BT_STACK_ROOT}/src/btstack_linked_list.c


@@ 94,6 93,11 @@ set(BNEP_LWIP
    # 	a2dp_sink.c            \
    # 	btstack_ring_buffer.c \

include_directories(
        ${BT_STACK_ROOT}
        ${BT_STACK_ROOT}/src
        ${BT_STACK_ROOT}/src/classic)

set(TARGET_LIBRARIES_INCLUDES
    "${BT_INT}"
    ${BT_STACK_ROOT}/platform/freertos/


@@ 134,22 138,66 @@ list(APPEND TARGET_LIBRARIES_INCLUDES ${LWIP_INCLUDE_DIRS})
list(APPEND TARGET_LIBRARIES_INCLUDES
    ${BT_STACK_ROOT}/platform/lwip
    )

set(BOARD_DIR_SOURCES
            ${BT_INT}/GAP.cpp
            ${BT_INT}/PAN.cpp

            ${BT_GLU}/bluetooth_init_cc2564C_1.0.c
            ${BT_GLU}/btstack_uart_block_rt1051.cpp
            ${BT_GLU}/btstack_uart_block_rt1051.h
            ${BT_GLU}/hal_time_ms.c
            ${BT_STACK_ROOT}/chipset/cc256x/btstack_chipset_cc256x.c
            ${BT_STACK_ROOT}/platform/freertos/btstack_run_loop_freertos.c
            ${BT_STACK_ROOT}/src/hci_transport_h4.c
            ${BT_CORE}
            ${BT_COMMON}
            ${BT_CLASSIC}
            ${BNEP_LWIP}
    ${BT_INT}/GAP.cpp
    ${BT_INT}/PAN.cpp

    ${BT_GLU}/bluetooth_init_cc2564C_1.0.c
    ${BT_GLU}/btstack_uart_block_rt1051.cpp
    ${BT_GLU}/btstack_uart_block_rt1051.h
    ${BT_GLU}/hal_time_ms.c
    ${BT_STACK_ROOT}/chipset/cc256x/btstack_chipset_cc256x.c
    ${BT_STACK_ROOT}/platform/freertos/btstack_run_loop_freertos.c
    ${BT_STACK_ROOT}/src/hci_transport_h4.c
    ${BT_CORE}
    ${BT_COMMON}
    ${BT_CLASSIC}
    ${BNEP_LWIP}
    ${BT_STACK_ROOT}/3rd-party/hxcmod-player/mods/nao-deceased_by_disease.c
    ${BT_STACK_ROOT}/3rd-party/hxcmod-player/hxcmod.c
    ${BT_STACK_ROOT}/src/classic/btstack_sbc_encoder_bluedroid.c
    ${BT_STACK_ROOT}/src/classic/a2dp_source.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_encoder.c
    ${BT_STACK_ROOT}/src/classic/avdtp_util.c
    ${BT_STACK_ROOT}/src/classic/avdtp_source.c
    ${BT_STACK_ROOT}/src/classic/avdtp.c
    ${BT_STACK_ROOT}/src/classic/avrcp.c
    ${BT_STACK_ROOT}/src/classic/avrcp_controller.c
    ${BT_STACK_ROOT}/src/classic/avdtp_acceptor.c
    ${BT_STACK_ROOT}/src/classic/avdtp_initiator.c
    ${BT_STACK_ROOT}/src/classic/sdp_client.c
    ${BT_STACK_ROOT}/src/classic/avrcp_target.c
    ${BT_STACK_ROOT}/src/classic/hsp_ag.c
    ${BT_STACK_ROOT}/src/classic/hfp_msbc.c
    ${BT_STACK_ROOT}/src/classic/btstack_cvsd_plc.c
    ${BT_STACK_ROOT}/src/classic/btstack_sbc_plc.c
    ${BT_STACK_ROOT}/src/classic/sdp_client_rfcomm.c

    ${BT_STACK_ROOT}/src/classic/btstack_sbc_decoder_bluedroid.c
    ${BT_STACK_ROOT}/src/btstack_ring_buffer.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_analysis.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_dct.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_dct_coeffs.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_enc_bit_alloc_mono.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_enc_bit_alloc_ste.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_enc_bit_alloc_ste.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_enc_coeffs.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/encoder/srce/sbc_packing.c

    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/alloc.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/bitalloc.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/bitalloc-sbc.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/bitstream-decode.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/decoder-oina.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/decoder-private.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/decoder-sbc.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/dequant.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/framing.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/framing-sbc.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/oi_codec_version.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/synthesis-sbc.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/synthesis-dct8.c
    ${BT_STACK_ROOT}/3rd-party/bluedroid/decoder/srce/synthesis-8-generated.c
    )

if(${PROJECT_TARGET} STREQUAL "TARGET_Linux")

M module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp => module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp +7 -5
@@ 241,14 241,16 @@ void BluetoothCommon::set_irq(bool enable)
    LPUART_EnableTx(BSP_BLUETOOTH_UART_BASE, false);
    LPUART_ClearStatusFlags(BSP_BLUETOOTH_UART_BASE, 0xFFFFFFFF);
    if (enable) {
        LPUART_EnableInterrupts(BSP_BLUETOOTH_UART_BASE, kLPUART_RxDataRegFullInterruptEnable);
        LPUART_EnableInterrupts(BSP_BLUETOOTH_UART_BASE,
                                kLPUART_RxDataRegFullInterruptEnable | kLPUART_IdleLineInterruptEnable);
    }
    else {
        LPUART_DisableInterrupts(BSP_BLUETOOTH_UART_BASE, kLPUART_RxDataRegFullInterruptEnable);
        LPUART_DisableInterrupts(BSP_BLUETOOTH_UART_BASE,
                                 kLPUART_RxDataRegFullInterruptEnable | kLPUART_IdleLineInterruptEnable);
    }
    // LPUART_EnableInterrupts(BSP_BLUETOOTH_UART_BASE,
    // kLPUART_RxDataRegFullInterruptEnable|kLPUART_TxDataRegEmptyInterruptEnable|kLPUART_TransmissionCompleteInterruptEnable|kLPUART_RxOverrunInterruptEnable
    // );
    //     LPUART_EnableInterrupts(BSP_BLUETOOTH_UART_BASE,
    //     kLPUART_RxDataRegFullInterruptEnable|kLPUART_TxDataRegEmptyInterruptEnable|kLPUART_TransmissionCompleteInterruptEnable|kLPUART_RxOverrunInterruptEnable
    //     );
    LPUART_EnableRx(BSP_BLUETOOTH_UART_BASE, true);
    LPUART_EnableTx(BSP_BLUETOOTH_UART_BASE, true);
}

M module-bsp/bsp/bluetooth/Bluetooth.cpp => module-bsp/bsp/bluetooth/Bluetooth.cpp +1 -0
@@ 21,4 21,5 @@ namespace bsp {
            va_end(args);
        }
    }

};

M module-bsp/bsp/bluetooth/Bluetooth.hpp => module-bsp/bsp/bluetooth/Bluetooth.hpp +1 -0
@@ 132,6 132,7 @@ namespace bsp {
    };

    /// definitions needed by BT stack

    class BlueKitchen : public BluetoothCommon {
        public:
            BlueKitchen(unsigned int in_size=default_buff_size, unsigned int out_size=default_buff_size);

M module-db/Interface/ThreadRecord.cpp => module-db/Interface/ThreadRecord.cpp +1 -1
@@ 84,7 84,7 @@ std::unique_ptr<std::vector<ThreadRecord>> ThreadRecordInterface::GetLimitOffset
{
    auto records = std::make_unique<std::vector<ThreadRecord>>();

    ThreadsTableFields threadsField;
    ThreadsTableFields threadsField = ThreadsTableFields();
    switch (field) {
    case ThreadRecordField::ContactID: {
        threadsField = ThreadsTableFields::ContactID;

M module-services/service-bluetooth/ServiceBluetooth.cpp => module-services/service-bluetooth/ServiceBluetooth.cpp +9 -0
@@ 44,6 44,7 @@ sys::Message_t ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg, sys:
            switch (lmsg->req) {
            case BluetoothMessage::Start:
                worker->run();

                break;
            case BluetoothMessage::Scan:
                if (worker->scan()) {


@@ 73,6 74,13 @@ sys::Message_t ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg, sys:
                worker->set_visible();
                break;

            case BluetoothMessage::Play:
                worker->play_audio();
                break;
            case BluetoothMessage::Stop:
                worker->stop_audio();
                break;

            default:
                break;
            }


@@ 90,6 98,7 @@ sys::Message_t ServiceBluetooth::DataReceivedHandler(sys::DataMessage *msg, sys:
        auto addrMsg = static_cast<BluetoothAddrMessage *>(msg);
        worker->set_addr(addrMsg->addr);
    }

    return std::make_shared<sys::ResponseMessage>();
}


M module-services/service-bluetooth/ServiceBluetooth.hpp => module-services/service-bluetooth/ServiceBluetooth.hpp +1 -1
@@ 1,7 1,7 @@
#pragma once

#include "Service/Service.hpp"
#include "Bluetooth/BluetoothWorker.hpp"
#include "Service/Service.hpp"
#include <memory>

class ServiceBluetooth : public sys::Service

M module-services/service-bluetooth/messages/BluetoothMessage.hpp => module-services/service-bluetooth/messages/BluetoothMessage.hpp +36 -5
@@ 1,5 1,7 @@
#pragma once

#include "Bluetooth/Device.hpp"
#include "MessageType.hpp"
#include "Service/Message.hpp"

#include <utility>


@@ 22,6 24,8 @@ class BluetoothMessage : public sys::DataMessage
        StopScan,
        PAN,
        Visible,
        Play,
        Stop
    };
    enum Request req = Request::None;
    BluetoothMessage(enum Request req = None) : sys::DataMessage(MessageType::BluetoothRequest), req(req){};


@@ 37,6 41,23 @@ class BluetoothScanResultMessage : public sys::DataMessage
    ~BluetoothScanResultMessage() override = default;
};

class BluetoothPairResultMessage : public sys::DataMessage
{
  public:
    bool status;
    explicit BluetoothPairResultMessage(bool status)
        : sys::DataMessage(MessageType::BluetoothPairResult), status(status){};
};

class BluetoothScanMessage : public sys::DataMessage
{
  public:
    std::vector<Devicei> devices;
    BluetoothScanMessage(std::vector<Devicei> devices)
        : sys::DataMessage(MessageType::BluetoothScanResult), devices(std::move(devices)){};
    ~BluetoothScanMessage() override = default;
};

class BluetoothAddrMessage : public sys::DataMessage
{
  public:


@@ 48,12 69,22 @@ class BluetoothAddrMessage : public sys::DataMessage
    ~BluetoothAddrMessage() override = default;
};

class BluetoothPairResultMessage : public sys::DataMessage
class BluetoothAudioRegisterMessage : public sys::DataMessage
{
  public:
    bool status;
    explicit BluetoothPairResultMessage(bool status)
        : sys::DataMessage(MessageType::BluetoothPairResult), status(status){};
    QueueHandle_t audioSourceQueue;
    QueueHandle_t audioSinkQueue;
    BluetoothAudioRegisterMessage(QueueHandle_t audioSourceQueue, QueueHandle_t audioSinkQueue)
        : sys::DataMessage(MessageType::BluetoothAudioRegister), audioSourceQueue(audioSourceQueue),
          audioSinkQueue(audioSinkQueue){};
    ~BluetoothAudioRegisterMessage() override = default;
};

    ~BluetoothPairResultMessage() override = default;
class BluetoothDeviceMetadataMessage : public sys::DataMessage
{
  public:
    DeviceMetadata_t metadata;
    BluetoothDeviceMetadataMessage(DeviceMetadata_t metadata)
        : DataMessage(MessageType::BluetoothDeviceMetadata), metadata(std::move(metadata)){};
    ~BluetoothDeviceMetadataMessage() override = default;
};

M source/MessageType.hpp => source/MessageType.hpp +2 -0
@@ 184,6 184,8 @@ enum class MessageType
    BluetoothScanResult,
    BluetoothAddrResult,
    BluetoothPairResult,
    BluetoothAudioRegister,
    BluetoothDeviceMetadata,

    LwIP_request,
    EVM_GPIO,