From 76f06a045a019bdf9623c492ebf6252ac03397b7 Mon Sep 17 00:00:00 2001 From: Dirk Helbig <42479019+hegdi@users.noreply.github.com> Date: Fri, 28 Feb 2025 09:42:11 +0100 Subject: [PATCH] platform/linux: add ALSA support --- CHANGELOG.md | 2 + platform/linux/btstack_audio_alsa.c | 423 ++++++++++++++++++++++++++++ port/linux/CMakeLists.txt | 23 +- port/linux/main.c | 4 +- src/btstack_audio.h | 2 + 5 files changed, 446 insertions(+), 8 deletions(-) create mode 100644 platform/linux/btstack_audio_alsa.c diff --git a/CHANGELOG.md b/CHANGELOG.md index 33d5d1fbf..0d1aeb5f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - HCI: support newer AIROC Controller that require Download Mode with ENABLE_AIROC_DOWNLOAD_MODE - Zephyr: provide hal_flash_bank implementation for native flash driver - POSIX: support error condition for file descriptors in btstack_run_loop +- Linux: HCI Transport for Linux HCI Kernel Socket +- Linux: Audio sink implementation for Linux ALSA ### Fixed - GAP: store link key for standard/non-SSP pairing diff --git a/platform/linux/btstack_audio_alsa.c b/platform/linux/btstack_audio_alsa.c new file mode 100644 index 000000000..a926842ea --- /dev/null +++ b/platform/linux/btstack_audio_alsa.c @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2025 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN + * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ +#ifdef HAVE_ALSA +#define BTSTACK_FILE__ "btstack_audio_alsa.c" + +#include +#include +#include +#include +#include +#include + +#include "btstack_debug.h" +#include "btstack_audio.h" + +static const char* device = "default"; +static const char* simple_mixer_name = "Master"; +static snd_pcm_t *pcm_handle = NULL; +static snd_mixer_t *mixer_handle = NULL; +static snd_mixer_elem_t* master_volume = NULL; +static long volume_max; +static uint32_t current_sample_rate; +static uint8_t num_channels; +static void (*playback_callback)(int16_t *buffer, uint16_t num_samples); + +static unsigned int buffer_time = 500000; /* ring buffer length in us */ +static unsigned int period_time = 100000; /* period time in us */ +static int resample = 1; /* enable alsa-lib resampling */ +static snd_output_t *output = NULL; +static snd_pcm_sframes_t buffer_size; +static snd_pcm_sframes_t period_size; +static int period_event = 0; /* produce poll event after each period */ + +static struct pollfd *ufds = NULL; +static int ufds_count; +// TODO: corner case handling +#if 0 +/* + * Underrun and suspend recovery + */ +static int xrun_recovery(snd_pcm_t *handle, int err) +{ + printf("stream recovery\n"); + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); + return 0; + } else if (err == -ESTRPIPE) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + sleep(1); /* wait until the suspend flag is released */ + if (err < 0) { + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); + } + return 0; + } + return err; +} +#endif + +static btstack_data_source_t *btstack_audio_alsa_data_sources; + +static void btstack_audio_alsa_handler(btstack_data_source_t * ds, btstack_data_source_callback_type_t callback_type) { + int source_fd = ds->source.fd; + int err; + + for(int i=0; i data sources + for(int i=0; i