arch/sim: add sim alsa support

Squashed commit of the following:

sim audio: call alsa to playback/capture data
sim/audio: correct the format capability
sim/audio: add pause/resume support
sim/audio: add auto stop when meet AUDIO_APB_FINAL
sim/audio: fix abort when set small buffer_size
sim/audio: move sim_audio.c to sim_alsa.c

Change-Id: I8e00ece79159e844ca17fd4c363480b985ee0490
Signed-off-by: ligd <liguiding1@xiaomi.com>
This commit is contained in:
ligd 2020-07-24 10:40:54 +08:00 committed by Brennan Ashton
parent 639093feea
commit 73282fe2d8
7 changed files with 831 additions and 0 deletions

View File

@ -201,6 +201,25 @@ config SIM_NET_BRIDGE_DEVICE
endif
config SIM_SOUND
bool "Simulated sound support"
depends on AUDIO
default y
if SIM_SOUND
choice
prompt "Simulated sound Type"
default SIM_SOUND_ALSA
config SIM_SOUND_ALSA
bool "alsa support on sim"
depends on HOST_LINUX
endchoice
endif
config SIM_RPTUN_MASTER
bool "Remote Processor Tunneling Role"
depends on RPTUN

View File

@ -205,6 +205,11 @@ ifeq ($(CONFIG_RPTUN),y)
CSRCS += up_rptun.c
endif
ifeq ($(CONFIG_SIM_SOUND_ALSA),y)
CSRCS += up_alsa.c
STDLIBS += -lasound
endif
ifeq ($(CONFIG_FS_HOSTFS),y)
ifneq ($(CONFIG_FS_HOSTFS_RPMSG),y)
HOSTSRCS += up_hostfs.c

731
arch/sim/src/sim/up_alsa.c Normal file
View File

