M module-audio/Audio/AudioCommon.hpp => module-audio/Audio/AudioCommon.hpp +1 -0
@@ 94,6 94,7 @@ namespace audio
// HW state change notifications
JackState, //!< jack input plugged / unplugged event
BlutoothHSPDeviceState, //!< BT device connected / disconnected event (Headset Profile)
+ BlutoothHFPDeviceState, //!< BT device connected / disconnected event (Headset Profile)
BlutoothA2DPDeviceState, //!< BT device connected / disconnected event (Advanced Audio Distribution Profile)
// call control
M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp +2 -3
@@ 5,14 5,13 @@
#include <interface/profiles/A2DP/AVDTP.hpp>
#include <interface/profiles/A2DP/AVRCP.hpp>
-#include <interface/profiles/HSP/SCO.hpp>
+#include <interface/profiles/SCO/SCO.hpp>
+#include <interface/profiles/SCO/ScoUtils.hpp>
#include <Audio/AudioCommon.hpp>
#include <Audio/VolumeScaler.hpp>
#include <Audio/Stream.hpp>
-#include <module-bluetooth/Bluetooth/interface/profiles/HSP/ScoUtils.hpp>
-
#include <chrono>
#include <cassert>
A module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.cpp +406 -0
@@ 0,0 1,406 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "HFPImpl.hpp"
+#include "HFP.hpp"
+
+#include <Bluetooth/Error.hpp>
+#include <log/log.hpp>
+#include <service-evtmgr/Constants.hpp>
+#include <service-audio/AudioMessage.hpp>
+#include <BluetoothWorker.hpp>
+
+extern "C"
+{
+#include "btstack.h"
+#include "btstack_run_loop_freertos.h"
+#include "btstack_stdin.h"
+#include <btstack_defines.h>
+}
+
+namespace bluetooth
+{
+ HFP::HFP() : pimpl(std::make_unique<HFPImpl>(HFPImpl()))
+ {}
+
+ HFP::HFP(HFP &&other) noexcept : pimpl(std::move(other.pimpl))
+ {}
+
+ auto HFP::operator=(HFP &&other) noexcept -> HFP &
+ {
+ if (&other == this) {
+ return *this;
+ }
+ pimpl = std::move(other.pimpl);
+ return *this;
+ }
+
+ auto HFP::init() -> Error::Code
+ {
+ return pimpl->init();
+ }
+
+ void HFP::setDeviceAddress(uint8_t *addr)
+ {
+ pimpl->setDeviceAddress(addr);
+ }
+
+ void HFP::setOwnerService(const sys::Service *service)
+ {
+ ownerService = service;
+ pimpl->setOwnerService(service);
+ }
+
+ void HFP::connect()
+ {
+ pimpl->connect();
+ }
+
+ void HFP::disconnect()
+ {
+ pimpl->disconnect();
+ }
+
+ void HFP::start()
+ {
+ pimpl->start();
+ }
+
+ void HFP::stop()
+ {
+ pimpl->stop();
+ }
+ auto HFP::startRinging() const noexcept -> Error::Code
+ {
+ return Error::Success;
+ }
+ auto HFP::stopRinging() const noexcept -> Error::Code
+ {
+ return Error::Success;
+ }
+ auto HFP::initializeCall() const noexcept -> Error::Code
+ {
+ pimpl->start();
+ return Error::Success;
+ }
+
+ HFP::~HFP() = default;
+
+ hci_con_handle_t HFP::HFPImpl::scoHandle = HCI_CON_HANDLE_INVALID;
+ hci_con_handle_t HFP::HFPImpl::aclHandle = HCI_CON_HANDLE_INVALID;
+ bd_addr_t HFP::HFPImpl::deviceAddr;
+ std::array<uint8_t, serviceBufferLength> HFP::HFPImpl::serviceBuffer;
+ std::unique_ptr<SCO> HFP::HFPImpl::sco;
+ std::unique_ptr<CellularInterface> HFP::HFPImpl::cellularInterface = nullptr;
+ const sys::Service *HFP::HFPImpl::ownerService;
+ std::string HFP::HFPImpl::agServiceName = "PurePhone HFP";
+ bool HFP::HFPImpl::isConnected = false;
+ SCOCodec HFP::HFPImpl::codec = SCOCodec::CVSD;
+
+ int HFP::HFPImpl::memory_1_enabled = 1;
+ btstack_packet_callback_registration_t HFP::HFPImpl::hci_event_callback_registration;
+ [[maybe_unused]] int HFP::HFPImpl::ag_indicators_nr = 7;
+ hfp_ag_indicator_t HFP::HFPImpl::ag_indicators[] = {
+ // index, name, min range, max range, status, mandatory, enabled, status changed
+ {1, "service", 0, 1, 1, 0, 0, 0},
+ {2, "call", 0, 1, 0, 1, 1, 0},
+ {3, "callsetup", 0, 3, 0, 1, 1, 0},
+ {4, "battchg", 0, 5, 3, 0, 0, 0},
+ {5, "signal", 0, 5, 5, 0, 1, 0},
+ {6, "roam", 0, 1, 0, 0, 1, 0},
+ {7, "callheld", 0, 2, 0, 1, 1, 0}};
+ [[maybe_unused]] int HFP::HFPImpl::call_hold_services_nr = 5;
+ const char *HFP::HFPImpl::call_hold_services[] = {"1", "1x", "2", "2x", "3"};
+ [[maybe_unused]] int HFP::HFPImpl::hf_indicators_nr = 2;
+ [[maybe_unused]] hfp_generic_status_indicator_t HFP::HFPImpl::hf_indicators[] = {
+ {1, 1},
+ {2, 1},
+ };
+ void HFP::HFPImpl::dump_supported_codecs(void)
+ {
+ LOG_DEBUG("Supported codecs: ");
+
+ switch (codec) {
+ case SCOCodec::CVSD:
+ LOG_DEBUG("CVSD");
+ break;
+ case SCOCodec::both:
+ LOG_DEBUG("CVSD");
+ [[fallthrough]];
+ case SCOCodec::mSBC:
+ if (hci_extended_sco_link_supported()) {
+ LOG_DEBUG("mSBC");
+ }
+ else {
+ LOG_WARN("mSBC codec disabled because eSCO not supported by local controller.\n");
+ }
+ break;
+
+ case SCOCodec::other:
+ LOG_WARN("Using other codec");
+ break;
+ }
+ }
+ void HFP::HFPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState state)
+ {
+ auto evt = std::make_shared<audio::Event>(event, state);
+ auto msg = std::make_shared<AudioEventRequest>(std::move(evt));
+ auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
+ busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
+ }
+
+ void HFP::HFPImpl::packetHandler(uint8_t packetType, uint16_t channel, uint8_t *event, uint16_t eventSize)
+ {
+ switch (packetType) {
+ case HCI_SCO_DATA_PACKET:
+ if (READ_SCO_CONNECTION_HANDLE(event) != scoHandle) {
+ break;
+ }
+ LOG_DEBUG("Processing SCO receive");
+
+ sco->receive(event, eventSize);
+ break;
+
+ case HCI_EVENT_PACKET:
+ processHCIEvent(event);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void HFP::HFPImpl::processHCIEvent(uint8_t *event)
+ {
+ LOG_DEBUG("Inside hfp hci event");
+ switch (hci_event_packet_get_type(event)) {
+ case HCI_EVENT_SCO_CAN_SEND_NOW:
+ LOG_DEBUG("SCO send event");
+
+ sco->send(scoHandle);
+ break;
+ case HCI_EVENT_HFP_META:
+ LOG_DEBUG("Processsing HFP event");
+ processHFPEvent(event);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void HFP::HFPImpl::processHFPEvent(uint8_t *event)
+ {
+ // NOTE some of the subevents are not mentioned here yet
+ /*
+ *
+ * in hfp_ag.h there are descriptions of cellular and UI interface of HFP:
+ * // Cellular Actions
+ * and
+ * // actions used by local device / user
+ * those should be called to trigger requested state in HFP's state machine
+ * (which this switch...case depends on)
+ */
+ switch (hci_event_hfp_meta_get_subevent_code(event)) {
+ case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED:
+ std::uint8_t status;
+ status = hfp_subevent_service_level_connection_established_get_status(event);
+ if (status) {
+ LOG_DEBUG("Connection failed, status 0x%02x\n", status);
+ break;
+ }
+ aclHandle = hfp_subevent_service_level_connection_established_get_acl_handle(event);
+ hfp_subevent_service_level_connection_established_get_bd_addr(event, deviceAddr);
+ LOG_DEBUG("Service level connection established to %s.\n", bd_addr_to_str(deviceAddr));
+ isConnected = true;
+ sendAudioEvent(audio::EventType::BlutoothHFPDeviceState, audio::Event::DeviceState::Connected);
+ dump_supported_codecs();
+ break;
+ case HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED:
+ LOG_DEBUG("Service level connection released.\n");
+ aclHandle = HCI_CON_HANDLE_INVALID;
+
+ sendAudioEvent(audio::EventType::BlutoothHFPDeviceState, audio::Event::DeviceState::Disconnected);
+
+ break;
+ case HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED:
+ if (hfp_subevent_audio_connection_established_get_status(event)) {
+ LOG_DEBUG("Audio connection establishment failed with status %u\n",
+ hfp_subevent_audio_connection_established_get_status(event));
+ }
+ else {
+ scoHandle = hfp_subevent_audio_connection_established_get_sco_handle(event);
+ LOG_DEBUG("Audio connection established with SCO handle 0x%04x.\n", scoHandle);
+ codec = static_cast<SCOCodec>(hfp_subevent_audio_connection_established_get_negotiated_codec(event));
+ dump_supported_codecs();
+
+ sco->setCodec(codec);
+ hci_request_sco_can_send_now_event();
+ RunLoop::trigger();
+ }
+ break;
+ case HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED:
+ LOG_DEBUG("Audio connection released\n");
+ scoHandle = HCI_CON_HANDLE_INVALID;
+ break;
+ case HFP_SUBEVENT_START_RINGINIG:
+ LOG_DEBUG("Start Ringing\n");
+
+ // todo request here ringtone stream
+ break;
+ case HFP_SUBEVENT_STOP_RINGINIG:
+ LOG_DEBUG("Stop Ringing\n");
+ // todo stop ringtone stream here
+ break;
+ case HFP_SUBEVENT_PLACE_CALL_WITH_NUMBER:
+ LOG_DEBUG("Outgoing call '%s'\n", hfp_subevent_place_call_with_number_get_number(event));
+ // todo has to be feeded with proper phone number from cellular
+ if (strcmp("1234567", hfp_subevent_place_call_with_number_get_number(event)) == 0 ||
+ strcmp("7654321", hfp_subevent_place_call_with_number_get_number(event)) == 0 ||
+ (memory_1_enabled && strcmp(">1", hfp_subevent_place_call_with_number_get_number(event)) == 0)) {
+ LOG_DEBUG("Dialstring valid: accept call\n");
+ hfp_ag_outgoing_call_accepted();
+ }
+ else {
+ LOG_DEBUG("Dialstring invalid: reject call\n");
+ hfp_ag_outgoing_call_rejected();
+ }
+ break;
+
+ case HFP_SUBEVENT_ATTACH_NUMBER_TO_VOICE_TAG:
+ // todo has to be feeded with proper phone number from cellular
+ // LOG_DEBUG("Attach number to voice tag. Sending '1234567\n");
+ hfp_ag_send_phone_number_for_voice_tag(aclHandle, "1234567");
+ break;
+ case HFP_SUBEVENT_TRANSMIT_DTMF_CODES:
+ LOG_DEBUG("Send DTMF Codes: '%s'\n", hfp_subevent_transmit_dtmf_codes_get_dtmf(event));
+ hfp_ag_send_dtmf_code_done(aclHandle);
+ break;
+ case HFP_SUBEVENT_CALL_ANSWERED:
+ LOG_DEBUG("Call answered by HF\n");
+ cellularInterface->answerIncomingCall(const_cast<sys::Service *>(ownerService));
+ break;
+ case HFP_SUBEVENT_CALL_TERMINATED:
+ LOG_DEBUG("Call terminated by HF\n");
+ cellularInterface->hangupCall(const_cast<sys::Service *>(ownerService));
+ break;
+ default:
+ LOG_DEBUG("Event not handled %u\n", hci_event_hfp_meta_get_subevent_code(event));
+ break;
+ }
+ }
+
+ void HFP::HFPImpl::establishAudioConnection()
+ {
+ LOG_DEBUG("Establish Audio connection to %s...\n", bd_addr_to_str(deviceAddr));
+ hfp_ag_establish_audio_connection(aclHandle);
+ }
+ static hfp_phone_number_t subscriber_number = {129, "225577"};
+ auto HFP::HFPImpl::init() -> Error::Code
+ {
+ sco = std::make_unique<SCO>();
+ sco->setOwnerService(ownerService);
+ sco->init();
+
+ cellularInterface = std::make_unique<CellularInterfaceImpl>();
+
+ Profile::initL2cap();
+ Profile::initSdp();
+
+ serviceBuffer.fill(0);
+ constexpr std::uint32_t hspSdpRecordHandle = 0x10001;
+ uint16_t supported_features = (1 << HFP_AGSF_ESCO_S4) | (1 << HFP_AGSF_HF_INDICATORS) |
+ (1 << HFP_AGSF_CODEC_NEGOTIATION) | (1 << HFP_AGSF_EXTENDED_ERROR_RESULT_CODES) |
+ (1 << HFP_AGSF_ENHANCED_CALL_CONTROL) | (1 << HFP_AGSF_ENHANCED_CALL_STATUS) |
+ (1 << HFP_AGSF_ABILITY_TO_REJECT_A_CALL) | (1 << HFP_AGSF_IN_BAND_RING_TONE) |
+ (1 << HFP_AGSF_VOICE_RECOGNITION_FUNCTION) | (1 << HFP_AGSF_THREE_WAY_CALLING);
+ int wide_band_speech = 0;
+ hfp_ag_create_sdp_record(serviceBuffer.data(),
+ hspSdpRecordHandle,
+ rfcommChannelNr,
+ agServiceName.c_str(),
+ 0,
+ supported_features,
+ wide_band_speech);
+
+ if (const auto status = sdp_register_service(serviceBuffer.data()); status != ERROR_CODE_SUCCESS) {
+ LOG_ERROR("Can't register service. Status %x", status);
+ }
+ rfcomm_init();
+ hfp_ag_init(rfcommChannelNr);
+ hfp_ag_init_supported_features(supported_features);
+ initCodecs();
+ hfp_ag_init_ag_indicators(ag_indicators_nr, ag_indicators);
+ hfp_ag_init_hf_indicators(hf_indicators_nr, hf_indicators);
+ hfp_ag_init_call_hold_services(call_hold_services_nr, call_hold_services);
+ hfp_ag_set_subcriber_number_information(&subscriber_number, 1);
+
+ hci_event_callback_registration.callback = &packetHandler;
+ hci_add_event_handler(&hci_event_callback_registration);
+ hci_register_sco_packet_handler(&packetHandler);
+
+ hfp_ag_register_packet_handler(&packetHandler);
+
+ LOG_INFO("HFP init done!");
+
+ return bluetooth::Error::Success;
+ }
+
+ void HFP::HFPImpl::connect()
+ {
+ if (!isConnected) {
+ LOG_DEBUG("Connecting the HFP profile");
+ hfp_ag_establish_service_level_connection(deviceAddr);
+ }
+ }
+
+ void HFP::HFPImpl::disconnect()
+ {
+ hfp_ag_release_service_level_connection(aclHandle);
+ }
+
+ void HFP::HFPImpl::setDeviceAddress(bd_addr_t addr)
+ {
+ bd_addr_copy(deviceAddr, addr);
+ LOG_DEBUG("Address set!");
+ }
+
+ void HFP::HFPImpl::setOwnerService(const sys::Service *service)
+ {
+ ownerService = service;
+ }
+
+ auto HFP::HFPImpl::getStreamData() -> std::shared_ptr<BluetoothStreamData>
+ {
+ return sco->getStreamData();
+ }
+ void HFP::HFPImpl::start()
+ {
+ if (!isConnected) {
+ connect();
+ }
+ establishAudioConnection();
+ }
+ void HFP::HFPImpl::stop()
+ {
+ hfp_ag_release_audio_connection(aclHandle);
+ }
+
+ void HFP::HFPImpl::initCodecs()
+ {
+ std::vector<SCOCodec> codecsList;
+ switch (codec) {
+
+ case SCOCodec::other:
+ case SCOCodec::CVSD:
+ codecsList.push_back(SCOCodec::CVSD);
+ break;
+ case SCOCodec::mSBC:
+ codecsList.push_back(SCOCodec::mSBC);
+ break;
+ case SCOCodec::both:
+ codecsList.push_back(SCOCodec::CVSD);
+ codecsList.push_back(SCOCodec::mSBC);
+ break;
+ }
+ hfp_ag_init_codecs(codecsList.size(), reinterpret_cast<uint8_t *>(codecsList.data()));
+ }
+} // namespace bluetooth
A module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.hpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFP.hpp +57 -0
@@ 0,0 1,57 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "Profile.hpp"
+#include <interface/profiles/PhoneInterface.hpp>
+#include <service-bluetooth/BluetoothMessage.hpp>
+#include <btstack_run_loop.h>
+
+namespace bluetooth
+{
+
+ class HFP : public Profile
+ {
+ // static constexpr auto CLASS_OF_DEVICE = 0x400204;
+ // Service class: Telephony, Major device class: Phone, Minor device class: Cellular
+ public:
+ HFP();
+ ~HFP() override;
+
+ HFP(const HFP &other) = delete;
+ auto operator=(const HFP &rhs) -> HFP & = delete;
+ HFP(HFP &&other) noexcept;
+ auto operator=(HFP &&other) noexcept -> HFP &;
+
+ auto init() -> Error::Code override;
+ void setDeviceAddress(uint8_t *addr) override;
+ void setOwnerService(const sys::Service *service) override;
+
+ void connect() override;
+ void disconnect() override;
+ void start() override;
+ void stop() override;
+ /// @brief Starts ring
+ /// @return Success
+ [[nodiscard]] auto startRinging() const noexcept -> Error::Code override;
+ /// @brief Stops ring
+ /// @return Success
+ [[nodiscard]] auto stopRinging() const noexcept -> Error::Code override;
+ /// @brief Initializes bluetooth audio call which is divided into two parts:
+ /// - Ring stop
+ /// - SCO link establishment
+ /// @return Success
+ [[nodiscard]] auto initializeCall() const noexcept -> Error::Code override;
+
+ void setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice) override
+ {}
+
+ private:
+ class HFPImpl;
+ std::unique_ptr<HFPImpl> pimpl;
+ const sys::Service *ownerService{};
+ btstack_run_loop *runLoopInstance{};
+ };
+
+} // namespace bluetooth
A module-bluetooth/Bluetooth/interface/profiles/HFP/HFPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HFP/HFPImpl.hpp +58 -0
@@ 0,0 1,58 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+#include "HFP.hpp"
+#include "Error.hpp"
+#include <interface/profiles/SCO/SCO.hpp>
+#include <Audio/AudioCommon.hpp>
+
+namespace bluetooth
+{
+ static constexpr int serviceBufferLength = 150;
+ static constexpr int commandBufferLength = 150;
+
+ class HFP::HFPImpl
+ {
+ public:
+ static void packetHandler(uint8_t packetType, uint16_t channel, uint8_t *event, uint16_t eventSize);
+ auto init() -> Error::Code;
+ void start();
+ void stop();
+ void startRinging() const noexcept;
+ void stopRinging() const noexcept;
+ void initializeCall() const noexcept;
+ void connect();
+ void disconnect();
+ void setDeviceAddress(bd_addr_t addr);
+ void setOwnerService(const sys::Service *service);
+ auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;
+
+ private:
+ static void sendAudioEvent(audio::EventType event, audio::Event::DeviceState state);
+ static void processHCIEvent(uint8_t *event);
+ static void processHFPEvent(uint8_t *event);
+ static void establishAudioConnection();
+ static void initCodecs();
+ static void dump_supported_codecs(void);
+ static std::array<uint8_t, serviceBufferLength> serviceBuffer;
+ static constexpr uint8_t rfcommChannelNr = 1;
+ static std::string agServiceName;
+ static hci_con_handle_t scoHandle;
+ static hci_con_handle_t aclHandle;
+ static std::unique_ptr<SCO> sco;
+ static std::unique_ptr<CellularInterface> cellularInterface;
+ static bd_addr_t deviceAddr;
+ static const sys::Service *ownerService;
+ static bool isConnected;
+ static SCOCodec codec;
+ static int memory_1_enabled;
+ static btstack_packet_callback_registration_t hci_event_callback_registration;
+ [[maybe_unused]] static int ag_indicators_nr;
+ static hfp_ag_indicator_t ag_indicators[7];
+ [[maybe_unused]] static int call_hold_services_nr;
+ static const char *call_hold_services[5];
+ [[maybe_unused]] static int hf_indicators_nr;
+ [[maybe_unused]] static hfp_generic_status_indicator_t hf_indicators[2];
+ };
+} // namespace bluetooth
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.cpp +1 -16
@@ 8,7 8,7 @@
#include <Bluetooth/Error.hpp>
#include <service-evtmgr/Constants.hpp>
#include <BluetoothWorker.hpp>
-#include <module-bluetooth/Bluetooth/interface/profiles/HSP/ScoUtils.hpp>
+#include <module-bluetooth/Bluetooth/interface/profiles/SCO/ScoUtils.hpp>
#include <service-audio/AudioMessage.hpp>
#include <service-bluetooth/Constants.hpp>
#include <service-bluetooth/messages/AudioVolume.hpp>
@@ 26,21 26,6 @@ extern "C"
namespace bluetooth
{
- bool CellularInterfaceImpl::answerIncomingCall(sys::Service *service)
- {
- return CellularServiceAPI::AnswerIncomingCall(service);
- }
-
- bool CellularInterfaceImpl::hangupCall(sys::Service *service)
- {
- return CellularServiceAPI::HangupCall(service);
- }
-
- bool AudioInterfaceImpl::startAudioRouting(sys::Service *service)
- {
- return AudioServiceAPI::RoutingStart(service);
- }
-
HSP::HSP() : pimpl(std::make_unique<HSPImpl>(HSPImpl()))
{}
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSP.hpp +1 -24
@@ 6,33 6,10 @@
#include "Profile.hpp"
#include <service-bluetooth/BluetoothMessage.hpp>
#include <btstack_run_loop.h>
+#include <module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.hpp>
namespace bluetooth
{
- class CellularInterface
- {
- public:
- virtual ~CellularInterface() = default;
- virtual bool answerIncomingCall(sys::Service *service) = 0;
- virtual bool hangupCall(sys::Service *service) = 0;
- };
- class AudioInterface
- {
- public:
- virtual ~AudioInterface() = default;
- virtual bool startAudioRouting(sys::Service *service) = 0;
- };
- class CellularInterfaceImpl : public CellularInterface
- {
- public:
- bool answerIncomingCall(sys::Service *service) override;
- bool hangupCall(sys::Service *service) override;
- };
- class AudioInterfaceImpl : public AudioInterface
- {
- public:
- bool startAudioRouting(sys::Service *service) override;
- };
class HSP : public Profile
{
static constexpr auto CLASS_OF_DEVICE = 0x400204;
M module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp => module-bluetooth/Bluetooth/interface/profiles/HSP/HSPImpl.hpp +1 -1
@@ 4,7 4,7 @@
#pragma once
#include "HSP.hpp"
#include "Error.hpp"
-#include "SCO.hpp"
+#include <interface/profiles/SCO/SCO.hpp>
#include <Audio/AudioCommon.hpp>
namespace bluetooth
A module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.cpp => module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.cpp +24 -0
@@ 0,0 1,24 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "PhoneInterface.hpp"
+#include "service-audio/AudioServiceAPI.hpp"
+#include "service-cellular/CellularServiceAPI.hpp"
+
+namespace bluetooth
+{
+ bool CellularInterfaceImpl::answerIncomingCall(sys::Service *service)
+ {
+ return CellularServiceAPI::AnswerIncomingCall(service);
+ }
+
+ bool CellularInterfaceImpl::hangupCall(sys::Service *service)
+ {
+ return CellularServiceAPI::HangupCall(service);
+ }
+
+ bool AudioInterfaceImpl::startAudioRouting(sys::Service *service)
+ {
+ return AudioServiceAPI::RoutingStart(service);
+ }
+} // namespace bluetooth
A module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.hpp => module-bluetooth/Bluetooth/interface/profiles/PhoneInterface.hpp +36 -0
@@ 0,0 1,36 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <Service/ServiceForward.hpp>
+
+namespace bluetooth
+{
+ class CellularInterface
+ {
+ public:
+ virtual ~CellularInterface() = default;
+ virtual bool answerIncomingCall(sys::Service *service) = 0;
+ virtual bool hangupCall(sys::Service *service) = 0;
+ };
+
+ class CellularInterfaceImpl : public CellularInterface
+ {
+ public:
+ bool answerIncomingCall(sys::Service *service) override;
+ bool hangupCall(sys::Service *service) override;
+ };
+
+ class AudioInterface
+ {
+ public:
+ virtual ~AudioInterface() = default;
+ virtual bool startAudioRouting(sys::Service *service) = 0;
+ };
+ class AudioInterfaceImpl : public AudioInterface
+ {
+ public:
+ bool startAudioRouting(sys::Service *service) override;
+ };
+} // namespace bluetooth
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.cpp +5 -1
@@ 16,7 16,7 @@ namespace bluetooth
if (!initialized) {
profilesList = {{AudioProfile::A2DP, std::make_shared<bluetooth::A2DP>()},
{AudioProfile::HSP, std::make_shared<bluetooth::HSP>()},
- {AudioProfile::HFP, nullptr},
+ {AudioProfile::HFP, std::make_shared<bluetooth::HFP>()},
{AudioProfile::None, nullptr}};
for (auto &[profileName, ptr] : profilesList) {
@@ 25,6 25,10 @@ namespace bluetooth
ptr->init();
}
}
+ // TODO profile selection based on capabilities and priority?
+ // audio & capa cell & HSP & HFP & A2DP-> HFP
+ // audio & capa cell & HSP & A2DP-> HSP
+ // audio & HSP & HFP & A2DP -> A2DP
currentProfilePtr = profilesList[AudioProfile::A2DP].get();
if (auto serviceBt = dynamic_cast<ServiceBluetooth *>(ownerService); serviceBt != nullptr) {
M module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp => module-bluetooth/Bluetooth/interface/profiles/ProfileManager.hpp +1 -0
@@ 9,6 9,7 @@
#include "AudioProfile.hpp"
#include "interface/profiles/A2DP/A2DP.hpp"
#include "interface/profiles/HSP/HSP.hpp"
+#include "interface/profiles/HFP/HFP.hpp"
#include "audio/BluetoothAudioDevice.hpp"
#include <memory>
R module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.cpp => module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.cpp +26 -3
@@ 29,6 29,7 @@ namespace bluetooth
static void send(hci_con_handle_t scoHandle);
static void receive(uint8_t *packet, uint16_t size);
void setOwnerService(const sys::Service *service);
+ void setCodec(uint8_t codec);
auto getStreamData() -> std::shared_ptr<BluetoothStreamData>;
private:
@@ 44,6 45,7 @@ namespace bluetooth
static QueueHandle_t sourceQueue;
static DeviceMetadata_t metadata;
static const sys::Service *ownerService;
+ static uint8_t negotiated_codec;
static auto audioInitialize(int sampleRate) -> Error;
static void initCvsd();
@@ 89,9 91,13 @@ namespace bluetooth
{
pimpl->setOwnerService(service);
}
+ void SCO::setCodec(SCOCodec codec)
+ {
+ pimpl->setCodec(static_cast<uint8_t>(codec));
+ }
SCO::~SCO() = default;
-} // namespace Bt
+} // namespace bluetooth
using namespace bluetooth;
@@ 100,11 106,12 @@ QueueHandle_t SCO::SCOImpl::sinkQueue;
QueueHandle_t SCO::SCOImpl::sourceQueue;
const sys::Service *SCO::SCOImpl::ownerService = nullptr;
DeviceMetadata_t SCO::SCOImpl::metadata;
+uint8_t SCO::SCOImpl::negotiated_codec;
void SCO::SCOImpl::sendEvent(audio::EventType event, audio::Event::DeviceState state)
{
- auto evt = std::make_shared<audio::Event>(event, state);
- auto msg = std::make_shared<AudioEventRequest>(std::move(evt));
+ auto evt = std::make_shared<audio::Event>(event, state);
+ auto msg = std::make_shared<AudioEventRequest>(std::move(evt));
auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
busProxy.sendUnicast(std::move(msg), service::name::evt_manager);
}
@@ 247,3 254,19 @@ void SCO::SCOImpl::setOwnerService(const sys::Service *service)
{
ownerService = service;
}
+
+void SCO::SCOImpl::setCodec(uint8_t codec)
+{
+ if (negotiated_codec == codec) {
+ return;
+ }
+ negotiated_codec = codec;
+
+ if (negotiated_codec == HFP_CODEC_MSBC) {
+ // btstack_sbc_decoder_init(&decoder_state, SBC_MODE_mSBC, &handle_pcm_data, NULL);
+ hfp_msbc_init();
+ }
+ else {
+ initCvsd();
+ }
+}
R module-bluetooth/Bluetooth/interface/profiles/HSP/SCO.hpp => module-bluetooth/Bluetooth/interface/profiles/SCO/SCO.hpp +8 -1
@@ 12,7 12,13 @@ extern "C"
};
namespace bluetooth
{
-
+ enum SCOCodec
+ {
+ CVSD = 1,
+ mSBC = 2,
+ both = 3,
+ other = 4
+ };
class SCO
{
public:
@@ 29,6 35,7 @@ namespace bluetooth
void receive(uint8_t *packet, uint16_t size);
[[nodiscard]] auto getStreamData() const -> std::shared_ptr<BluetoothStreamData>;
void setOwnerService(const sys::Service *service);
+ void setCodec(SCOCodec codec);
static constexpr auto CVSD_SAMPLE_RATE = 8000;
R module-bluetooth/Bluetooth/interface/profiles/HSP/ScoUtils.cpp => module-bluetooth/Bluetooth/interface/profiles/SCO/ScoUtils.cpp +0 -0
R module-bluetooth/Bluetooth/interface/profiles/HSP/ScoUtils.hpp => module-bluetooth/Bluetooth/interface/profiles/SCO/ScoUtils.hpp +0 -0
M module-bluetooth/CMakeLists.txt => module-bluetooth/CMakeLists.txt +4 -3
@@ 20,11 20,12 @@ set(SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVRCP.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/A2DP/AVDTP.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/HSP/HSP.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/HSP/SCO.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/HSP/ScoUtils.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/SCO/SCO.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/SCO/ScoUtils.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/HFP/HFP.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/Profile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/ProfileManager.cpp
-
+ ${CMAKE_CURRENT_SOURCE_DIR}/Bluetooth/interface/profiles/PhoneInterface.cpp
)
message("Build with BlueKitchen")
M module-bluetooth/lib/btstack.cmake => module-bluetooth/lib/btstack.cmake +3 -0
@@ 168,6 168,9 @@ set(BOARD_DIR_SOURCES
${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.c
+ ${BT_STACK_ROOT}/src/classic/hfp_gsm_model.c
+ ${BT_STACK_ROOT}/src/classic/hfp_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