Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions include/pbl/services/system_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,8 @@ uint32_t system_task_get_available_space(void);
//! Debug! Return the callback we're currently executing.
void* system_task_get_current_callback(void);

//! @param is_raised When true, priority of the KernelBG task is raised to a higher priority. When
//! false, the priority is set to the normal priority.
//! @note WARNING: if you want to use this, implement ref counting internally. Currently only
//! comm/session.c uses this hence we can get away without ref counting.
//! Acquires or releases a reference that keeps KernelBG at a higher priority.
//! @param is_raised True to acquire a reference, false to release one. Calls must be balanced.
void system_task_enable_raised_priority(bool is_raised);

//! @return True if the KernelBG task is ready to run (i.e. not blocked by mutex / queue)
Expand Down
12 changes: 9 additions & 3 deletions src/fw/drivers/speaker/sf32lb52/audec.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <pbl/drivers/speaker/sf32lb52/audio_definitions.h>
#include "kernel/pbl_malloc.h"
#include "pbl/mcu/cache.h"
#include "pbl/kernel/irq.h"
#include "system/passert.h"
#include <pbl/logging/logging.h>
#include "pbl/util/misc.h"
Expand Down Expand Up @@ -403,16 +404,21 @@ void audec_start(AudioDevice* audio_device, AudioTransCB cb) {

uint32_t audec_write(AudioDevice* audio_device, void *writeBuf, uint32_t size) {
AudioDeviceState* state = audio_device->state;
uint32_t free_size = 0;

// Keep DMA from reading a partially copied block or racing the buffer indices.
pbl_irq_lock();
if (state->circ_buffer_storage) {
uint32_t free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer);
free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer);
uint16_t to_write = (size > free_size) ? (uint16_t)free_size : (uint16_t)size;
if (to_write > 0) {
circular_buffer_write(&state->circ_buffer, writeBuf, to_write);
}
return circular_buffer_get_write_space_remaining(&state->circ_buffer);
free_size = circular_buffer_get_write_space_remaining(&state->circ_buffer);
}
pbl_irq_unlock();

return 0;
return free_size;
}