@ -0,0 +1,731 @@
/****************************************************************************
* arch/sim/src/sim/up_alsa.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/audio/audio.h>
#include <nuttx/kmalloc.h>
#include <nuttx/nuttx.h>
#include <queue.h>
#include <alsa/asoundlib.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define AUDMIN(a,b) ((a) > (b) ? (b) : (a))
/****************************************************************************
* Private Types
****************************************************************************/
struct sim_audio_s
{
struct audio_lowerhalf_s dev;
struct dq_queue_s pendq;
sq_entry_t link;
bool playback;
uint32_t frame_size;
uint32_t nbuffers;
uint32_t buffer_size;
uint32_t sample_rate;
uint32_t channels;
uint32_t bps;
snd_pcm_t *pcm;
snd_mixer_t *mixer;
snd_mixer_elem_t *volume;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int sim_audio_getcaps(struct audio_lowerhalf_s *dev, int type,
struct audio_caps_s *caps);
static int sim_audio_configure(struct audio_lowerhalf_s *dev,
const struct audio_caps_s *caps);
static int sim_audio_shutdown(struct audio_lowerhalf_s *dev);
static int sim_audio_start(struct audio_lowerhalf_s *dev);
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
static int sim_audio_stop(struct audio_lowerhalf_s *dev);
#endif
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
static int sim_audio_pause(struct audio_lowerhalf_s *dev);
static int sim_audio_resume(struct audio_lowerhalf_s *dev);
#endif
static int sim_audio_enqueuebuffer(struct audio_lowerhalf_s *dev,
struct ap_buffer_s *apb);
static int sim_audio_ioctl(struct audio_lowerhalf_s *dev, int cmd,
unsigned long arg);
static int sim_audio_reserve(struct audio_lowerhalf_s *dev);
static int sim_audio_release(struct audio_lowerhalf_s *dev);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct audio_ops_s g_sim_audio_ops =
{
.getcaps = sim_audio_getcaps,
.configure = sim_audio_configure,
.shutdown = sim_audio_shutdown,
.start = sim_audio_start,
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
.stop = sim_audio_stop,
#endif
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
.pause = sim_audio_pause,
.resume = sim_audio_resume,
#endif
.enqueuebuffer = sim_audio_enqueuebuffer,
.ioctl = sim_audio_ioctl,
.reserve = sim_audio_reserve,
.release = sim_audio_release,
};
static sq_queue_t g_sim_audio;
/****************************************************************************
* Private Functions
****************************************************************************/
static int sim_audio_config_format(struct sim_audio_s *priv, snd_pcm_t *pcm)
{
snd_pcm_hw_params_t *hw_params;
snd_pcm_uframes_t pframes;
snd_pcm_format_t format;
uint32_t total_size;
int ret;
switch (priv->bps)
{
case 8:
format = SND_PCM_FORMAT_S8;
break;
case 24:
format = SND_PCM_FORMAT_S24;
break;
case 32:
format = SND_PCM_FORMAT_S32;
break;
default:
format = SND_PCM_FORMAT_S16;
break;
}
ret = snd_pcm_hw_params_malloc(&hw_params);
if (ret < 0)
{
return ret;
}
ret = snd_pcm_hw_params_any(pcm, hw_params);
if (ret < 0)
{
goto fail;
}
ret = snd_pcm_hw_params_set_access(pcm, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (ret < 0)
{
goto fail;
}
ret = snd_pcm_hw_params_set_format(pcm, hw_params, format);
if (ret < 0)
{
goto fail;
}
ret = snd_pcm_hw_params_set_rate(pcm, hw_params,
priv->sample_rate, 0);
if (ret < 0)
{
goto fail;
}
ret = snd_pcm_hw_params_set_channels(pcm, hw_params,
priv->channels);
if (ret < 0)
{
goto fail;
}
total_size = priv->nbuffers * priv->buffer_size;
pframes = priv->buffer_size / priv->frame_size;
ret = snd_pcm_hw_params_set_period_size_near(pcm, hw_params,
&pframes, NULL);
if (ret < 0)
{
goto fail;
}
priv->buffer_size = pframes * priv->frame_size;
priv->nbuffers = total_size / priv->buffer_size;
ret = snd_pcm_hw_params_set_periods_near(pcm, hw_params,
&priv->nbuffers, NULL);
if (ret < 0)
{
goto fail;
}
ret = snd_pcm_hw_params(pcm, hw_params);
fail:
snd_pcm_hw_params_free(hw_params);
return ret;
}
static int sim_audio_open(struct sim_audio_s *priv)
{
snd_pcm_t *pcm;
int direction;
int ret;
if (priv->pcm)
{
return 0;
}
direction = priv->playback ? SND_PCM_STREAM_PLAYBACK
: SND_PCM_STREAM_CAPTURE;
ret = snd_pcm_open(&pcm, "default", direction, 0);
if (ret < 0)
{
return ret;
}
ret = sim_audio_config_format(priv, pcm);
if (ret < 0)
{
goto fail;
}
ret = snd_pcm_start(pcm);
if (ret < 0)
{
goto fail;
}
priv->pcm = pcm;
return 0;
fail:
snd_pcm_close(pcm);
return ret;
}
static int sim_audio_close(struct sim_audio_s *priv)
{
if (!priv->pcm)
{
return 0;
}
snd_pcm_close(priv->pcm);
priv->pcm = NULL;
return 0;
}
static int sim_audio_getcaps(struct audio_lowerhalf_s *dev, int type,
struct audio_caps_s *caps)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
long val;
caps->ac_format.hw = 0;
caps->ac_controls.w = 0;
switch (caps->ac_type)
{
case AUDIO_TYPE_QUERY:
caps->ac_channels = 2; /* Stereo output */
switch (caps->ac_subtype)
{
case AUDIO_TYPE_QUERY:
caps->ac_controls.b[0] = (priv->playback ?
AUDIO_TYPE_OUTPUT :
AUDIO_TYPE_INPUT) |
AUDIO_TYPE_FEATURE |
AUDIO_TYPE_PROCESSING;
caps->ac_format.hw = (1 << (AUDIO_FMT_PCM - 1));
break;
default:
caps->ac_controls.b[0] = AUDIO_SUBFMT_END;
break;
}
break;
case AUDIO_TYPE_OUTPUT:
case AUDIO_TYPE_INPUT:
caps->ac_channels = 2;
switch (caps->ac_subtype)
{
case AUDIO_TYPE_QUERY:
/* Report the Sample rates we support */
caps->ac_controls.b[0] = AUDIO_SAMP_RATE_8K |
AUDIO_SAMP_RATE_11K |
AUDIO_SAMP_RATE_16K |
AUDIO_SAMP_RATE_22K |
AUDIO_SAMP_RATE_32K |
AUDIO_SAMP_RATE_44K |
AUDIO_SAMP_RATE_48K;
break;
default:
break;
}
break;
case AUDIO_TYPE_FEATURE:
switch (caps->ac_format.hw)
{
case AUDIO_FU_VOLUME:
snd_mixer_selem_get_playback_volume(priv->volume,
SND_MIXER_SCHN_UNKNOWN,
&val);
caps->ac_controls.w = val;
break;
case AUDIO_FU_INP_GAIN:
snd_mixer_selem_get_capture_volume(priv->volume,
SND_MIXER_SCHN_MONO,
&val);
caps->ac_controls.w = val;
break;
default:
break;
}
break;
default:
caps->ac_subtype = 0;
caps->ac_channels = 0;
break;
}
/* Return the length of the audio_caps_s struct for validation of
* proper Audio device type.
*/
audinfo("Return %d\n", caps->ac_len);
return caps->ac_len;
}
static int sim_audio_configure(struct audio_lowerhalf_s *dev,
const struct audio_caps_s *caps)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
int ret = 0;
switch (caps->ac_type)
{
case AUDIO_TYPE_FEATURE:
switch (caps->ac_format.hw)
{
case AUDIO_FU_VOLUME:
ret = snd_mixer_selem_set_playback_volume_all(priv->volume,
caps->ac_controls.hw[0]);
break;
case AUDIO_FU_INP_GAIN:
ret = snd_mixer_selem_set_capture_volume_all(priv->volume,
caps->ac_controls.hw[0]);
break;
default:
ret = -ENOTTY;
break;
}
break;
case AUDIO_TYPE_OUTPUT:
case AUDIO_TYPE_INPUT:
priv->sample_rate = caps->ac_controls.hw[0] |
(caps->ac_controls.b[3] << 16);
priv->channels = caps->ac_channels;
priv->bps = caps->ac_controls.b[2];
priv->frame_size = priv->bps / 8 * priv->channels;
break;
default:
ret = -ENOTTY;
break;
}
return ret;
}
static int sim_audio_shutdown(struct audio_lowerhalf_s *dev)
{
return 0;
}
static int sim_audio_start(struct audio_lowerhalf_s *dev)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
return sim_audio_open(priv);
}
#ifndef CONFIG_AUDIO_EXCLUDE_STOP
static int sim_audio_stop(struct audio_lowerhalf_s *dev)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
sim_audio_close(priv);
while (!dq_empty(&priv->pendq))
{
struct ap_buffer_s *apb;
apb = (struct ap_buffer_s *)dq_remfirst(&priv->pendq);
priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
}
priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_COMPLETE, NULL, OK);
return 0;
}
#endif
#ifndef CONFIG_AUDIO_EXCLUDE_PAUSE_RESUME
static int sim_audio_pause(struct audio_lowerhalf_s *dev)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
int ret;
if (!priv->pcm)
{
return 0;
}
ret = snd_pcm_pause(priv->pcm, 0);
if (ret < 0)
{
return ret;
}
return 0;
}
static int sim_audio_resume(struct audio_lowerhalf_s *dev)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
int ret;
if (!priv->pcm)
{
return 0;
}
ret = snd_pcm_resume(priv->pcm);
if (ret < 0)
{
return ret;
}
return ret;
}
#endif
static int sim_audio_enqueuebuffer(struct audio_lowerhalf_s *dev,
struct ap_buffer_s *apb)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
apb->flags |= AUDIO_APB_OUTPUT_ENQUEUED;
dq_addlast(&apb->dq_entry, &priv->pendq);
return 0;
}
static int sim_audio_ioctl(struct audio_lowerhalf_s *dev, int cmd,
unsigned long arg)
{
struct sim_audio_s *priv = (struct sim_audio_s *)dev;
int ret = 0;
switch (cmd)
{
case AUDIOIOC_SETBUFFERINFO:
{
struct ap_buffer_info_s *info =
(struct ap_buffer_info_s *)arg;
priv->nbuffers = info->nbuffers;
priv->buffer_size = info->buffer_size;
}
break;
case AUDIOIOC_GETBUFFERINFO:
{
struct ap_buffer_info_s *info =
(struct ap_buffer_info_s *)arg;
info->nbuffers = priv->nbuffers;
info->buffer_size = priv->buffer_size;
}
break;
default:
ret = -ENOTTY;
break;
}
return ret;
}
static int sim_audio_reserve(struct audio_lowerhalf_s *dev)
{
return 0;
}
static int sim_audio_release(struct audio_lowerhalf_s *dev)
{
return 0;
}
static void sim_audio_process(struct sim_audio_s *priv)
{
struct ap_buffer_s *apb;
snd_pcm_sframes_t expect;
snd_pcm_sframes_t avail;
int ret = 0;
if (!priv->pcm)
{
return;
}
apb = (struct ap_buffer_s *)dq_peek(&priv->pendq);
if (!apb)
{
return;
}
expect = priv->playback ? apb->nbytes / priv->frame_size
: AUDMIN(apb->nmaxbytes, priv->buffer_size)
/ priv->frame_size;
avail = snd_pcm_avail(priv->pcm);
if (avail < expect)
{
ret = avail;
goto out;
}
if (priv->playback)
{
ret = snd_pcm_writei(priv->pcm, apb->samp, expect);
}
else
{
ret = snd_pcm_readi(priv->pcm, apb->samp, expect);
}
if (ret >= 0)
{
bool final = false;
dq_remfirst(&priv->pendq);
apb->nbytes = ret * priv->frame_size;
if (apb->flags & AUDIO_APB_FINAL)
{
final = true;
}
priv->dev.upper(priv->dev.priv, AUDIO_CALLBACK_DEQUEUE, apb, OK);
if (final)
{
snd_pcm_drain(priv->pcm);
sim_audio_stop(&priv->dev);
}
}
out:
if (ret == -EPIPE)
{
awarn("ALSA buffer xrun.\n");
snd_pcm_prepare(priv->pcm);
snd_pcm_start(priv->pcm);
}
else if (ret < 0 && ret != -EAGAIN)
{
aerr("pcm writei/readi failed %d, %s\n", ret, snd_strerror(ret));
}
}
static int sim_mixer_open(struct sim_audio_s *priv)
{
snd_mixer_selem_id_t *sid = NULL;
int ret;
ret = snd_mixer_open(&priv->mixer, 0);
if (ret < 0)
{
return ret;
}
ret = snd_mixer_attach(priv->mixer, "default");
if (ret < 0)
{
goto fail;
}
ret = snd_mixer_selem_register(priv->mixer, NULL, NULL);
if (ret < 0)
{
goto fail;
}
ret = snd_mixer_load(priv->mixer);
if (ret < 0)
{
goto fail;
}
ret = snd_mixer_selem_id_malloc(&sid);
if (ret < 0)
{
goto fail;
}
if (priv->playback)
{
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, "Master");
priv->volume = snd_mixer_find_selem(priv->mixer, sid);
snd_mixer_selem_id_free(sid);
if (!priv->volume)
{
goto fail;
}
ret = snd_mixer_selem_set_playback_volume_range(priv->volume, 0, 1000);
if (ret < 0)
{
goto fail;
}
}
else
{
snd_mixer_selem_id_set_index(sid, 0);
snd_mixer_selem_id_set_name(sid, "Capture");
priv->volume = snd_mixer_find_selem(priv->mixer, sid);
snd_mixer_selem_id_free(sid);
if (!priv->volume)
{
goto fail;
}
ret = snd_mixer_selem_set_capture_volume_range(priv->volume, 0, 1000);
if (ret < 0)
{
goto fail;
}
}
return 0;
fail:
snd_mixer_close(priv->mixer);
priv->mixer = NULL;
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
void sim_audio_loop(void)
{
sq_entry_t *entry;
for (entry = sq_peek(&g_sim_audio); entry; entry = sq_next(entry))
{
struct sim_audio_s *priv =
container_of(entry, struct sim_audio_s, link);
sim_audio_process(priv);
}
}
struct audio_lowerhalf_s *sim_audio_initialize(bool playback)
{
struct sim_audio_s *priv;
int ret;
priv = kmm_zalloc(sizeof(struct sim_audio_s));
if (!priv)
{
return NULL;
}
priv->playback = playback;
priv->dev.ops = &g_sim_audio_ops;
ret = sim_mixer_open(priv);
if (ret < 0)
{
kmm_free(priv);
return NULL;
}
sq_addlast(&priv->link, &g_sim_audio);
/* Setting default config */
priv->nbuffers = CONFIG_AUDIO_NUM_BUFFERS;
priv->buffer_size = CONFIG_AUDIO_BUFFER_NUMBYTES;
priv->sample_rate = 48000;
priv->channels = 2;
priv->bps = 16;
priv->frame_size = 4;
return &priv->dev;
}

