mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-04-20 06:59:13 -04:00
395 lines
13 KiB
C++
395 lines
13 KiB
C++
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include <log/log.hpp>
|
|
#include "HSPImpl.hpp"
|
|
#include "HSP.hpp"
|
|
|
|
#include <Bluetooth/Error.hpp>
|
|
#include <service-evtmgr/Constants.hpp>
|
|
#include <BluetoothWorker.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>
|
|
#include <service-bluetooth/messages/Connect.hpp>
|
|
#include <service-bluetooth/messages/Disconnect.hpp>
|
|
#include <service-cellular/service-cellular/CellularServiceAPI.hpp>
|
|
#include <service-evtmgr/Constants.hpp>
|
|
#include <service-audio/AudioServiceAPI.hpp>
|
|
|
|
extern "C"
|
|
{
|
|
#include "btstack.h"
|
|
#include "btstack_run_loop_freertos.h"
|
|
#include "btstack_stdin.h"
|
|
#include <btstack_defines.h>
|
|
}
|
|
|
|
namespace bluetooth
|
|
{
|
|
HSP::HSP() : pimpl(std::make_unique<HSPImpl>(HSPImpl()))
|
|
{}
|
|
|
|
HSP::HSP(HSP &&other) noexcept : pimpl(std::move(other.pimpl))
|
|
{}
|
|
|
|
auto HSP::operator=(HSP &&other) noexcept -> HSP &
|
|
{
|
|
if (&other == this) {
|
|
return *this;
|
|
}
|
|
pimpl = std::move(other.pimpl);
|
|
return *this;
|
|
}
|
|
|
|
auto HSP::init() -> Error::Code
|
|
{
|
|
return pimpl->init();
|
|
}
|
|
|
|
void HSP::setDevice(const Devicei &device)
|
|
{
|
|
pimpl->setDevice(device);
|
|
}
|
|
|
|
void HSP::setOwnerService(const sys::Service *service)
|
|
{
|
|
ownerService = service;
|
|
pimpl->setOwnerService(service);
|
|
}
|
|
|
|
void HSP::connect()
|
|
{
|
|
pimpl->connect();
|
|
}
|
|
|
|
void HSP::disconnect()
|
|
{
|
|
pimpl->disconnect();
|
|
}
|
|
|
|
void HSP::start()
|
|
{
|
|
pimpl->start();
|
|
}
|
|
|
|
void HSP::stop()
|
|
{
|
|
pimpl->stop();
|
|
}
|
|
|
|
auto HSP::startRinging() const noexcept -> Error::Code
|
|
{
|
|
pimpl->startRinging();
|
|
return Error::Success;
|
|
}
|
|
|
|
auto HSP::stopRinging() const noexcept -> Error::Code
|
|
{
|
|
pimpl->stopRinging();
|
|
return Error::Success;
|
|
}
|
|
|
|
auto HSP::initializeCall() const noexcept -> Error::Code
|
|
{
|
|
pimpl->initializeCall();
|
|
return Error::Success;
|
|
}
|
|
|
|
HSP::~HSP() = default;
|
|
|
|
uint16_t HSP::HSPImpl::scoHandle = HCI_CON_HANDLE_INVALID;
|
|
std::array<char, commandBufferLength> HSP::HSPImpl::ATcommandBuffer;
|
|
std::array<uint8_t, serviceBufferLength> HSP::HSPImpl::serviceBuffer;
|
|
std::unique_ptr<SCO> HSP::HSPImpl::sco;
|
|
std::unique_ptr<CellularInterface> HSP::HSPImpl::cellularInterface = nullptr;
|
|
std::unique_ptr<AudioInterface> HSP::HSPImpl::audioInterface = nullptr;
|
|
const sys::Service *HSP::HSPImpl::ownerService;
|
|
const std::string_view HSP::HSPImpl::agServiceName = "Mudita Pure HSP";
|
|
bool HSP::HSPImpl::isConnected = false;
|
|
bool HSP::HSPImpl::callAnswered = false;
|
|
bool HSP::HSPImpl::isRinging = false;
|
|
std::shared_ptr<CVSDAudioDevice> HSP::HSPImpl::audioDevice;
|
|
Devicei HSP::HSPImpl::device;
|
|
|
|
void HSP::HSPImpl::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 HSP::HSPImpl::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;
|
|
}
|
|
if (audioDevice != nullptr) {
|
|
audioDevice->receiveCVSD(audio::AbstractStream::Span{.data = event, .dataSize = eventSize});
|
|
}
|
|
break;
|
|
|
|
case HCI_EVENT_PACKET:
|
|
processHCIEvent(event);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HSP::HSPImpl::processHCIEvent(uint8_t *event)
|
|
{
|
|
switch (hci_event_packet_get_type(event)) {
|
|
case HCI_EVENT_SCO_CAN_SEND_NOW:
|
|
if (audioDevice != nullptr) {
|
|
audioDevice->onDataSend(scoHandle);
|
|
}
|
|
else {
|
|
sco::utils::sendZeros(scoHandle);
|
|
}
|
|
break;
|
|
case HCI_EVENT_HSP_META:
|
|
processHSPEvent(event);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HSP::HSPImpl::processHSPEvent(uint8_t *event)
|
|
{
|
|
auto eventDescriptor = event[2];
|
|
switch (eventDescriptor) {
|
|
case HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE:
|
|
if (hsp_subevent_rfcomm_connection_complete_get_status(event) != 0u) {
|
|
LOG_DEBUG("RFCOMM connection establishement failed with status %u\n",
|
|
hsp_subevent_rfcomm_connection_complete_get_status(event));
|
|
sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
|
|
isConnected = false;
|
|
break;
|
|
}
|
|
LOG_DEBUG("RFCOMM connection established.\n");
|
|
sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Connected);
|
|
{
|
|
auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
|
|
device.deviceState = DeviceState::ConnectedVoice;
|
|
busProxy.sendUnicast(std::make_shared<message::bluetooth::ConnectResult>(device, true),
|
|
service::name::bluetooth);
|
|
}
|
|
isConnected = true;
|
|
break;
|
|
case HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE:
|
|
if (hsp_subevent_rfcomm_disconnection_complete_get_status(event) != 0u) {
|
|
LOG_DEBUG("RFCOMM disconnection failed with status %u.\n",
|
|
hsp_subevent_rfcomm_disconnection_complete_get_status(event));
|
|
}
|
|
else {
|
|
LOG_DEBUG("RFCOMM disconnected.\n");
|
|
sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
|
|
auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
|
|
busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
|
|
service::name::bluetooth);
|
|
}
|
|
isConnected = false;
|
|
break;
|
|
case HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE:
|
|
if (hsp_subevent_audio_connection_complete_get_status(event) != 0u) {
|
|
LOG_DEBUG("Audio connection establishment failed with status %u\n",
|
|
hsp_subevent_audio_connection_complete_get_status(event));
|
|
isConnected = false;
|
|
callAnswered = false;
|
|
audioDevice.reset();
|
|
}
|
|
else {
|
|
scoHandle = hsp_subevent_audio_connection_complete_get_handle(event);
|
|
LOG_DEBUG("Audio connection established with SCO handle 0x%04x.\n", scoHandle);
|
|
callAnswered = true;
|
|
hci_request_sco_can_send_now_event();
|
|
}
|
|
break;
|
|
case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE:
|
|
LOG_DEBUG("Audio connection released.\n\n");
|
|
scoHandle = HCI_CON_HANDLE_INVALID;
|
|
callAnswered = false;
|
|
sendAudioEvent(audio::EventType::BlutoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
|
|
break;
|
|
case HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED:
|
|
LOG_DEBUG("Received microphone gain change %d\n", hsp_subevent_microphone_gain_changed_get_gain(event));
|
|
break;
|
|
case HSP_SUBEVENT_SPEAKER_GAIN_CHANGED: {
|
|
const auto volume = hsp_subevent_speaker_gain_changed_get_gain(event);
|
|
auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
|
|
busProxy.sendUnicast(std::make_shared<message::bluetooth::HSPVolume>(volume), service::name::bluetooth);
|
|
LOG_DEBUG("Received speaker gain change %d\n", hsp_subevent_speaker_gain_changed_get_gain(event));
|
|
} break;
|
|
case HSP_SUBEVENT_HS_CALL_ANSWER:
|
|
LOG_DEBUG("HSP CALL ANSWER");
|
|
cellularInterface->answerIncomingCall(const_cast<sys::Service *>(ownerService));
|
|
break;
|
|
case HSP_SUBEVENT_HS_CALL_HANGUP:
|
|
LOG_DEBUG("HSP CALL HANGUP");
|
|
cellularInterface->hangupCall(const_cast<sys::Service *>(ownerService));
|
|
break;
|
|
case HSP_SUBEVENT_HS_COMMAND: {
|
|
ATcommandBuffer.fill(0);
|
|
auto cmd_length = hsp_subevent_hs_command_get_value_length(event);
|
|
auto size = cmd_length <= ATcommandBuffer.size() ? cmd_length : ATcommandBuffer.size();
|
|
auto commandValue = hsp_subevent_hs_command_get_value(event);
|
|
memcpy(ATcommandBuffer.data(), commandValue, size - 1);
|
|
LOG_DEBUG("Received custom command: \"%s\". \nExit code or call hsp_ag_send_result.\n",
|
|
ATcommandBuffer.data());
|
|
break;
|
|
}
|
|
case HSP_SUBEVENT_BUTTON_PRESSED: {
|
|
if (scoHandle == HCI_CON_HANDLE_INVALID) {
|
|
if (isRinging) {
|
|
LOG_DEBUG("Button event -> establish audio");
|
|
establishAudioConnection();
|
|
audioInterface->startAudioRouting(const_cast<sys::Service *>(ownerService));
|
|
cellularInterface->answerIncomingCall(const_cast<sys::Service *>(ownerService));
|
|
}
|
|
break;
|
|
}
|
|
LOG_DEBUG("Button event -> release audio");
|
|
isRinging = false;
|
|
if (callAnswered) {
|
|
cellularInterface->hangupCall(const_cast<sys::Service *>(ownerService));
|
|
}
|
|
callAnswered = false;
|
|
hsp_ag_release_audio_connection();
|
|
} break;
|
|
default:
|
|
LOG_DEBUG("event not handled %u\n", event[2]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HSP::HSPImpl::establishAudioConnection()
|
|
{
|
|
LOG_DEBUG("Establish Audio connection...\n");
|
|
hsp_ag_establish_audio_connection();
|
|
}
|
|
|
|
auto HSP::HSPImpl::init() -> Error::Code
|
|
{
|
|
sco = std::make_unique<SCO>();
|
|
sco->setOwnerService(ownerService);
|
|
sco->init();
|
|
|
|
cellularInterface = std::make_unique<CellularInterfaceImpl>();
|
|
audioInterface = std::make_unique<AudioInterfaceImpl>();
|
|
|
|
Profile::initL2cap();
|
|
Profile::initSdp();
|
|
|
|
serviceBuffer.fill(0);
|
|
constexpr uint32_t hspSdpRecordHandle = 0x10004;
|
|
hsp_ag_create_sdp_record(serviceBuffer.data(), hspSdpRecordHandle, rfcommChannelNr, agServiceName.data());
|
|
|
|
if (const auto status = sdp_register_service(serviceBuffer.data()); status != ERROR_CODE_SUCCESS) {
|
|
LOG_ERROR("Can't register service. Status %x", status);
|
|
}
|
|
|
|
rfcomm_init();
|
|
|
|
hsp_ag_init(rfcommChannelNr);
|
|
hsp_ag_register_packet_handler(&packetHandler);
|
|
|
|
// register for SCO packets
|
|
hci_register_sco_packet_handler(&packetHandler);
|
|
|
|
gap_discoverable_control(1);
|
|
gap_set_class_of_device(CLASS_OF_DEVICE);
|
|
|
|
LOG_INFO("HSP init done!");
|
|
|
|
return bluetooth::Error::Success;
|
|
}
|
|
|
|
void HSP::HSPImpl::connect()
|
|
{
|
|
if (isConnected) {
|
|
disconnect();
|
|
}
|
|
hsp_ag_connect(device.address);
|
|
}
|
|
|
|
void HSP::HSPImpl::disconnect()
|
|
{
|
|
hsp_ag_release_audio_connection();
|
|
hsp_ag_disconnect();
|
|
auto &busProxy = const_cast<sys::Service *>(ownerService)->bus;
|
|
busProxy.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device), service::name::bluetooth);
|
|
}
|
|
|
|
void HSP::HSPImpl::setDevice(const Devicei &dev)
|
|
{
|
|
device = dev;
|
|
LOG_INFO("Device set!");
|
|
}
|
|
|
|
void HSP::HSPImpl::setOwnerService(const sys::Service *service)
|
|
{
|
|
ownerService = service;
|
|
}
|
|
|
|
auto HSP::HSPImpl::getStreamData() -> std::shared_ptr<BluetoothStreamData>
|
|
{
|
|
return sco->getStreamData();
|
|
}
|
|
void HSP::HSPImpl::start()
|
|
{
|
|
if (!isConnected) {
|
|
connect();
|
|
}
|
|
}
|
|
void HSP::HSPImpl::stop()
|
|
{
|
|
stopRinging();
|
|
hsp_ag_release_audio_connection();
|
|
callAnswered = false;
|
|
}
|
|
|
|
void HSP::HSPImpl::startRinging() const noexcept
|
|
{
|
|
LOG_DEBUG("Bluetooth ring started");
|
|
hsp_ag_start_ringing();
|
|
isRinging = true;
|
|
}
|
|
|
|
void HSP::HSPImpl::stopRinging() const noexcept
|
|
{
|
|
LOG_DEBUG("Bluetooth ring stopped");
|
|
hsp_ag_stop_ringing();
|
|
isRinging = false;
|
|
}
|
|
|
|
void HSP::HSPImpl::initializeCall() const noexcept
|
|
{
|
|
stopRinging();
|
|
establishAudioConnection();
|
|
}
|
|
|
|
void HSP::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
|
|
{
|
|
pimpl->setAudioDevice(audioDevice);
|
|
}
|
|
auto HSP::callAnswered() const noexcept -> Error::Code
|
|
{
|
|
return Error::Success;
|
|
}
|
|
auto HSP::setIncomingCallNumber(const std::string &num) const noexcept -> Error::Code
|
|
{
|
|
return Error::Success;
|
|
}
|
|
|
|
void HSP::HSPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
|
|
{
|
|
HSP::HSPImpl::audioDevice = std::static_pointer_cast<CVSDAudioDevice>(audioDevice);
|
|
}
|
|
} // namespace bluetooth
|