Files
2026-08-24 10:03:24 +08:00

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);