View File

@ -113,6 +113,10 @@ void up_idle(void)
bthcisock_loop();
#endif
#ifdef CONFIG_SIM_SOUND
sim_audio_loop();
#endif
#ifdef CONFIG_ONESHOT
/* Driver the simulated interval timer */

View File

@ -42,6 +42,7 @@
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/audio/audio.h>
#include <nuttx/drivers/drivers.h>
#include <nuttx/fs/loop.h>
#include <nuttx/fs/ioctl.h>
@ -281,4 +282,9 @@ void up_initialize(void)
#if defined(CONFIG_FS_SMARTFS) && (defined(CONFIG_SIM_SPIFLASH) || defined(CONFIG_SIM_QSPIFLASH))
up_init_smartfs();
#endif
#ifdef CONFIG_SIM_SOUND
audio_register("pcm0p", sim_audio_initialize(true));
audio_register("pcm0c", sim_audio_initialize(false));
#endif
}

View File

@ -397,6 +397,13 @@ int bthcisock_register(int dev_id);
int bthcisock_loop(void);
#endif
/* up_audio.c ***************************************************************/
#ifdef CONFIG_SIM_SOUND
struct audio_lowerhalf_s *sim_audio_initialize(bool playback);
void sim_audio_loop(void);
#endif
/* Debug ********************************************************************/
#ifdef CONFIG_STACK_COLORATION

