780 lines
22 KiB
C
780 lines
22 KiB
C
/*
|
|
* PCM - AMLOGIC Audio HAL plugin
|
|
*
|
|
* Copyright (c) 2022 by Wei Du <wei.du@amlogic.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 program 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
#define LOG_TAG "audio_ahal_pcm"
|
|
|
|
#define _GNU_SOURCE
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <pthread.h>
|
|
#include <alsa/asoundlib.h>
|
|
#include <alsa/pcm_external.h>
|
|
#include "audio_if.h"
|
|
#include <IpcBuffer/IpcBuffer_c.h>
|
|
#include <cutils/log.h>
|
|
#include <byteswap.h>
|
|
#include <sys/eventfd.h>
|
|
|
|
#include "pcm.h"
|
|
|
|
//#define VDEBUG
|
|
#ifdef VDEBUG
|
|
#define VDBG(f, ...) \
|
|
fprintf(stderr, "%s:%i %i "f"\n", __FUNCTION__, __LINE__, getpid(), ## __VA_ARGS__);
|
|
#else
|
|
#define VDBG(f, ...)
|
|
#endif
|
|
#define AM_LOGI(fmt, ...) ALOGI("[%s:%d] " fmt, __func__,__LINE__, ##__VA_ARGS__)
|
|
|
|
#define AML_FRAME_SIZE 4
|
|
#define AML_DEVICE_COUNT 20
|
|
#define CAP_BUFFER_NAME "ahal_capture"
|
|
#define CAPTURE_BUFFER_MIN_SIZE (4*48*32)
|
|
#define ENCODER_CAP_DEV 11
|
|
#define AML_PCM_FD_COUNT 1
|
|
|
|
typedef enum _snd_enum_setting_type {
|
|
SND_ENUM_SETTING_TYPE_DEEPBUFFER,
|
|
SND_ENUM_SETTING_TYPE_AUX,
|
|
SND_ENUM_SETTING_TYPE_TTS,
|
|
SND_ENUM_SETTING_TYPE_HWMIXER,
|
|
SND_ENUM_SETTING_TYPE_CAPTURE,
|
|
SND_ENUM_SETTING_TYPE_TINYALSA,
|
|
} snd_enum_setting_type;
|
|
|
|
|
|
typedef struct _snd_pcm_setting_t {
|
|
long device;
|
|
long stream;
|
|
long type;
|
|
} snd_pcm_setting_t;
|
|
|
|
typedef struct _snd_pcm_hal_t {
|
|
snd_pcm_ioplug_t io;
|
|
snd_pcm_setting_t setting[AML_DEVICE_COUNT];
|
|
bool stream_opened;
|
|
bool use_ahal;
|
|
unsigned channels;
|
|
snd_pcm_uframes_t period_size;
|
|
snd_pcm_uframes_t buffer_size;
|
|
unsigned int period_count;
|
|
unsigned int rate;
|
|
size_t frame_size;
|
|
void * transfer_buffer;
|
|
int card;
|
|
int snd_device;
|
|
snd_pcm_stream_t snd_stream;
|
|
|
|
struct pcm_config tiny_config;
|
|
struct pcm *tiny_pcm;
|
|
|
|
audio_hw_device_t *device;
|
|
struct audio_stream_out *stream;
|
|
struct audio_stream_in *in_stream;
|
|
struct audio_config config;
|
|
int flags;
|
|
|
|
snd_enum_setting_type type;
|
|
struct pollfd pfd;
|
|
int event_fd;
|
|
snd_pcm_sframes_t transfer_samples;
|
|
|
|
} snd_pcm_hal_t;
|
|
|
|
static int ahal_audio_sleep(uint64_t us)
|
|
{
|
|
struct timespec ts;
|
|
if (us == 0) {
|
|
return 0;
|
|
}
|
|
|
|
ts.tv_sec = (long)us / 1000000ULL;
|
|
ts.tv_nsec = (long)(us - ts.tv_sec * 1000000ULL) * 1000;
|
|
|
|
return nanosleep(&ts, NULL);
|
|
}
|
|
|
|
static int snd_pcm_hal_dump_data(const char *path, const void *buf, size_t bytes)
|
|
{
|
|
if (!path) {
|
|
return -1;
|
|
}
|
|
|
|
int flen;
|
|
FILE *fp = fopen(path, "ab+");
|
|
if (fp) {
|
|
flen = fwrite((char *)buf, 1, bytes, fp);
|
|
fclose(fp);
|
|
return flen;
|
|
}
|
|
AM_LOGI("fail to open path=%s, errno=%d/%s", path, errno, strerror(errno));
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
static void hal_free(snd_pcm_hal_t *ahal)
|
|
{
|
|
if (ahal->transfer_buffer) {
|
|
free(ahal->transfer_buffer);
|
|
}
|
|
free(ahal);
|
|
}
|
|
|
|
static int snd_pcm_hal_get_flags(int type, int channel)
|
|
{
|
|
int flags = AUDIO_OUTPUT_FLAG_PRIMARY | AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
|
|
switch (type) {
|
|
case SND_ENUM_SETTING_TYPE_DEEPBUFFER:
|
|
flags = AUDIO_OUTPUT_FLAG_PRIMARY | AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
|
|
if (channel > 2) {
|
|
//deep buffer doesn't support multi-ch
|
|
flags = AUDIO_OUTPUT_FLAG_PRIMARY;
|
|
}
|
|
break;
|
|
case SND_ENUM_SETTING_TYPE_AUX:
|
|
flags = AUDIO_OUTPUT_FLAG_PRIMARY;
|
|
break;
|
|
case SND_ENUM_SETTING_TYPE_TTS:
|
|
flags = AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_PRIMARY;
|
|
break;
|
|
case SND_ENUM_SETTING_TYPE_HWMIXER:
|
|
flags = AUDIO_OUTPUT_FLAG_FAST;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
static int snd_pcm_hal_open_stream(snd_pcm_hal_t *ahal)
|
|
{
|
|
int err;
|
|
int ret = 0;
|
|
err = audio_hw_load_interface(&ahal->device);
|
|
if (err) {
|
|
AM_LOGI("error:%d", err);
|
|
return err;
|
|
}
|
|
|
|
int inited = ahal->device->init_check(ahal->device);
|
|
if (inited) {
|
|
AM_LOGI("device not inited, quit");
|
|
err = -1;
|
|
goto exit;
|
|
}
|
|
|
|
memset(&ahal->config, 0, sizeof(struct audio_config));
|
|
ahal->config.sample_rate = ahal->rate;
|
|
switch (ahal->channels) {
|
|
case 1:
|
|
//set as STEREO, transfer to STEREO before write!!
|
|
ahal->config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
|
|
break;
|
|
case 2:
|
|
ahal->config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
|
|
break;
|
|
case 6:
|
|
ahal->config.channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
|
|
break;
|
|
case 8:
|
|
ahal->config.channel_mask = AUDIO_CHANNEL_OUT_7POINT1;
|
|
break;
|
|
default:
|
|
ahal->config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
|
|
break;
|
|
}
|
|
|
|
ahal->config.format = AUDIO_FORMAT_PCM_16_BIT;
|
|
|
|
if (SND_ENUM_SETTING_TYPE_CAPTURE == ahal->type) {
|
|
ahal->config.channel_mask = AUDIO_CHANNEL_IN_STEREO;
|
|
audio_source_t src = AUDIO_SOURCE_MIXER_OUT;
|
|
if (ahal->snd_device == ENCODER_CAP_DEV) {
|
|
src = AUDIO_SOURCE_ENCODER_OUT;
|
|
}
|
|
ret = ahal->device->open_input_stream(ahal->device, 0, AUDIO_DEVICE_OUT_SPEAKER, &ahal->config, &ahal->in_stream, AUDIO_INPUT_FLAG_RAW, NULL, src);
|
|
if (ret) {
|
|
AM_LOGI("open input stream fail");
|
|
goto exit;
|
|
} else {
|
|
AM_LOGI("open input stream success");
|
|
}
|
|
} else {
|
|
char addr[64] = {0};
|
|
snprintf(addr, sizeof(addr), "pcm_ahal_%d", ahal->snd_device);
|
|
err = ahal->device->open_output_stream(ahal->device,
|
|
0, AUDIO_DEVICE_OUT_SPEAKER,
|
|
ahal->flags, &ahal->config,
|
|
&ahal->stream, addr);
|
|
if (err) {
|
|
AM_LOGI("open output stream error: channel_mask(%#x) rate(%d) format(%#x) flags(%#x)",
|
|
ahal->config.channel_mask, ahal->config.sample_rate, ahal->config.format, ahal->flags);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
exit:
|
|
audio_hw_unload_interface(ahal->device);
|
|
return -1;
|
|
}
|
|
|
|
static int snd_pcm_hal_open_alsa(snd_pcm_hal_t *ahal)
|
|
{
|
|
struct pcm_config *config = &ahal->tiny_config;
|
|
|
|
memset(config, 0, sizeof(struct pcm_config));
|
|
config->channels = ahal->channels;
|
|
config->rate = ahal->rate;
|
|
config->period_size = ahal->period_size;
|
|
config->period_count = ahal->period_count;
|
|
config->format = PCM_FORMAT_S16_LE;
|
|
config->start_threshold = 0;
|
|
config->stop_threshold = 0;
|
|
config->silence_threshold = 0;
|
|
|
|
ahal->tiny_pcm = pcm_open(ahal->card, ahal->snd_device,
|
|
ahal->snd_stream == SND_PCM_STREAM_PLAYBACK? PCM_OUT: PCM_IN,
|
|
config);
|
|
|
|
if (!ahal->tiny_pcm || !pcm_is_ready(ahal->tiny_pcm)) {
|
|
AM_LOGI("Unable to open PCM device %u (%s)\n",
|
|
ahal->snd_device, pcm_get_error(ahal->tiny_pcm));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int snd_pcm_hal_apply_conf(snd_pcm_hal_t *ahal)
|
|
{
|
|
int i = 0;
|
|
ahal->use_ahal = false;
|
|
snd_pcm_setting_t* setting = NULL;
|
|
for (i = 0; i < AML_DEVICE_COUNT; i++) {
|
|
setting = &ahal->setting[i];
|
|
if (ahal->snd_device == setting->device && ahal->snd_stream == setting->stream) {
|
|
ahal->use_ahal = (setting->type != SND_ENUM_SETTING_TYPE_TINYALSA);
|
|
ahal->type = setting->type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ahal->flags = snd_pcm_hal_get_flags(ahal->type, ahal->channels);
|
|
|
|
AM_LOGI("type %d", ahal->type);
|
|
return 0;
|
|
}
|
|
|
|
static int snd_pcm_hal_close(snd_pcm_ioplug_t *io)
|
|
{
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
|
|
if (ahal) {
|
|
AM_LOGI("---in---stream opened: %d, ahal %p. event fd %d", ahal->stream_opened, ahal, ahal->event_fd);
|
|
if (ahal->stream_opened) {
|
|
if (ahal->use_ahal) {
|
|
if (SND_ENUM_SETTING_TYPE_CAPTURE == ahal->type) {
|
|
ahal->device->close_input_stream(ahal->device, ahal->in_stream);
|
|
} else {
|
|
ahal->device->close_output_stream(ahal->device, ahal->stream);
|
|
}
|
|
audio_hw_unload_interface(ahal->device);
|
|
} else {
|
|
pcm_close(ahal->tiny_pcm);
|
|
ahal->tiny_pcm = NULL;
|
|
}
|
|
}
|
|
if (ahal->event_fd > 0) {
|
|
close(ahal->event_fd);
|
|
}
|
|
ahal->stream_opened = false;
|
|
io->private_data = NULL;
|
|
hal_free(ahal);
|
|
} else {
|
|
AM_LOGI("ahal is null");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static snd_pcm_sframes_t snd_pcm_hal_pointer(snd_pcm_ioplug_t *io)
|
|
{
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
snd_pcm_sframes_t hw_pointer = 0;
|
|
|
|
switch (io->state) {
|
|
case SND_PCM_STATE_RUNNING:
|
|
if (ahal->snd_stream == SND_PCM_STREAM_PLAYBACK) {
|
|
hw_pointer = ahal->transfer_samples;
|
|
} else {
|
|
hw_pointer = ahal->transfer_samples + ahal->period_size;
|
|
}
|
|
break;
|
|
case SND_PCM_STATE_XRUN:
|
|
hw_pointer = -EPIPE;
|
|
break;
|
|
default:
|
|
hw_pointer = 0;
|
|
break;
|
|
}
|
|
//AM_LOGI("---in---hw_pointer:%ld", hw_pointer);
|
|
|
|
return hw_pointer;
|
|
}
|
|
|
|
static int snd_pcm_hal_prepare(snd_pcm_ioplug_t *io)
|
|
{
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
int ret = 0;
|
|
if (!ahal) {
|
|
return -1;
|
|
}
|
|
|
|
AM_LOGI("---in---stream opened: %d", ahal->stream_opened);
|
|
|
|
if (!ahal->stream_opened) {
|
|
snd_pcm_hal_apply_conf(ahal);
|
|
|
|
if (ahal->use_ahal) {
|
|
ret = snd_pcm_hal_open_stream(ahal);
|
|
} else {
|
|
ret = snd_pcm_hal_open_alsa(ahal);
|
|
}
|
|
if (ret < 0) {
|
|
ahal->stream_opened = false;
|
|
} else {
|
|
ahal->stream_opened = true;
|
|
}
|
|
}
|
|
|
|
AM_LOGI("---out---");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int snd_pcm_hal_start(snd_pcm_ioplug_t *io)
|
|
{
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
AM_LOGI("---in---");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int snd_pcm_hal_stop(snd_pcm_ioplug_t *io)
|
|
{
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
AM_LOGI("---in---");
|
|
return 0;
|
|
}
|
|
|
|
static snd_pcm_sframes_t snd_pcm_hal_write(snd_pcm_ioplug_t *io,
|
|
const snd_pcm_channel_area_t *areas,
|
|
snd_pcm_uframes_t offset,
|
|
snd_pcm_uframes_t size)
|
|
{
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
char *buf;
|
|
size_t writebytes;
|
|
//AM_LOGI("ahal write in %d", size);
|
|
|
|
buf = (char *) areas->addr + (areas->first +
|
|
areas->step * offset) / 8;
|
|
writebytes = size * ahal->channels * 2;
|
|
if (io->format == SND_PCM_FORMAT_S16_BE) {
|
|
int16_t *buffer = (int16_t *)buf;
|
|
for (int i = 0; i < size * ahal->channels; i++) {
|
|
buffer[i] = bswap_16(buffer[i]); // 16-bit be to le
|
|
}
|
|
}
|
|
if (ahal->use_ahal) {
|
|
if (ahal->channels == 1) {
|
|
short * frame = (short *)ahal->transfer_buffer;
|
|
short * ori_frame = (short *)buf;
|
|
for (int i = 0; i < size; i ++) {
|
|
frame[i*2] = frame[i*2+1] = ori_frame[i];
|
|
}
|
|
buf = (char *)ahal->transfer_buffer;
|
|
writebytes = writebytes *2;
|
|
}
|
|
|
|
ssize_t s = ahal->stream->write(ahal->stream, buf, writebytes);
|
|
if (s < 0) {
|
|
AM_LOGI("stream writing error %d", s);
|
|
}
|
|
} else {
|
|
pcm_writei(ahal->tiny_pcm, buf, size);
|
|
}
|
|
ahal->transfer_samples += size;
|
|
//AM_LOGI("ahal write out %d");
|
|
return size;
|
|
}
|
|
|
|
static snd_pcm_sframes_t snd_pcm_hal_read(snd_pcm_ioplug_t *io,
|
|
const snd_pcm_channel_area_t *areas,
|
|
snd_pcm_uframes_t offset,
|
|
snd_pcm_uframes_t size)
|
|
{
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
snd_pcm_uframes_t read = 0;
|
|
char * buf;
|
|
//AM_LOGI("---in---size:%ld", size);
|
|
buf = (char *) areas->addr + (areas->first +
|
|
areas->step * offset) / 8;
|
|
|
|
if (size) {
|
|
if (ahal->use_ahal) {
|
|
int read_len = 0;
|
|
|
|
read_len = ahal->in_stream->read(ahal->in_stream, buf, size * AML_FRAME_SIZE);
|
|
read = read_len / AML_FRAME_SIZE;
|
|
} else {
|
|
read = pcm_readi(ahal->tiny_pcm, buf, size);
|
|
}
|
|
}
|
|
//AM_LOGI("---out---size:%ld", read);
|
|
|
|
ahal->transfer_samples += read;
|
|
|
|
return read;
|
|
}
|
|
|
|
static int snd_pcm_hal_drain(snd_pcm_ioplug_t * io)
|
|
{
|
|
int err = 0;
|
|
|
|
AM_LOGI("--drain out---");
|
|
return err;
|
|
}
|
|
|
|
static int snd_pcm_hal_hw_params(snd_pcm_ioplug_t * io, snd_pcm_hw_params_t * params)
|
|
{
|
|
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
int err = 0;
|
|
AM_LOGI("---in---");
|
|
if (!ahal) {
|
|
return -1;
|
|
}
|
|
|
|
switch (io->format) {
|
|
case SND_PCM_FORMAT_S16_LE:
|
|
case SND_PCM_FORMAT_S16_BE:
|
|
break;
|
|
default:
|
|
AM_LOGI("Ahal: Unsupported format %s\n", snd_pcm_format_name(io->format));
|
|
err = -EINVAL;
|
|
goto finish;
|
|
}
|
|
|
|
ahal->rate = io->rate;
|
|
ahal->channels = io->channels;
|
|
ahal->period_size = io->period_size;
|
|
ahal->buffer_size = io->buffer_size;
|
|
ahal->period_count = io->buffer_size / io->period_size;
|
|
ahal->frame_size = io->channels * 2;
|
|
if (ahal->channels == 1) {
|
|
ahal->transfer_buffer = calloc(1, ahal->buffer_size * ahal->frame_size);
|
|
}
|
|
|
|
AM_LOGI("rate:%d, channel:%d, period size: %lu, count: %d, buffer_size:%lu", ahal->rate, ahal->channels, ahal->period_size, ahal->period_count, ahal->buffer_size);
|
|
|
|
finish:
|
|
return err;
|
|
}
|
|
|
|
static int snd_pcm_hal_poll_revents(snd_pcm_ioplug_t * io, struct pollfd *pfd, unsigned int nfds, unsigned short *revents)
|
|
{
|
|
int err = 0;
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
if (!ahal) {
|
|
return -1;
|
|
}
|
|
*revents = ahal->snd_stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
|
|
return err;
|
|
}
|
|
|
|
static int snd_pcm_hal_poll_descriptors_count(snd_pcm_ioplug_t * io)
|
|
{
|
|
return AML_PCM_FD_COUNT;
|
|
}
|
|
|
|
static int snd_pcm_hal_poll_descriptors(snd_pcm_ioplug_t *io, struct pollfd *pfd, unsigned int space)
|
|
{
|
|
snd_pcm_hal_t *ahal = io->private_data;
|
|
if (!ahal) {
|
|
return -1;
|
|
}
|
|
if (space != AML_PCM_FD_COUNT)
|
|
return -EINVAL;
|
|
|
|
if (io->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
pfd[0].fd = ahal->event_fd;
|
|
pfd[0].events = POLLOUT;
|
|
} else {
|
|
pfd[0].fd = ahal->event_fd;
|
|
pfd[0].events = POLLIN;
|
|
}
|
|
|
|
return space;
|
|
}
|
|
|
|
static snd_pcm_ioplug_callback_t hal_playback_callback = {
|
|
.close = snd_pcm_hal_close,
|
|
.start = snd_pcm_hal_start,
|
|
.stop = snd_pcm_hal_stop,
|
|
.transfer = snd_pcm_hal_write,
|
|
.pointer = snd_pcm_hal_pointer,
|
|
.prepare = snd_pcm_hal_prepare,
|
|
.drain = snd_pcm_hal_drain,
|
|
.hw_params = snd_pcm_hal_hw_params,
|
|
};
|
|
static snd_pcm_ioplug_callback_t hal_capture_callback = {
|
|
.close = snd_pcm_hal_close,
|
|
.start = snd_pcm_hal_start,
|
|
.stop = snd_pcm_hal_stop,
|
|
.transfer = snd_pcm_hal_read,
|
|
.pointer = snd_pcm_hal_pointer,
|
|
.prepare = snd_pcm_hal_prepare,
|
|
.drain = snd_pcm_hal_drain,
|
|
.hw_params = snd_pcm_hal_hw_params,
|
|
.poll_revents = snd_pcm_hal_poll_revents,
|
|
.poll_descriptors_count = snd_pcm_hal_poll_descriptors_count,
|
|
.poll_descriptors = snd_pcm_hal_poll_descriptors,
|
|
};
|
|
|
|
#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0]))
|
|
|
|
static int hal_set_hw_constraint(snd_pcm_hal_t *ahal)
|
|
{
|
|
unsigned access_list[] = {
|
|
SND_PCM_ACCESS_RW_INTERLEAVED,
|
|
SND_PCM_ACCESS_MMAP_INTERLEAVED,
|
|
};
|
|
unsigned format_list[] = {
|
|
SND_PCM_FORMAT_S16_LE,
|
|
SND_PCM_FORMAT_S16_BE,
|
|
};
|
|
|
|
int err;
|
|
|
|
if ((err = snd_pcm_ioplug_set_param_list(&ahal->io, SND_PCM_IOPLUG_HW_ACCESS,
|
|
ARRAY_SIZE(access_list), access_list)) < 0 ||
|
|
(err = snd_pcm_ioplug_set_param_list(&ahal->io, SND_PCM_IOPLUG_HW_FORMAT,
|
|
ARRAY_SIZE(format_list), format_list)) < 0 ||
|
|
(err = snd_pcm_ioplug_set_param_minmax(&ahal->io, SND_PCM_IOPLUG_HW_CHANNELS,
|
|
1, 6)) < 0 ||
|
|
(err = snd_pcm_ioplug_set_param_minmax(&ahal->io, SND_PCM_IOPLUG_HW_RATE,
|
|
16000, 48000)) < 0 ||
|
|
(err = snd_pcm_ioplug_set_param_minmax(&ahal->io,
|
|
SND_PCM_IOPLUG_HW_BUFFER_BYTES,
|
|
256*4*4, 4*1024*16)) < 0 ||
|
|
(err = snd_pcm_ioplug_set_param_minmax(&ahal->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
|
|
256*2, 4096*4)) < 0 ||
|
|
(err = snd_pcm_ioplug_set_param_minmax(&ahal->io, SND_PCM_IOPLUG_HW_PERIODS,
|
|
4, 25)) < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static int snd_pcm_hal_parse_setting(snd_pcm_hal_t *ahal, snd_config_t *conf)
|
|
{
|
|
int idx = 0;
|
|
snd_config_iterator_t i, next;
|
|
snd_config_t *node;
|
|
snd_config_t *sub_node;
|
|
snd_config_iterator_t j, sub_next;
|
|
snd_pcm_setting_t* setting = NULL;
|
|
snd_config_for_each(i, next, conf) {
|
|
node = snd_config_iterator_entry(i);
|
|
//AM_LOGI("type %d", snd_config_get_type(node));
|
|
setting = &ahal->setting[idx];
|
|
snd_config_for_each(j, sub_next, node) {
|
|
sub_node = snd_config_iterator_entry(j);
|
|
//AM_LOGI("type %d", snd_config_get_type(sub_node));
|
|
const char * name;
|
|
snd_config_get_id(sub_node, &name);
|
|
//AM_LOGI("name %s", name);
|
|
if (strcmp(name, "id") == 0) {
|
|
if (snd_config_get_type(sub_node) != SND_CONFIG_TYPE_INTEGER) {
|
|
AM_LOGI("Invalid type for %s", name);
|
|
return -EINVAL;
|
|
}
|
|
snd_config_get_integer(sub_node, &setting->device);
|
|
//AM_LOGI("device %d", setting->device);
|
|
continue;
|
|
}
|
|
if (strcmp(name, "stream") == 0) {
|
|
if (snd_config_get_type(sub_node) != SND_CONFIG_TYPE_INTEGER) {
|
|
AM_LOGI("Invalid type for %s", name);
|
|
return -EINVAL;
|
|
}
|
|
snd_config_get_integer(sub_node, &setting->stream);
|
|
//AM_LOGI("playback %d", setting->playback);
|
|
if (setting->stream) {
|
|
setting->stream = 1;
|
|
}
|
|
continue;
|
|
}
|
|
if (strcmp(name, "type") == 0) {
|
|
if (snd_config_get_type(sub_node) != SND_CONFIG_TYPE_INTEGER) {
|
|
AM_LOGI("Invalid type for %s", name);
|
|
return -EINVAL;
|
|
}
|
|
snd_config_get_integer(sub_node, &setting->type);
|
|
//AM_LOGI("flag %d", setting->flag);
|
|
continue;
|
|
}
|
|
}
|
|
idx++;
|
|
}
|
|
|
|
if (idx > 0) {
|
|
for (int k = 0; k < idx; k++) {
|
|
//AM_LOGI("[%d] id:%ld, stream:%ld, type %ld", k, ahal->setting[k].device, ahal->setting[k].stream, ahal->setting[k].type);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int snd_pcm_hal_parse_conf(snd_pcm_hal_t *ahal, snd_config_t *conf)
|
|
{
|
|
snd_config_iterator_t i, next;
|
|
long device = 0;
|
|
int card = -1;
|
|
int err = -1;
|
|
|
|
snd_config_for_each(i, next, conf) {
|
|
snd_config_t *n = snd_config_iterator_entry(i);
|
|
const char *id;
|
|
if (snd_config_get_id(n, &id) < 0)
|
|
continue;
|
|
|
|
if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0
|
|
|| strcmp(id, "subdevice")== 0 || strcmp(id, "hint") == 0)
|
|
continue;
|
|
if (strcmp(id, "card") == 0) {
|
|
card = snd_config_get_card(n);
|
|
if (card < 0) {
|
|
AM_LOGI("Invalid card '%s'", id);
|
|
card = 0;
|
|
}
|
|
continue;
|
|
}
|
|
if (strcmp(id, "device") == 0) {
|
|
err = snd_config_get_integer(n, &device);
|
|
if (err < 0) {
|
|
AM_LOGI("Invalid type for %s", id);
|
|
return -EINVAL;
|
|
}
|
|
continue;
|
|
}
|
|
if (strcmp(id, "setting") == 0) {
|
|
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
|
|
AM_LOGI("Invalid type for %s", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
AM_LOGI("array %d", snd_config_is_array(n));
|
|
snd_pcm_hal_parse_setting(ahal, n);
|
|
continue;
|
|
}
|
|
|
|
AM_LOGI("Unknown field %s", id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
AM_LOGI("card:%d device:%ld", card, device);
|
|
ahal->card = card;
|
|
ahal->snd_device = device;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int snd_pcm_hal_open(snd_pcm_t **pcmp, const char *name,
|
|
snd_config_t *conf, snd_pcm_stream_t stream, int mode)
|
|
{
|
|
snd_pcm_hal_t *ahal;
|
|
int err;
|
|
|
|
assert(pcmp);
|
|
ahal = calloc(1, sizeof(*ahal));
|
|
if (!ahal)
|
|
return -ENOMEM;
|
|
ALOGI("name %s. stream %d, mode %d", name, stream, mode);
|
|
|
|
snd_pcm_hal_parse_conf(ahal, conf);
|
|
|
|
ahal->channels = 2;
|
|
ahal->period_size = 1024;
|
|
ahal->rate = 48000;
|
|
ahal->snd_stream = stream;
|
|
|
|
ahal->io.version = SND_PCM_IOPLUG_VERSION;
|
|
ahal->io.name = "ALSA <-> AMLOGIC HAL PCM I/O Plugin";
|
|
ahal->io.callback = stream == SND_PCM_STREAM_PLAYBACK ? &hal_playback_callback : &hal_capture_callback;
|
|
ahal->io.private_data = ahal;
|
|
ahal->io.mmap_rw = 0;
|
|
ahal->io.poll_fd = ahal->pfd.fd;
|
|
ahal->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
|
|
|
|
err = snd_pcm_ioplug_create(&ahal->io, name, stream, mode);
|
|
if (err < 0) {
|
|
hal_free(ahal);
|
|
return err;
|
|
}
|
|
ahal->event_fd = eventfd(1, EFD_NONBLOCK);
|
|
if (ahal->event_fd == -1) {
|
|
ALOGE("fail create event_fd:%s", strerror(errno));
|
|
} else {
|
|
AM_LOGI("create event_fd:%d", ahal->event_fd);
|
|
}
|
|
|
|
err = hal_set_hw_constraint(ahal);
|
|
if (err < 0) {
|
|
snd_pcm_ioplug_delete(&ahal->io);
|
|
return err;
|
|
}
|
|
|
|
*pcmp = ahal->io.pcm;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
SND_PCM_PLUGIN_DEFINE_FUNC(ahal)
|
|
{
|
|
return snd_pcm_hal_open(pcmp, name, conf, stream, mode);
|
|
}
|
|
|
|
SND_PCM_PLUGIN_SYMBOL(ahal);
|