void audec_set_vol(AudioDevice* audio_device, int volume) {
Expand Down
31 changes: 21 additions & 10 deletions src/fw/services/speaker/speaker_service.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ typedef struct {

static SpeakerServiceState s_state;

// Serializes public APIs against prv_refill_bg (system task).
// Serializes public APIs against the audio refill callback (system task).
static PBL_MUTEX_DEFINE(s_lock);

//! Why playback is currently silent, cached so a muted watch logs once per change
Expand All @@ -113,7 +113,7 @@ static uint32_t s_total_speaker_on_time_ms; // Total speaker on-time tracked

static void prv_stop_internal(SpeakerFinishReason reason);
static void prv_audio_trans_cb(uint32_t *free_size);
static void prv_refill_bg(void *data);
static void prv_refill_locked(void);

static bool prv_is_speaker_muted(void) {
if (alerts_preferences_get_speaker_muted()) {
Expand Down Expand Up @@ -205,6 +205,8 @@ static void prv_start_audio(uint8_t vol) {
PBL_ANALYTICS_ADD(speaker_play_count, 1);
prv_update_volume_analytics(effective_vol);

// Keep DMA refills ahead of CPU-heavy app work until playback stops.
system_task_enable_raised_priority(true);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This boosts all KernelBG callbacks above the app for the entire playback lifetime, including long-lived streams. Can the priority boost be scoped to pending refill work instead, so unrelated background callbacks do not also preempt the app?

audio_init((AudioDevice *)AUDIO);
audio_set_volume((AudioDevice *)AUDIO, effective_vol);
audio_start((AudioDevice *)AUDIO, prv_audio_trans_cb);
Expand All @@ -215,6 +217,7 @@ static void prv_stop_audio(void) {
prv_update_volume_analytics(0);

audio_stop((AudioDevice *)AUDIO);
system_task_enable_raised_priority(false);
}

static void prv_free_tracks(void) {
Expand Down Expand Up @@ -300,8 +303,22 @@ static bool prv_can_preempt(SpeakerPriority new_pri) {
//! This is the DMA refill callback path:
//! DMA ISR -> system_task_add_callback_from_isr -> audio driver trans_cb -> here
static void prv_audio_trans_cb(uint32_t *free_size) {
// Schedule actual refill work on system task to keep ISR-context callback short
system_task_add_callback(prv_refill_bg, NULL);
// The drivers already dispatch on KernelBG; refill here and catch up missed blocks.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes free_size is the amount currently writable, but the QEMU driver always passes a hard-coded full-buffer size (4096 * sizeof(int16_t)) on every interrupt. Would need to be changed in the QEMU driver

uint32_t refill_count = free_size
? *free_size / (SPEAKER_REFILL_SAMPLES * sizeof(int16_t))
: 1;
if (refill_count == 0) {
refill_count = 1;
}

pbl_mutex_lock(&s_lock, PBL_FOREVER);
if (s_state.source_type != SpeakerSourceStream) {
refill_count = 1;
}
while (refill_count-- && s_state.state != SpeakerStateIdle) {
prv_refill_locked();
}
pbl_mutex_unlock(&s_lock);
}

//! Convert a raw sample from the input buffer to 16-bit signed.
Expand Down Expand Up @@ -502,12 +519,6 @@ static void prv_refill_locked(void) {
}
}

static void prv_refill_bg(void *data) {
pbl_mutex_lock(&s_lock, PBL_FOREVER);
prv_refill_locked();
pbl_mutex_unlock(&s_lock);
}

bool speaker_service_play_note_seq(const SpeakerNote *notes, uint32_t num_notes,
SpeakerPriority pri, uint8_t vol) {
pbl_mutex_lock(&s_lock, PBL_FOREVER);
Expand Down
30 changes: 28 additions & 2 deletions src/fw/services/system_task/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "pbl/kernel/msgq.h"
#include "pbl/kernel/poll.h"
#include "pbl/kernel/thread.h"
#include "pbl/kernel/irq.h"
#include "system/passert.h"

PBL_LOG_MODULE_DEFINE(service_system_task, CONFIG_SERVICE_SYSTEM_TASK_LOG_LEVEL);

Expand All @@ -38,6 +40,7 @@ static SystemTaskEventCallback s_current_cb;

static bool s_system_task_idle = true;
static bool s_should_block_callbacks = false;
static uint32_t s_raised_priority_refcount;

static bool prv_is_accepting_callbacks() {
return s_initialized && !s_should_block_callbacks;
Expand Down Expand Up @@ -79,6 +82,7 @@ static void system_task_main(void* paramater) {
}

void system_task_init(void) {
s_raised_priority_refcount = 0;
pbl_poll_group_add(&s_system_task_queue_set, &s_system_task_queue);
pbl_poll_group_add(&s_system_task_queue_set, &s_from_app_system_task_queue);
s_initialized = true;
Expand Down Expand Up @@ -213,8 +217,30 @@ void* system_task_get_current_callback(void) {

void system_task_enable_raised_priority(bool is_raised) {
const pbl_prio_t raised_priority_level = PBL_PRIO_IDLE + 3; // Same as KernelMain / BT tasks
pbl_thread_prio_set(pebble_task_get_thread(PebbleTask_KernelBackground),
is_raised ? raised_priority_level : SYSTEM_TASK_PRIORITY);

pbl_irq_lock();
if (is_raised) {
PBL_ASSERTN(s_raised_priority_refcount < UINT32_MAX);
if (s_raised_priority_refcount == UINT32_MAX) {
pbl_irq_unlock();
return;
}
if (s_raised_priority_refcount++ == 0) {
pbl_thread_prio_set(pebble_task_get_thread(PebbleTask_KernelBackground),
raised_priority_level);
}
} else {
PBL_ASSERTN(s_raised_priority_refcount > 0);
if (s_raised_priority_refcount == 0) {
pbl_irq_unlock();
return;
}
if (--s_raised_priority_refcount == 0) {
pbl_thread_prio_set(pebble_task_get_thread(PebbleTask_KernelBackground),
SYSTEM_TASK_PRIORITY);
}
}
pbl_irq_unlock();
}

bool system_task_is_ready_to_run(void) {
Expand Down