Unverified Commit bf385490 authored by Earle F. Philhower, III's avatar Earle F. Philhower, III Committed by GitHub

Fix crash on audio end from IRQ, refactor A2DP (#2189)

Fixes #2188

We get a call to stop the audio channel from a timer/IRQ context, so can't
safely remove the IRQ handler for the AudioBufferManager.  The SDK will panic.

Because the IRQ handler will be a noop if it's not uninstalled, we will
instead just leave our shared handler in place and let it do nothing.

Use a common BluetoothLock RAII in BluetoothAudio to clen up the code and
automatically lock BT for the AVRCP button methods.
parent 919a754e
...@@ -24,8 +24,9 @@ ...@@ -24,8 +24,9 @@
#include <hardware/irq.h> #include <hardware/irq.h>
#include "AudioBufferManager.h" #include "AudioBufferManager.h"
static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler static int __channelCount = 0; // # of channels left. When we hit 0, then remove our handler
static AudioBufferManager* __channelMap[12]; // Lets the IRQ handler figure out where to dispatch to static AudioBufferManager* __channelMap[12]; // Lets the IRQ handler figure out where to dispatch to
static bool __irqInstalled = false; // Have we put in our IRQ handler yet?
AudioBufferManager::AudioBufferManager(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction, enum dma_channel_transfer_size dmaSize) { AudioBufferManager::AudioBufferManager(size_t bufferCount, size_t bufferWords, int32_t silenceSample, PinMode direction, enum dma_channel_transfer_size dmaSize) {
_running = false; _running = false;
...@@ -78,11 +79,6 @@ AudioBufferManager::~AudioBufferManager() { ...@@ -78,11 +79,6 @@ AudioBufferManager::~AudioBufferManager() {
dma_channel_unclaim(_channelDMA[i]); dma_channel_unclaim(_channelDMA[i]);
__channelCount--; __channelCount--;
} }
if (!__channelCount) {
irq_set_enabled(DMA_IRQ_0, false);
// TODO - how can we know if there are no other parts of the core using DMA0 IRQ??
irq_remove_handler(DMA_IRQ_0, _irq);
}
} }
interrupts(); interrupts();
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
...@@ -120,7 +116,7 @@ bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) { ...@@ -120,7 +116,7 @@ bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) {
return false; return false;
} }
} }
bool needSetIRQ = __channelCount == 0;
// Need to know both channels to set up ping-pong, so do in 2 stages // Need to know both channels to set up ping-pong, so do in 2 stages
for (auto i = 0; i < 2; i++) { for (auto i = 0; i < 2; i++) {
dma_channel_config c = dma_channel_get_default_config(_channelDMA[i]); dma_channel_config c = dma_channel_get_default_config(_channelDMA[i]);
...@@ -146,9 +142,10 @@ bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) { ...@@ -146,9 +142,10 @@ bool AudioBufferManager::begin(int dreq, volatile void *pioFIFOAddr) {
__channelMap[_channelDMA[i]] = this; __channelMap[_channelDMA[i]] = this;
__channelCount++; __channelCount++;
} }
if (needSetIRQ) { if (!__irqInstalled) {
irq_add_shared_handler(DMA_IRQ_0, _irq, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); irq_add_shared_handler(DMA_IRQ_0, _irq, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
irq_set_enabled(DMA_IRQ_0, true); irq_set_enabled(DMA_IRQ_0, true);
__irqInstalled = true;
} }
dma_channel_start(_channelDMA[0]); dma_channel_start(_channelDMA[0]);
......
...@@ -45,7 +45,6 @@ void setup() { ...@@ -45,7 +45,6 @@ void setup() {
void loop() { void loop() {
if (BOOTSEL) { if (BOOTSEL) {
__lockBluetooth();
if (status == A2DPSink::PAUSED) { if (status == A2DPSink::PAUSED) {
a2dp.play(); a2dp.play();
Serial.printf("Resuming\n"); Serial.printf("Resuming\n");
...@@ -53,7 +52,6 @@ void loop() { ...@@ -53,7 +52,6 @@ void loop() {
a2dp.pause(); a2dp.pause();
Serial.printf("Pausing\n"); Serial.printf("Pausing\n");
} }
__unlockBluetooth();
while (BOOTSEL); while (BOOTSEL);
} }
} }
...@@ -191,9 +191,10 @@ bool A2DPSink::begin() { ...@@ -191,9 +191,10 @@ bool A2DPSink::begin() {
} }
bool A2DPSink::disconnect() { bool A2DPSink::disconnect() {
__lockBluetooth(); BluetoothLock b;
a2dp_sink_disconnect(a2dp_sink_a2dp_connection.a2dp_cid); if (_connected) {
__unlockBluetooth(); a2dp_sink_disconnect(a2dp_sink_a2dp_connection.a2dp_cid);
}
if (!_running || !_connected) { if (!_running || !_connected) {
return false; return false;
} }
...@@ -202,10 +203,11 @@ bool A2DPSink::disconnect() { ...@@ -202,10 +203,11 @@ bool A2DPSink::disconnect() {
} }
void A2DPSink::clearPairing() { void A2DPSink::clearPairing() {
disconnect(); BluetoothLock b;
__lockBluetooth(); if (_connected) {
a2dp_sink_disconnect(a2dp_sink_a2dp_connection.a2dp_cid);
}
gap_delete_all_link_keys(); gap_delete_all_link_keys();
__unlockBluetooth();
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <Arduino.h> #include <Arduino.h>
#include "BluetoothHCI.h" #include "BluetoothHCI.h"
#include "BluetoothLock.h"
#include "BluetoothAudioConsumer.h" #include "BluetoothAudioConsumer.h"
#include "BluetoothMediaConfigurationSBC.h" #include "BluetoothMediaConfigurationSBC.h"
...@@ -120,60 +121,70 @@ public: ...@@ -120,60 +121,70 @@ public:
void playback_handler(int16_t * buffer, uint16_t num_audio_frames); void playback_handler(int16_t * buffer, uint16_t num_audio_frames);
void play() { void play() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_play(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_play(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void stop() { void stop() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_stop(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_stop(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void pause() { void pause() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_pause(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_pause(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void fastForward() { void fastForward() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_fast_forward(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_fast_forward(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void rewind() { void rewind() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_rewind(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_rewind(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void forward() { void forward() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_forward(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_forward(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void backward() { void backward() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_backward(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_backward(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void volumeUp() { void volumeUp() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_volume_up(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_volume_up(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void volumeDown() { void volumeDown() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_volume_down(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_volume_down(a2dp_sink_avrcp_connection.avrcp_cid);
} }
} }
void mute() { void mute() {
BluetoothLock b;
if (_connected) { if (_connected) {
avrcp_controller_mute(a2dp_sink_avrcp_connection.avrcp_cid); avrcp_controller_mute(a2dp_sink_avrcp_connection.avrcp_cid);
} }
......
...@@ -196,11 +196,11 @@ bool A2DPSource::connect(const uint8_t *addr) { ...@@ -196,11 +196,11 @@ bool A2DPSource::connect(const uint8_t *addr) {
return false; return false;
} }
} }
bool A2DPSource::disconnect() { bool A2DPSource::disconnect() {
__lockBluetooth(); BluetoothLock b;
a2dp_source_disconnect(media_tracker.a2dp_cid); if (_connected) {
__unlockBluetooth(); a2dp_source_disconnect(media_tracker.a2dp_cid);
}
if (!_running || !_connected) { if (!_running || !_connected) {
return false; return false;
} }
...@@ -209,17 +209,20 @@ bool A2DPSource::disconnect() { ...@@ -209,17 +209,20 @@ bool A2DPSource::disconnect() {
} }
void A2DPSource::clearPairing() { void A2DPSource::clearPairing() {
disconnect(); BluetoothLock b;
__lockBluetooth(); if (_connected) {
a2dp_source_disconnect(media_tracker.a2dp_cid);
}
gap_delete_all_link_keys(); gap_delete_all_link_keys();
__unlockBluetooth();
} }
// from Print (see notes on write() methods below) // from Print (see notes on write() methods below)
size_t A2DPSource::write(const uint8_t *buffer, size_t size) { size_t A2DPSource::write(const uint8_t *buffer, size_t size) {
BluetoothLock b;
size_t count = 0; size_t count = 0;
size /= 2; size /= 2;
__lockBluetooth();
// First copy from writer to either end of // First copy from writer to either end of
uint32_t start = _pcmWriter; uint32_t start = _pcmWriter;
uint32_t end = _pcmReader > _pcmWriter ? _pcmReader : _pcmBufferSize; uint32_t end = _pcmReader > _pcmWriter ? _pcmReader : _pcmBufferSize;
...@@ -244,13 +247,12 @@ size_t A2DPSource::write(const uint8_t *buffer, size_t size) { ...@@ -244,13 +247,12 @@ size_t A2DPSource::write(const uint8_t *buffer, size_t size) {
_pcmWriter += end - start; _pcmWriter += end - start;
_pcmWriter %= _pcmBufferSize; _pcmWriter %= _pcmBufferSize;
} }
__unlockBluetooth();
return count; return count;
} }
int A2DPSource::availableForWrite() { int A2DPSource::availableForWrite() {
BluetoothLock b;
int avail = 0; int avail = 0;
__lockBluetooth();
if (_pcmWriter == _pcmReader) { if (_pcmWriter == _pcmReader) {
avail = _pcmBufferSize - 1; avail = _pcmBufferSize - 1;
} else if (_pcmReader > _pcmWriter) { } else if (_pcmReader > _pcmWriter) {
...@@ -258,7 +260,6 @@ int A2DPSource::availableForWrite() { ...@@ -258,7 +260,6 @@ int A2DPSource::availableForWrite() {
} else { } else {
avail = _pcmBufferSize - _pcmWriter + _pcmReader - 1; avail = _pcmBufferSize - _pcmWriter + _pcmReader - 1;
} }
__unlockBluetooth();
return avail; return avail;
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <Arduino.h> #include <Arduino.h>
#include "BluetoothHCI.h" #include "BluetoothHCI.h"
#include "BluetoothLock.h"
#include "BluetoothMediaConfigurationSBC.h" #include "BluetoothMediaConfigurationSBC.h"
#include <functional> #include <functional>
#include <list> #include <list>
...@@ -84,13 +85,12 @@ public: ...@@ -84,13 +85,12 @@ public:
} }
bool getUnderflow() { bool getUnderflow() {
BluetoothLock b;
if (!_running) { if (!_running) {
return false; return false;
} }
__lockBluetooth();
auto ret = _underflow; auto ret = _underflow;
_underflow = false; _underflow = false;
__unlockBluetooth();
return ret; return ret;
} }
......
#include "BluetoothLock.h"
#include "BluetoothDevice.h" #include "BluetoothDevice.h"
#include "BluetoothHCI.h" #include "BluetoothHCI.h"
#include "BluetoothMediaConfigurationSBC.h" #include "BluetoothMediaConfigurationSBC.h"
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <memory> #include <memory>
#include "BluetoothHCI.h" #include "BluetoothHCI.h"
#include "BluetoothLock.h"
#define CCALLBACKNAME _BTHCICB #define CCALLBACKNAME _BTHCICB
#include <ctocppcallback.h> #include <ctocppcallback.h>
...@@ -46,10 +47,9 @@ void BluetoothHCI_::begin() { ...@@ -46,10 +47,9 @@ void BluetoothHCI_::begin() {
} }
void BluetoothHCI_::uninstall() { void BluetoothHCI_::uninstall() {
__lockBluetooth(); BluetoothLock b;
hci_remove_event_handler(&hci_event_callback_registration); hci_remove_event_handler(&hci_event_callback_registration);
_running = false; _running = false;
__unlockBluetooth();
} }
bool BluetoothHCI_::running() { bool BluetoothHCI_::running() {
...@@ -72,9 +72,11 @@ std::list<BTDeviceInfo> BluetoothHCI_::scan(uint32_t mask, int scanTimeSec, bool ...@@ -72,9 +72,11 @@ std::list<BTDeviceInfo> BluetoothHCI_::scan(uint32_t mask, int scanTimeSec, bool
inquiryTime = 1; inquiryTime = 1;
} }
DEBUGV("HCI::scan(): inquiry start\n"); DEBUGV("HCI::scan(): inquiry start\n");
__lockBluetooth(); // Only need to lock around the inquiry start command, not the wait
gap_inquiry_start(inquiryTime); {
__unlockBluetooth(); BluetoothLock b;
gap_inquiry_start(inquiryTime);
}
if (async) { if (async) {
return _btdList; return _btdList;
} }
......
/*
Bluetooth lock helper class
Copyright (c) 2024 Earle F. Philhower, III <earlephilhower@yahoo.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <Arduino.h>
class BluetoothLock {
public:
BluetoothLock() {
__lockBluetooth();
}
~BluetoothLock() {
__unlockBluetooth();
}
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment