* apps/audioutils/tinycompress/src/lib/compress_hw.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 <debug.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <mqueue.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/time.h>
#include <unistd.h>
#include <nuttx/audio/audio.h>
#include <nuttx/config.h>
#include <sound/compress_offload.h>
#include <tinycompress/compress_ops.h>
* Pre-processor Prototypes
****************************************************************************/
#define DEFAULT_MAX_POLL_WAIT_MS 20000
* Private Data
****************************************************************************/
struct compress_hw_data
{
char name[32];
int fd;
mqd_t mq;
int flags;
int max_poll_wait_ms;
int nonblocking;
FAR void *session;
dq_queue_t bufferq;
struct compr_config config;
compress_event_t on_event;
FAR void *cookie;
};
* Private Functions
****************************************************************************/
static int compress_hw_get_capabilities(int fd, int ac_type, int ac_subtype,
FAR struct audio_caps_s *caps)
{
int ret;
caps->ac_len = sizeof(struct audio_caps_s);
caps->ac_type = ac_type;
caps->ac_subtype = ac_subtype;
ret = ioctl(fd, AUDIOIOC_GETCAPS, caps);
if (ret < 0)
{
return -errno;
}
return ret;
}
static int compress_hw_query_supported_codecs(int fd,
int codecs[MAX_NUM_CODECS])
{
struct audio_caps_s caps;
int ncodecs = 0;
int ac_subtype;
int format;
int ret;
int k;
ret = compress_hw_get_capabilities(fd, AUDIO_TYPE_QUERY, AUDIO_TYPE_QUERY,
&caps);
if (ret < 0)
{
return ret;
}
format = caps.ac_format.hw;
for (; format; )
{
ac_subtype = AUDIO_FMT_UNDEF;
if (format & (1 << (AUDIO_FMT_PCM - 1)))
{
ac_subtype = AUDIO_FMT_PCM;
format &= ~(1 << (AUDIO_FMT_PCM - 1));
}
else if (format & (1 << (AUDIO_FMT_MP3 - 1)))
{
ac_subtype = AUDIO_FMT_MP3;
format &= ~(1 << (AUDIO_FMT_MP3 - 1));
}
else if (format & (1 << (AUDIO_FMT_AC3 - 1)))
{
ac_subtype = AUDIO_FMT_AC3;
format &= ~(1 << (AUDIO_FMT_AC3 - 1));
}
else if (format & (1 << (AUDIO_FMT_WMA - 1)))
{
ac_subtype = AUDIO_FMT_WMA;
format &= ~(1 << (AUDIO_FMT_WMA - 1));
}
else if (format & (1 << (AUDIO_FMT_DTS - 1)))
{
ac_subtype = AUDIO_FMT_DTS;
format &= ~(1 << (AUDIO_FMT_DTS - 1));
}
else if (format & (1 << (AUDIO_FMT_OGG_VORBIS - 1)))
{
ac_subtype = AUDIO_FMT_OGG_VORBIS;
format &= ~(1 << (AUDIO_FMT_OGG_VORBIS - 1));
}
else if (format & (1 << (AUDIO_FMT_FLAC - 1)))
{
ac_subtype = AUDIO_FMT_FLAC;
format &= ~(1 << (AUDIO_FMT_FLAC - 1));
}
else if (format & (1 << (AUDIO_FMT_AMR - 1)))
{
ac_subtype = AUDIO_FMT_AMR;
format &= ~(1 << (AUDIO_FMT_AMR - 1));
}
else if (format & (1 << (AUDIO_FMT_AAC - 1)))
{
ac_subtype = AUDIO_FMT_AAC;
format &= ~(1 << (AUDIO_FMT_AAC - 1));
}
else if (format & (1 << (AUDIO_FMT_OTHER - 1)))
{
ac_subtype = AUDIO_FMT_OTHER;
format &= ~(1 << (AUDIO_FMT_OTHER - 1));
}
ret = compress_hw_get_capabilities(fd, AUDIO_TYPE_QUERY, ac_subtype,
&caps);
if (ret < 0)
{
continue;
}
if (ac_subtype == AUDIO_FMT_OTHER)
{
for (; caps.ac_controls.w; )
{
ac_subtype = AUDIO_FMT_UNDEF;
if (caps.ac_controls.w & (1 << (AUDIO_FMT_OPUS - 1)))
{
ac_subtype = AUDIO_FMT_OPUS;
caps.ac_controls.w &= ~(1 << (AUDIO_FMT_OPUS - 1));
}
ret = compress_hw_get_capabilities(fd, AUDIO_TYPE_QUERY,
ac_subtype, &caps);
if (ret < 0)
{
continue;
}
for (k = 0; k < sizeof(caps.ac_controls.b) &&
ncodecs < MAX_NUM_CODECS;
k++)
{
if (caps.ac_controls.b[k] == AUDIO_SUBFMT_END)
{
if (k == 0)
{
codecs[ncodecs++] = ac_subtype;
}
break;
}
codecs[ncodecs++] = caps.ac_controls.b[k];
}
}
}
else
{
for (k = 0;
k < sizeof(caps.ac_controls.b) && ncodecs < MAX_NUM_CODECS;
k++)
{
if (caps.ac_controls.b[k] == AUDIO_SUBFMT_END)
{
if (k == 0)
{
codecs[ncodecs++] = ac_subtype;
}
break;
}
codecs[ncodecs++] = caps.ac_controls.b[k];
}
}
}
return ncodecs;
}
static bool compress_hw_is_codec_type_supported(int fd,
FAR struct snd_codec *codec)
{
int codecs[MAX_NUM_CODECS];
int ncodecs;
int i;
ncodecs = compress_hw_query_supported_codecs(fd, codecs);
for (i = 0; i < ncodecs; i++)
{
if (codecs[i] == codec->id)
{
return true;
}
}
return false;
}
static void compress_hw_deinit(FAR struct compress_hw_data *compress)
{
char mqname[64];
if (compress->mq >= 0)
{
ioctl(compress->fd, AUDIOIOC_UNREGISTERMQ, compress->session);
mq_close(compress->mq);
compress->mq = -1;
snprintf(mqname, sizeof(mqname), "/tmp/%s", compress->name);
mq_unlink(mqname);
}
ioctl(compress->fd, AUDIOIOC_RELEASE, compress->session);
close(compress->fd);
compress->fd = -1;
}
static int compress_hw_init(FAR struct compress_hw_data *compress,
FAR const char *name)
{
char mqname[64];
int ret;
struct mq_attr attr =
{
.mq_maxmsg = 32,
.mq_msgsize = sizeof(struct audio_msg_s),
};
compress->fd = open(name, O_RDWR | O_CLOEXEC);
if (compress->fd < 0)
{
return -errno;
}
ret = ioctl(compress->fd, AUDIOIOC_RESERVE, &compress->session);
if (ret < 0)
{
ret = -errno;
goto out;
}
snprintf(mqname, sizeof(mqname), "/tmp/%s", compress->name);
compress->mq =
mq_open(mqname, O_RDWR | O_CREAT | O_CLOEXEC, 0644, &attr);
if (compress->mq < 0)
{
ret = -errno;
goto out;
}
ret = ioctl(compress->fd, AUDIOIOC_REGISTERMQ, compress->mq);
if (ret < 0)
{
ret = -errno;
goto out;
}
return 0;
out:
compress_hw_deinit(compress);
return ret;
}
static int compress_hw_set_codec_params(FAR void *compress_data,
FAR struct snd_codec *codec)
{
FAR struct compress_hw_data *compress = compress_data;
struct audio_caps_desc_s caps_desc;
int ret;
if (!codec)
{
return -EINVAL;
}
memset(&caps_desc, 0, sizeof(caps_desc));
caps_desc.caps.ac_len = sizeof(struct audio_caps_s);
caps_desc.caps.ac_type =
compress->flags & COMPRESS_IN ? AUDIO_TYPE_OUTPUT : AUDIO_TYPE_INPUT;
caps_desc.caps.ac_channels = codec->ch_in;
caps_desc.caps.ac_controls.hw[0] = codec->sample_rate;
caps_desc.caps.ac_controls.b[3] = codec->sample_rate >> 16;
caps_desc.caps.ac_format.b[0] = codec->pcm_format;
caps_desc.caps.ac_subtype = codec->id;
caps_desc.caps.ac_codec.ch_mode = codec->ch_mode;
caps_desc.caps.ac_codec.level = codec->level;
caps_desc.caps.ac_codec.profile = codec->profile;
caps_desc.caps.ac_codec.rate_control = codec->rate_control;
caps_desc.caps.ac_codec.format = codec->format;
caps_desc.caps.ac_codec.align = codec->align;
caps_desc.caps.ac_codec.bit_rate = codec->bit_rate;
memcpy(&caps_desc.caps.ac_codec.options, &codec->options,
sizeof(codec->options));
ret = ioctl(compress->fd, AUDIOIOC_CONFIGURE, &caps_desc);
audinfo("configure, codec:%" PRIu32 ", bit_rate:%" PRIu32 ", ch:%" PRIu32
", rate:%" PRIu32 ", ret:%d\n",
codec->id, codec->bit_rate, codec->ch_in,
codec->sample_rate, ret);
if (ret < 0)
{
return -errno;
}
return 0;
}
static int compress_hw_get_codec_params(FAR void *compress_data,
FAR struct snd_codec *codec)
{
FAR struct compress_hw_data *compress = compress_data;
struct audio_info_s info;
int ret;
if (!codec)
{
return -EINVAL;
}
memset(codec, 0, sizeof(*codec));
ret = ioctl(compress->fd, AUDIOIOC_GETAUDIOINFO, &info);
if (ret < 0)
{
return -errno;
}
codec->id = info.format;
codec->ch_in = info.channels;
codec->ch_out = info.channels;
codec->sample_rate = info.samplerate;
codec->pcm_format = info.subformat;
codec->ch_mode = info.codec.ch_mode;
codec->level = info.codec.level;
codec->profile = info.codec.profile;
codec->rate_control = info.codec.rate_control;
codec->format = info.codec.format;
codec->align = info.codec.align;
codec->bit_rate = info.codec.bit_rate;
memcpy(&codec->options, &info.codec.options, sizeof(info.codec.options));
return 0;
}
static int compress_hw_set_current_config(FAR void *compress_data,
FAR struct compr_config *config)
{
FAR struct compress_hw_data *compress = compress_data;
struct audio_buf_desc_s buf_desc;
struct ap_buffer_info_s buf_info;
int ret;
int i;
ret = compress_hw_set_codec_params(compress, config->codec);
if (ret < 0)
{
return ret;
}
if (config->fragments)
{
buf_info.nbuffers = config->fragments;
buf_info.buffer_size = config->fragment_size;
audinfo("set buffer info, n:%d size:%d\n", buf_info.nbuffers,
buf_info.buffer_size);
ioctl(compress->fd, AUDIOIOC_SETBUFFERINFO, &buf_info);
}
ret = ioctl(compress->fd, AUDIOIOC_GETBUFFERINFO, &buf_info);
audinfo("get buffer info, n:%d size:%d ret:%d\n", buf_info.nbuffers,
buf_info.buffer_size, ret);
if (ret >= 0)
{
config->fragments = buf_info.nbuffers;
config->fragment_size = buf_info.buffer_size;
}
else
{
config->fragments = CONFIG_AUDIO_NUM_BUFFERS;
config->fragment_size = CONFIG_AUDIO_BUFFER_NUMBYTES;
}
dq_init(&compress->bufferq);
for (i = 0; i < config->fragments; i++)
{
FAR struct ap_buffer_s *buffer;
buf_desc.numbytes = config->fragment_size;
buf_desc.u.pbuffer = &buffer;
ret = ioctl(compress->fd, AUDIOIOC_ALLOCBUFFER, &buf_desc);
if (ret < 0)
{
return -errno;
}
dq_addlast(&buffer->dq_entry, &compress->bufferq);
}
compress->config.fragment_size = config->fragment_size;
compress->config.fragments = config->fragments;
return 0;
}
static int compress_hw_poll_available(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
bool nonblocking = compress->nonblocking;
FAR struct ap_buffer_s *buffer;
struct audio_msg_s msg;
bool received = false;
struct timespec ts0 =
{
0,
0
};
for (; ; )
{
if (mq_timedreceive(compress->mq, (FAR char *)&msg, sizeof(msg), NULL,
nonblocking ? &ts0 : NULL) < 0)
{
if (errno != ETIMEDOUT)
{
return -errno;
}
return received ? 0 : -EAGAIN;
}
if (msg.msg_id == AUDIO_MSG_DEQUEUE)
{
buffer = msg.u.ptr;
buffer->curbyte = 0;
dq_addlast(&buffer->dq_entry, &compress->bufferq);
}
else
{
if (compress->on_event)
{
compress->on_event(compress->cookie, msg.msg_id, msg.u.ptr);
}
}
received = true;
nonblocking = true;
}
}
static int compress_hw_peek_buffer(FAR struct compress_hw_data *compress,
FAR struct ap_buffer_s **buffer)
{
int ret;
*buffer = (FAR struct ap_buffer_s *)dq_peek(&compress->bufferq);
if (*buffer)
{
return 0;
}
ret = compress_hw_poll_available(compress);
if (ret < 0)
{
return ret;
}
*buffer = (FAR struct ap_buffer_s *)dq_peek(&compress->bufferq);
if (!*buffer)
{
return -EAGAIN;
}
return 0;
}
static int compress_hw_enqueue_buffer(FAR struct compress_hw_data *compress,
FAR struct ap_buffer_s *buffer,
bool final)
{
struct audio_buf_desc_s desc;
int ret;
if (final)
{
buffer->flags |= AUDIO_APB_FINAL;
audinfo("compress %s enqueue apb final.\n", compress->name);
}
else
{
buffer->flags &= ~AUDIO_APB_FINAL;
}
memset(&desc, 0, sizeof(desc));
buffer->nbytes = buffer->curbyte;
buffer->curbyte = 0;
desc.u.buffer = buffer;
ret = ioctl(compress->fd, AUDIOIOC_ENQUEUEBUFFER, &desc);
if (ret < 0)
{
return -errno;
}
return 0;
}
static int compress_hw_get_current_config(FAR void *compress_data,
FAR struct compr_config *config)
{
FAR struct compress_hw_data *compress = compress_data;
struct ap_buffer_info_s buf_info;
int ret;
if (!config)
{
return -EINVAL;
}
ret = ioctl(compress->fd, AUDIOIOC_GETBUFFERINFO, &buf_info);
if (ret < 0)
{
return -errno;
}
config->fragments = buf_info.nbuffers;
config->fragment_size = buf_info.buffer_size;
return compress_hw_get_codec_params(compress, config->codec);
}
static void *compress_hw_open_by_name(FAR const char *name,
unsigned int flags,
FAR struct compr_config *config)
{
FAR struct compress_hw_data *compress;
int ret;
if (!((flags & COMPRESS_OUT) || (flags & COMPRESS_IN)))
{
return NULL;
}
compress = calloc(1, sizeof(struct compress_hw_data));
if (!compress)
{
return NULL;
}
compress->max_poll_wait_ms = DEFAULT_MAX_POLL_WAIT_MS;
strlcpy(compress->name, strrchr(name, '/'), sizeof(compress->name));
compress->flags = flags;
ret = compress_hw_init(compress, name);
if (ret < 0)
{
goto fail;
}
if (config)
{
ret = compress_hw_set_current_config(compress, config);
if (ret < 0)
{
compress_hw_deinit(compress);
goto fail;
}
}
return compress;
fail:
free(compress);
return NULL;
}
static int compress_hw_is_compress_running(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
struct audio_status_s status;
int ret;
ret = ioctl(compress->fd, AUDIOIOC_GETSTATUS, &status);
if (ret < 0)
{
return -errno;
}
return status.state == AUDIO_STATE_RUNNING;
}
static int compress_hw_stop(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
struct audio_buf_desc_s buf_desc;
struct audio_status_s status;
if (ioctl(compress->fd, AUDIOIOC_GETSTATUS, &status) >= 0 &&
(status.state == AUDIO_STATE_RUNNING ||
status.state == AUDIO_STATE_PAUSED))
{
if (ioctl(compress->fd, AUDIOIOC_STOP, compress->session) < 0)
{
auderr("cannot stop the stream");
return -errno;
}
}
compress->nonblocking = false;
while (dq_count(&compress->bufferq) < compress->config.fragments)
{
compress_hw_poll_available(compress);
}
while (!dq_empty(&compress->bufferq))
{
buf_desc.u.buffer =
(FAR struct ap_buffer_s *)dq_remfirst(&compress->bufferq);
ioctl(compress->fd, AUDIOIOC_FREEBUFFER, &buf_desc);
}
return 0;
}
static void compress_hw_close(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
compress_hw_stop(compress);
compress_hw_deinit(compress);
free(compress);
}
static int compress_hw_is_compress_ready(FAR void *compress_data)
{
return 1;
}
static int compress_hw_get_hpointer(FAR void *compress_data,
FAR unsigned int *avail,
struct timespec *tstamp)
{
FAR struct compress_hw_data *compress = compress_data;
FAR struct ap_buffer_s *buffer;
FAR struct dq_entry_s *cur;
long pos;
*avail = 0;
for (cur = dq_peek(&compress->bufferq); cur; cur = dq_next(cur))
{
buffer = (FAR struct ap_buffer_s *)cur;
avail += buffer->nbytes - buffer->curbyte;
}
if (ioctl(compress->fd, AUDIOIOC_GETPOSITION, &pos))
{
auderr("cannot get position");
return -errno;
}
tstamp->tv_sec = pos / 1000;
tstamp->tv_nsec = (pos % 1000) * 1000000;
return 0;
}
static int compress_hw_get_tstamp(FAR void *compress_data,
FAR unsigned int *samples,
FAR unsigned int *sampling_rate)
{
return -ENOTSUP;
}
static int compress_hw_write(FAR void *compress_data, FAR const void *buf,
size_t size)
{
FAR struct compress_hw_data *compress = compress_data;
FAR struct ap_buffer_s *buffer;
size_t left = size;
int ret = 0;
while (left > 0)
{
size_t len;
ret = compress_hw_peek_buffer(compress, &buffer);
if (ret < 0)
{
break;
}
len = MIN(buffer->nmaxbytes - buffer->curbyte, left);
memcpy(buffer->samp + buffer->curbyte, buf, len);
buffer->curbyte += len;
if (buffer->curbyte == buffer->nmaxbytes)
{
dq_remfirst(&compress->bufferq);
ret = compress_hw_enqueue_buffer(compress, buffer, false);
if (ret < 0)
{
break;
}
}
buf = (char *)buf + len;
left -= len;
}
return left != size ? size - left : ret;
}
static int compress_hw_read(FAR void *compress_data, FAR void *buf,
size_t size)
{
FAR struct compress_hw_data *compress = compress_data;
FAR struct ap_buffer_s *buffer;
size_t left = size;
int ret = 0;
while (left > 0)
{
size_t len;
ret = compress_hw_peek_buffer(compress, &buffer);
if (ret < 0)
{
break;
}
len = MIN(buffer->nbytes - buffer->curbyte, left);
memcpy(buf, buffer->samp + buffer->curbyte, len);
buffer->curbyte += len;
if (buffer->curbyte == buffer->nbytes)
{
dq_remfirst(&compress->bufferq);
buffer->curbyte = buffer->nmaxbytes;
ret = compress_hw_enqueue_buffer(compress, buffer, false);
if (ret < 0)
{
ret = -errno;
break;
}
}
buf = (FAR char *)buf + len;
left -= len;
}
return left != size ? size - left : ret;
}
static int compress_hw_start(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
FAR struct ap_buffer_s *buffer;
int ret;
if (compress->flags & COMPRESS_OUT)
{
while (dq_count(&compress->bufferq))
{
buffer = (FAR struct ap_buffer_s *)dq_remfirst(&compress->bufferq);
buffer->curbyte = buffer->nmaxbytes;
ret = compress_hw_enqueue_buffer(compress, buffer, false);
if (ret < 0)
{
return ret;
}
}
}
if (ioctl(compress->fd, AUDIOIOC_START, compress->session))
{
auderr("cannot start the stream");
return -errno;
}
return 0;
}
static int compress_hw_pause(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
if (ioctl(compress->fd, AUDIOIOC_PAUSE, compress->session) < 0)
{
auderr("cannot pause the stream");
return -errno;
}
return 0;
}
static int compress_hw_resume(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
if (ioctl(compress->fd, AUDIOIOC_RESUME, compress->session) < 0)
{
auderr("cannot resume the stream");
return -errno;
}
return 0;
}
static int compress_hw_flush(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
if (ioctl(compress->fd, AUDIOIOC_FLUSH, compress->session) < 0)
{
auderr("cannot flush the stream");
return -errno;
}
return 0;
}
static int compress_hw_drain(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
FAR struct ap_buffer_s *buffer;
struct audio_status_s status;
int ret;
ret = ioctl(compress->fd, AUDIOIOC_GETSTATUS, &status);
if (ret < 0)
{
return -errno;
}
if (status.state != AUDIO_STATE_RUNNING)
{
auderr("device not started");
return -ENODEV;
}
if ((compress->flags & COMPRESS_IN))
{
buffer = (FAR struct ap_buffer_s *)dq_peek(&compress->bufferq);
if (buffer && buffer->curbyte)
{
dq_remfirst(&compress->bufferq);
compress_hw_enqueue_buffer(compress, buffer, true);
}
}
while (dq_count(&compress->bufferq) < compress->config.fragments)
{
ret = compress_hw_poll_available(compress);
if (ret < 0)
{
return ret;
}
}
return 0;
}
static int compress_hw_reset(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
if (ioctl(compress->fd, AUDIOIOC_HWRESET, compress->session) < 0)
{
auderr("cannot reset the stream");
return -errno;
}
return 0;
}
static int compress_hw_partial_drain(FAR void *compress_data)
{
return -ENOTSUP;
}
static int compress_hw_next_track(FAR void *compress_data)
{
return -ENOTSUP;
}
static int
compress_hw_set_gapless_metadata(FAR void *compress_data,
FAR struct compr_gapless_mdata *mdata)
{
return -ENOTSUP;
}
static void compress_hw_set_max_poll_wait(FAR void *compress_data,
int milliseconds)
{
FAR struct compress_hw_data *compress = compress_data;
compress->max_poll_wait_ms = milliseconds;
}
static void compress_hw_set_nonblock(FAR void *compress_data, int nonblock)
{
FAR struct compress_hw_data *compress = compress_data;
compress->nonblocking = !!nonblock;
}
static int compress_hw_wait(FAR void *compress_data, int timeout_ms)
{
FAR struct compress_hw_data *compress = compress_data;
struct pollfd fds;
int ret;
if (!dq_empty(&compress->bufferq))
{
return 0;
}
memset(&fds, 0, sizeof(fds));
fds.fd = compress->fd;
fds.events = POLLOUT | POLLIN;
ret = poll(&fds, 1, timeout_ms);
if (ret > 0)
{
if (fds.revents & POLLERR)
{
auderr("poll returned error!");
return -EIO;
}
if (fds.revents & (POLLOUT | POLLIN))
{
return 0;
}
}
return ret < 0 ? -errno : -ETIME;
}
static bool compress_hw_is_codec_supported_by_name(
FAR const char *name, unsigned int flags, FAR struct snd_codec *codec)
{
int ret;
int fd;
fd = open(name, O_RDWR | O_CLOEXEC);
if (fd < 0)
{
return -errno;
}
ret = compress_hw_is_codec_type_supported(fd, codec);
close(fd);
return ret;
}
static FAR const char *compress_hw_get_error(FAR void *compress_data)
{
return strerror(errno);
}
static int compress_hw_set_volume(FAR void *compress_data, float volume)
{
FAR struct compress_hw_data *compress = compress_data;
struct audio_caps_desc_s caps_desc;
int ret;
memset(&caps_desc, 0, sizeof(caps_desc));
caps_desc.caps.ac_len = sizeof(struct audio_caps_s);
caps_desc.caps.ac_type = AUDIO_TYPE_FEATURE;
caps_desc.caps.ac_format.hw = AUDIO_FU_VOLUME;
caps_desc.caps.ac_controls.hw[0] = volume * 1000;
ret = ioctl(compress->fd, AUDIOIOC_CONFIGURE, &caps_desc);
if (ret < 0)
{
return -errno;
}
return 0;
}
static int compress_hw_set_params(FAR void *compress_data,
FAR const char *params)
{
FAR struct compress_hw_data *compress = compress_data;
int ret;
ret = ioctl(compress->fd, AUDIOIOC_SETPARAMTER, params);
if (ret < 0)
{
return -errno;
}
return 0;
}
static int compress_hw_set_event_callback(FAR void *compress_data,
compress_event_t on_event,
FAR void *cookie)
{
FAR struct compress_hw_data *compress = compress_data;
compress->cookie = cookie;
compress->on_event = on_event;
return 0;
}
static int compress_hw_get_file_descriptor(FAR void *compress_data)
{
FAR struct compress_hw_data *compress = compress_data;
return compress->mq;
}
const struct compress_ops g_compress_hw_ops =
{
.open_by_name = compress_hw_open_by_name,
.close = compress_hw_close,
.get_hpointer = compress_hw_get_hpointer,
.get_tstamp = compress_hw_get_tstamp,
.write = compress_hw_write,
.read = compress_hw_read,
.start = compress_hw_start,
.stop = compress_hw_stop,
.pause = compress_hw_pause,
.resume = compress_hw_resume,
.drain = compress_hw_drain,
.reset = compress_hw_reset,
.partial_drain = compress_hw_partial_drain,
.next_track = compress_hw_next_track,
.set_gapless_metadata = compress_hw_set_gapless_metadata,
.set_max_poll_wait = compress_hw_set_max_poll_wait,
.set_nonblock = compress_hw_set_nonblock,
.wait = compress_hw_wait,
.is_codec_supported_by_name = compress_hw_is_codec_supported_by_name,
.is_compress_running = compress_hw_is_compress_running,
.is_compress_ready = compress_hw_is_compress_ready,
.get_error = compress_hw_get_error,
.set_codec_params = compress_hw_set_codec_params,
.get_codec_params = compress_hw_get_codec_params,
.set_volume = compress_hw_set_volume,
.set_params = compress_hw_set_params,
.set_event_callback = compress_hw_set_event_callback,
.set_current_config = compress_hw_set_current_config,
.get_current_config = compress_hw_get_current_config,
.get_file_descriptor = compress_hw_get_file_descriptor,
.poll_available = compress_hw_poll_available,
.flush = compress_hw_flush,
};