mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-20 03:48:26 -05:00
Changes in dependecy managment Replacing state flags with states in ServiceGUI Unifying some parts of code
418 lines
14 KiB
C++
418 lines
14 KiB
C++
// Copyright (c) 2017-2023, 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/Result.hpp>
|
|
#include <service-evtmgr/ServiceEventManagerName.hpp>
|
|
#include <BluetoothWorker.hpp>
|
|
#include <module-bluetooth/Bluetooth/interface/profiles/SCO/ScoUtils.hpp>
|
|
#include <service-audio/AudioMessage.hpp>
|
|
#include <service-bluetooth/ServiceBluetoothName.hpp>
|
|
#include <service-bluetooth/messages/AudioVolume.hpp>
|
|
#include <service-bluetooth/messages/Connect.hpp>
|
|
#include <service-bluetooth/messages/Disconnect.hpp>
|
|
|
|
extern "C"
|
|
{
|
|
#include "btstack.h"
|
|
#include "btstack_run_loop_freertos.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() -> Result::Code
|
|
{
|
|
return pimpl->init();
|
|
}
|
|
|
|
void HSP::setDevice(const Devicei &device)
|
|
{
|
|
pimpl->setDevice(device);
|
|
}
|
|
|
|
void HSP::setOwnerService(sys::Service *service)
|
|
{
|
|
pimpl->setOwnerService(service);
|
|
}
|
|
|
|
void HSP::connect()
|
|
{
|
|
pimpl->connect();
|
|
}
|
|
|
|
void HSP::disconnect()
|
|
{
|
|
pimpl->disconnect();
|
|
}
|
|
|
|
void HSP::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDevice)
|
|
{
|
|
pimpl->setAudioDevice(audioDevice);
|
|
}
|
|
|
|
auto HSP::setIncomingCallNumber([[maybe_unused]] const std::string &num) const noexcept -> Result::Code
|
|
{
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::setSignalStrength([[maybe_unused]] int bars) const noexcept -> Result::Code
|
|
{
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::setOperatorName([[maybe_unused]] const std::string_view &name) const noexcept -> Result::Code
|
|
{
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::setBatteryLevel([[maybe_unused]] const BatteryLevel &name) const noexcept -> Result::Code
|
|
{
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::incomingCallStarted() const noexcept -> Result::Code
|
|
{
|
|
return pimpl->incomingCallStarted();
|
|
}
|
|
|
|
auto HSP::outgoingCallStarted(const std::string &number) const noexcept -> Result::Code
|
|
{
|
|
return pimpl->outgoingCallStarted(number);
|
|
}
|
|
|
|
auto HSP::incomingCallAnswered() const noexcept -> Result::Code
|
|
{
|
|
return pimpl->incomingCallAnswered();
|
|
}
|
|
|
|
auto HSP::outgoingCallAnswered() const noexcept -> Result::Code
|
|
{
|
|
return pimpl->outgoingCallAnswered();
|
|
}
|
|
|
|
auto HSP::callTerminated() const noexcept -> Result::Code
|
|
{
|
|
return pimpl->callTerminated();
|
|
}
|
|
|
|
auto HSP::callMissed() const noexcept -> Result::Code
|
|
{
|
|
return pimpl->callMissed();
|
|
}
|
|
|
|
auto HSP::setNetworkRegistrationStatus([[maybe_unused]] bool registered) const noexcept -> Result::Code
|
|
{
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::setRoamingStatus([[maybe_unused]] bool enabled) const noexcept -> Result::Code
|
|
{
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
HSP::~HSP() = default;
|
|
|
|
std::uint16_t HSP::HSPImpl::scoHandle = HCI_CON_HANDLE_INVALID;
|
|
std::uint8_t HSP::HSPImpl::serviceBuffer[serviceBufferSize];
|
|
|
|
const char *HSP::HSPImpl::agServiceName = "Mudita Pure HSP";
|
|
|
|
std::unique_ptr<SCO> HSP::HSPImpl::sco;
|
|
std::shared_ptr<CVSDAudioDevice> HSP::HSPImpl::audioDevice;
|
|
|
|
std::unique_ptr<CellularInterface> HSP::HSPImpl::cellularInterface = nullptr;
|
|
std::unique_ptr<AudioInterface> HSP::HSPImpl::audioInterface = nullptr;
|
|
|
|
sys::Service *HSP::HSPImpl::ownerService;
|
|
Devicei HSP::HSPImpl::device;
|
|
|
|
btstack_packet_callback_registration_t HSP::HSPImpl::hciEventCallbackRegistration;
|
|
char HSP::HSPImpl::ATCommandBuffer[commandBufferSize];
|
|
HSP::HSPImpl::HSPState HSP::HSPImpl::state = HSPState::RfcommDisconnected;
|
|
|
|
void HSP::HSPImpl::sendAudioEvent(audio::EventType event, audio::Event::DeviceState deviceState)
|
|
{
|
|
auto evt = std::make_shared<audio::Event>(event, deviceState);
|
|
auto msg = std::make_shared<AudioEventRequest>(std::move(evt));
|
|
ownerService->bus.sendUnicast(std::move(msg), service::name::evt_manager);
|
|
}
|
|
|
|
void HSP::HSPImpl::packetHandler(std::uint8_t packetType,
|
|
[[maybe_unused]] std::uint16_t channel,
|
|
std::uint8_t *event,
|
|
std::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(std::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(std::uint8_t *event)
|
|
{
|
|
switch (hci_event_hsp_meta_get_subevent_code(event)) {
|
|
case HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE: {
|
|
if (hsp_subevent_rfcomm_connection_complete_get_status(event) != ERROR_CODE_SUCCESS) {
|
|
LOG_ERROR("RFCOMM connection failed, status: 0x%02X",
|
|
hsp_subevent_rfcomm_connection_complete_get_status(event));
|
|
sendAudioEvent(audio::EventType::BluetoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
|
|
state = HSPState::RfcommDisconnected;
|
|
break;
|
|
}
|
|
|
|
LOG_INFO("RFCOMM connection established");
|
|
sendAudioEvent(audio::EventType::BluetoothHSPDeviceState, audio::Event::DeviceState::Connected);
|
|
state = HSPState::RfcommConnected;
|
|
device.deviceState = DeviceState::ConnectedVoice;
|
|
|
|
using message::bluetooth::ConnectResult;
|
|
ownerService->bus.sendUnicast(std::make_shared<ConnectResult>(device, ConnectResult::Result::Success),
|
|
service::name::bluetooth);
|
|
} break;
|
|
|
|
case HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE: {
|
|
LOG_INFO("RFCOMM disconnected");
|
|
sendAudioEvent(audio::EventType::BluetoothHSPDeviceState, audio::Event::DeviceState::Disconnected);
|
|
state = HSPState::RfcommDisconnected;
|
|
ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
|
|
service::name::bluetooth);
|
|
} break;
|
|
|
|
case HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE: {
|
|
const auto status = hsp_subevent_audio_connection_complete_get_status(event);
|
|
if (status != ERROR_CODE_SUCCESS) {
|
|
LOG_ERROR("Audio connection establishment failed, status: 0x%02X", status);
|
|
state = HSPState::RfcommDisconnected;
|
|
audioDevice.reset();
|
|
break;
|
|
}
|
|
|
|
scoHandle = hsp_subevent_audio_connection_complete_get_sco_handle(event);
|
|
LOG_INFO("Audio connection established with SCO handle 0x%04X", scoHandle);
|
|
state = HSPState::Answered;
|
|
|
|
audioInterface->startAudioRouting(ownerService);
|
|
hci_request_sco_can_send_now_event();
|
|
} break;
|
|
|
|
case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE: {
|
|
scoHandle = HCI_CON_HANDLE_INVALID;
|
|
audioDevice.reset();
|
|
state = HSPState::RfcommConnected;
|
|
LOG_INFO("Audio disconnected");
|
|
} break;
|
|
|
|
case HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED: {
|
|
LOG_INFO("Received microphone gain change: %d", 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);
|
|
LOG_INFO("Received speaker gain change: %d", hsp_subevent_speaker_gain_changed_get_gain(event));
|
|
ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::HSPVolume>(volume),
|
|
service::name::bluetooth);
|
|
} break;
|
|
|
|
case HSP_SUBEVENT_HS_CALL_ANSWER:
|
|
LOG_INFO("HSP call answer");
|
|
cellularInterface->answerIncomingCall(ownerService);
|
|
break;
|
|
|
|
case HSP_SUBEVENT_HS_CALL_HANGUP:
|
|
LOG_INFO("HSP call hangup");
|
|
cellularInterface->hangupCall(ownerService);
|
|
break;
|
|
|
|
case HSP_SUBEVENT_HS_COMMAND: {
|
|
const std::size_t cmdLength = hsp_subevent_hs_command_get_value_length(event);
|
|
const auto size = std::min(cmdLength, sizeof(ATCommandBuffer));
|
|
const auto commandValue = hsp_subevent_hs_command_get_value(event);
|
|
|
|
std::memset(ATCommandBuffer, 0, sizeof(ATCommandBuffer));
|
|
std::memcpy(ATCommandBuffer, commandValue, size - 1);
|
|
|
|
LOG_INFO("Received custom command: '%s'", ATCommandBuffer);
|
|
break;
|
|
}
|
|
|
|
case HSP_SUBEVENT_BUTTON_PRESSED: {
|
|
if ((scoHandle == HCI_CON_HANDLE_INVALID) && (state == HSPState::Ringing)) {
|
|
LOG_INFO("Received button event in ringing state, answering call");
|
|
cellularInterface->answerIncomingCall(ownerService);
|
|
break;
|
|
}
|
|
|
|
if (state == HSPState::Answered) {
|
|
LOG_INFO("Received button event in answered state, hanging up call");
|
|
cellularInterface->hangupCall(ownerService);
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
LOG_INFO("Event not handled: 0x%02X", hci_event_hsp_meta_get_subevent_code(event));
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto HSP::HSPImpl::init() -> Result::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();
|
|
|
|
std::memset(serviceBuffer, 0, sizeof(serviceBuffer));
|
|
|
|
hsp_ag_create_sdp_record(serviceBuffer, hspSdpRecordHandle, rfcommChannelNr, agServiceName);
|
|
|
|
if (const auto status = sdp_register_service(serviceBuffer); status != ERROR_CODE_SUCCESS) {
|
|
LOG_ERROR("Can't register service, status: 0x%02X", status);
|
|
}
|
|
|
|
rfcomm_init();
|
|
|
|
hsp_ag_init(rfcommChannelNr);
|
|
|
|
hciEventCallbackRegistration.callback = &packetHandler;
|
|
hci_add_event_handler(&hciEventCallbackRegistration);
|
|
hsp_ag_register_packet_handler(&packetHandler);
|
|
hci_register_sco_packet_handler(&packetHandler);
|
|
|
|
LOG_INFO("HSP initialized!");
|
|
return bluetooth::Result::Code::Success;
|
|
}
|
|
|
|
void HSP::HSPImpl::connect()
|
|
{
|
|
if (state != HSPState::RfcommDisconnected) {
|
|
disconnect();
|
|
}
|
|
hsp_ag_connect(device.address);
|
|
}
|
|
|
|
void HSP::HSPImpl::disconnect()
|
|
{
|
|
hsp_ag_release_audio_connection();
|
|
hsp_ag_disconnect();
|
|
ownerService->bus.sendUnicast(std::make_shared<message::bluetooth::DisconnectResult>(device),
|
|
service::name::bluetooth);
|
|
}
|
|
|
|
void HSP::HSPImpl::setDevice(const Devicei &dev)
|
|
{
|
|
device = dev;
|
|
LOG_DEBUG("Device set!");
|
|
}
|
|
|
|
void HSP::HSPImpl::setAudioDevice(std::shared_ptr<bluetooth::BluetoothAudioDevice> audioDev)
|
|
{
|
|
HSP::HSPImpl::audioDevice = std::static_pointer_cast<CVSDAudioDevice>(audioDev);
|
|
}
|
|
|
|
void HSP::HSPImpl::setOwnerService(sys::Service *service)
|
|
{
|
|
ownerService = service;
|
|
}
|
|
|
|
auto HSP::HSPImpl::incomingCallStarted() const noexcept -> Result::Code
|
|
{
|
|
LOG_DEBUG("Incoming call started");
|
|
hsp_ag_start_ringing();
|
|
state = HSPState::Ringing;
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::HSPImpl::outgoingCallStarted([[maybe_unused]] const std::string &number) const noexcept -> Result::Code
|
|
{
|
|
LOG_DEBUG("Outgoing call started");
|
|
hsp_ag_establish_audio_connection();
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::HSPImpl::incomingCallAnswered() const noexcept -> Result::Code
|
|
{
|
|
LOG_DEBUG("Incoming call answered");
|
|
hsp_ag_stop_ringing();
|
|
hsp_ag_establish_audio_connection();
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::HSPImpl::outgoingCallAnswered() const noexcept -> Result::Code
|
|
{
|
|
LOG_DEBUG("Outgoing call answered");
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::HSPImpl::callTerminated() const noexcept -> Result::Code
|
|
{
|
|
LOG_DEBUG("Call terminated");
|
|
hsp_ag_stop_ringing();
|
|
hsp_ag_release_audio_connection();
|
|
state = HSPState::RfcommConnected;
|
|
return Result::Code::Success;
|
|
}
|
|
|
|
auto HSP::HSPImpl::callMissed() const noexcept -> Result::Code
|
|
{
|
|
return callTerminated();
|
|
}
|
|
} // namespace bluetooth
|