View File

@ -0,0 +1,59 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ARCH="sim"
CONFIG_ARCH_BOARD="sim"
CONFIG_ARCH_BOARD_SIM=y
CONFIG_ARCH_CHIP="sim"
CONFIG_ARCH_SIM=y
CONFIG_AUDIO=y
CONFIG_BOARDCTL_APP_SYMTAB=y
CONFIG_BOARDCTL_POWEROFF=y
CONFIG_BOARD_LOOPSPERMSEC=0
CONFIG_BOOT_RUNFROMEXTSRAM=y
CONFIG_BUILTIN=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_DEV_LOOP=y
CONFIG_DEV_ZERO=y
CONFIG_EXAMPLES_HELLO=y
CONFIG_FAT_LCNAMES=y
CONFIG_FAT_LFN=y
CONFIG_FSUTILS_PASSWD=y
CONFIG_FSUTILS_PASSWD_READONLY=y
CONFIG_FS_BINFS=y
CONFIG_FS_FAT=y
CONFIG_FS_HOSTFS=y
CONFIG_FS_PROCFS=y
CONFIG_FS_ROMFS=y
CONFIG_IDLETHREAD_STACKSIZE=4096
CONFIG_LIBC_EXECFUNCS=y
CONFIG_LIB_ENVPATH=y
CONFIG_MAX_TASKS=64
CONFIG_NFILE_DESCRIPTORS=32
CONFIG_NSH_ARCHINIT=y
CONFIG_NSH_ARCHROMFS=y
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FATDEVNO=2
CONFIG_NSH_FILE_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_NSH_ROMFSDEVNO=1
CONFIG_NSH_ROMFSETC=y
CONFIG_PATH_INITIAL="/bin"
CONFIG_POSIX_SPAWN_PROXY_STACKSIZE=2048
CONFIG_READLINE_TABCOMPLETION=y
CONFIG_SCHED_HAVE_PARENT=y
CONFIG_SCHED_ONEXIT=y
CONFIG_SCHED_WAITPID=y
CONFIG_SDCLONE_DISABLE=y
CONFIG_START_MONTH=6
CONFIG_START_YEAR=2008
CONFIG_SYSTEM_NSH=y
CONFIG_SYSTEM_NXPLAYER=y
CONFIG_SYSTEM_NXRECORDER=y
CONFIG_USERMAIN_STACKSIZE=4096
CONFIG_USER_ENTRYPOINT="nsh_main"