blob: d8d9d8391f83dad46e9dae124db57061be83e05c [file] [log] [blame]
/*
* 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.
*/
#include <assert.h>
#include <os/os_eventq.h>
#include <os/os_sem.h>
#include <i2s/i2s.h>
#include <i2s/i2s_driver.h>
/* Function called from i2s_open/os_dev_open */
static int
i2s_open_handler(struct os_dev *dev, uint32_t timout, void *arg)
{
struct i2s *i2s;
struct i2s_client *client = (struct i2s_client *)arg;
struct i2s_sample_buffer *buffer;
int rc;
if (dev->od_flags & OS_DEV_F_STATUS_OPEN) {
return OS_EBUSY;
}
i2s = (struct i2s *)dev;
assert(client == NULL ||
(client->sample_buffer_ready_cb != NULL &&
client->state_changed_cb != NULL));
i2s->client = client;
if (client && client->sample_rate) {
i2s->sample_rate = client->sample_rate;
}
if (i2s->direction == I2S_IN) {
while (NULL != (buffer = i2s_buffer_get(i2s, 0))) {
i2s_buffer_put(i2s, buffer);
}
} else {
rc = i2s_start(i2s);
/*
* No buffers on opening output I2S stream is to be expected
* and is not error preventing device usage.
*/
if (rc != OS_OK && rc != I2S_ERR_NO_BUFFER) {
return rc;
}
}
return OS_OK;
}
/* Function called from i2s_close/os_dev_close */
static int
i2s_close_handler(struct os_dev *dev)
{
struct i2s *i2s;
i2s = (struct i2s *)dev;
i2s_stop(i2s);
i2s->client = NULL;
return OS_OK;
}
static int
i2s_suspend_handler(struct os_dev *dev, os_time_t timeout, int arg)
{
return i2s_driver_suspend((struct i2s *)dev, timeout, arg);
}
static int
i2s_resume_handler(struct os_dev *dev)
{
return i2s_driver_resume((struct i2s *)dev);
}
static void
i2s_add_to_user_queue(struct i2s *i2s, struct i2s_sample_buffer *buffer)
{
STAILQ_INSERT_TAIL(&i2s->user_queue, buffer, next_buffer);
os_sem_release(&i2s->user_queue_buffer_count);
}
static void
i2s_add_to_driver_queue(struct i2s *i2s, struct i2s_sample_buffer *buffer)
{
STAILQ_INSERT_TAIL(&i2s->driver_queue, buffer, next_buffer);
if (i2s->state != I2S_STATE_STOPPED) {
i2s_driver_buffer_queued(i2s);
}
}
static void
i2s_buffers_from_pool(struct i2s *i2s, struct i2s_buffer_pool *pool)
{
int i;
int j;
int sr;
struct i2s_sample_buffer *buffers;
uintptr_t sample_data;
uint32_t samples_per_buffer;
if (i2s->direction != I2S_IN && pool != NULL) {
os_sem_init(&i2s->user_queue_buffer_count, pool->buffer_count);
} else {
os_sem_init(&i2s->user_queue_buffer_count, 0);
}
i2s->buffer_pool = pool;
if (pool == NULL) {
return;
}
buffers = (struct i2s_sample_buffer *)pool->buffers;
samples_per_buffer = pool->buffer_size / i2s->sample_size_in_bytes;
sample_data = (uintptr_t)&buffers[pool->buffer_count];
for (i = 0; i < pool->buffer_count; ++i) {
for (j = 0; j < I2S_BUFFER_GUARD; ++j, ++sample_data) {
*(uint8_t *)sample_data = MYNEWT_VAL(I2S_BUFFER_GUARD_PATTERN);
}
buffers[i].capacity = samples_per_buffer;
buffers[i].sample_data = (void *)sample_data;
buffers[i].sample_count = 0;
sample_data += pool->buffer_size;
for (j = 0; j < I2S_BUFFER_GUARD; ++j, ++sample_data) {
*(uint8_t *)sample_data = MYNEWT_VAL(I2S_BUFFER_GUARD_PATTERN);
}
OS_ENTER_CRITICAL(sr);
if (i2s->direction == I2S_IN) {
i2s_add_to_driver_queue(i2s, &buffers[i]);
} else {
STAILQ_INSERT_TAIL(&i2s->user_queue, &buffers[i], next_buffer);
}
OS_EXIT_CRITICAL(sr);
}
}
int
i2s_init(struct i2s *i2s, struct i2s_buffer_pool *pool)
{
STAILQ_INIT(&i2s->driver_queue);
STAILQ_INIT(&i2s->user_queue);
i2s->state = I2S_STATE_STOPPED;
i2s_buffers_from_pool(i2s, pool);
i2s->dev.od_handlers.od_open = i2s_open_handler;
i2s->dev.od_handlers.od_close = i2s_close_handler;
i2s->dev.od_handlers.od_suspend = i2s_suspend_handler;
i2s->dev.od_handlers.od_resume = i2s_resume_handler;
return 0;
}
struct i2s *
i2s_open(const char *name, uint32_t timeout, struct i2s_client *client)
{
return (struct i2s *)os_dev_open(name, timeout, client);
}
int
i2s_close(struct i2s *i2s)
{
return os_dev_close(&i2s->dev);
}
int
i2s_write(struct i2s *i2s, void *samples, uint32_t sample_buffer_size)
{
size_t sample_count;
struct i2s_sample_buffer *buffer;
assert(i2s->direction == I2S_OUT);
buffer = i2s_buffer_get(i2s, OS_WAIT_FOREVER);
if (buffer == NULL) {
return -1;
}
/* Calculate buffer size */
sample_count = sample_buffer_size / i2s->sample_size_in_bytes;
if (buffer->capacity < sample_count) {
sample_count = buffer->capacity;
}
sample_buffer_size = sample_count * i2s->sample_size_in_bytes;
/* Move data to buffer */
memcpy(buffer->sample_data, samples, sample_buffer_size);
buffer->sample_count = sample_count;
/* Pass buffer to output device */
i2s_buffer_put(i2s, buffer);
return sample_buffer_size;
}
int
i2s_read(struct i2s *i2s, void *samples, uint32_t sample_buffer_size)
{
int sr;
uint32_t sample_pair_size = (i2s->sample_size_in_bytes * 2);
size_t sample_capacity = sample_buffer_size / sample_pair_size;
struct i2s_sample_buffer *buffer;
assert(i2s->direction == I2S_IN);
if (i2s->state == I2S_STATE_STOPPED) {
i2s_start(i2s);
}
buffer = i2s_buffer_get(i2s, OS_WAIT_FOREVER);
if (buffer == NULL) {
return 0;
}
if (sample_capacity > buffer->sample_count) {
sample_capacity = buffer->sample_count;
}
sample_buffer_size = sample_capacity * sample_pair_size;
memcpy(samples, buffer->sample_data, sample_buffer_size);
if (sample_capacity < buffer->sample_count) {
/* Not all data consumed, modify buffer and put buffer at the front again */
memmove(buffer->sample_data, buffer->sample_data,
(buffer->sample_count - sample_capacity) * sample_pair_size);
OS_ENTER_CRITICAL(sr);
STAILQ_INSERT_HEAD(&i2s->user_queue, buffer, next_buffer);
OS_EXIT_CRITICAL(sr);
}
return sample_buffer_size;
}
int
i2s_start(struct i2s *i2s)
{
int rc = I2S_OK;
if (i2s->state != I2S_STATE_RUNNING) {
if (STAILQ_EMPTY(&i2s->driver_queue)) {
i2s->state = I2S_STATE_OUT_OF_BUFFERS;
rc = I2S_ERR_NO_BUFFER;
} else {
rc = i2s_driver_start(i2s);
if (rc == I2S_OK) {
i2s->state = I2S_STATE_RUNNING;
if (i2s->client) {
i2s->client->state_changed_cb(i2s, I2S_STATE_RUNNING);
}
}
}
}
return rc;
}
int
i2s_stop(struct i2s *i2s)
{
struct i2s_sample_buffer *buffer;
i2s_driver_stop(i2s);
i2s->state = I2S_STATE_STOPPED;
if (i2s->client) {
i2s->client->state_changed_cb(i2s, i2s->state);
}
if (i2s->direction == I2S_IN) {
while (NULL != (buffer = i2s_buffer_get(i2s, 0))) {
i2s_add_to_driver_queue(i2s, buffer);
}
} else {
while (!STAILQ_EMPTY(&i2s->driver_queue)) {
buffer = STAILQ_FIRST(&i2s->driver_queue);
STAILQ_REMOVE_HEAD(&i2s->driver_queue, next_buffer);
i2s_add_to_user_queue(i2s, buffer);
}
}
return 0;
}
int
i2s_available_buffers(struct i2s *i2s)
{
return os_sem_get_count(&i2s->user_queue_buffer_count);
}
struct i2s_sample_buffer *
i2s_buffer_get(struct i2s *i2s, os_time_t timeout)
{
int sr;
struct i2s_sample_buffer *buffer = NULL;
if (OS_OK == os_sem_pend(&i2s->user_queue_buffer_count, timeout)) {
OS_ENTER_CRITICAL(sr);
buffer = STAILQ_FIRST(&i2s->user_queue);
assert(buffer);
STAILQ_REMOVE_HEAD(&i2s->user_queue, next_buffer);
OS_EXIT_CRITICAL(sr);
assert(buffer->capacity > 0);
}
return buffer;
}
int
i2s_buffer_put(struct i2s *i2s, struct i2s_sample_buffer *buffer)
{
int sr;
int rc = I2S_OK;
int i;
const uint8_t *pre;
const uint8_t *post;
if (I2S_BUFFER_GUARD) {
pre = (const uint8_t *)(buffer->sample_data);
post = (const uint8_t *)(buffer->sample_data) + buffer->capacity * i2s->sample_size_in_bytes;
for (i = 0; i < I2S_BUFFER_GUARD; ++i) {
/*
* Assert here means that a memory was corrupted in some way,
* no indication that the buffer was overwritten due to buffer size mismatch,
* but rather some other memory manipulation went amiss.
*/
assert(*--pre == MYNEWT_VAL(I2S_BUFFER_GUARD_PATTERN));
/* Assert here usually means that more data was written to the buffer then it's size. */
assert(*post++ == MYNEWT_VAL(I2S_BUFFER_GUARD_PATTERN));
}
}
/*
* Output sample buffer without samples?
* Don't bother driver, just put in client queue.
*/
if (i2s->direction == I2S_OUT && buffer->sample_count == 0) {
i2s_driver_buffer_put(i2s, buffer);
} else {
OS_ENTER_CRITICAL(sr);
i2s_add_to_driver_queue(i2s, buffer);
OS_EXIT_CRITICAL(sr);
if (i2s->state == I2S_STATE_OUT_OF_BUFFERS) {
rc = i2s_driver_start(i2s);
}
}
return rc;
}
struct i2s_sample_buffer *
i2s_driver_buffer_get(struct i2s *i2s)
{
int sr;
struct i2s_sample_buffer *buffer;
OS_ENTER_CRITICAL(sr);
buffer = STAILQ_FIRST(&i2s->driver_queue);
if (buffer) {
STAILQ_REMOVE_HEAD(&i2s->driver_queue, next_buffer);
}
OS_EXIT_CRITICAL(sr);
return buffer;
}
void
i2s_driver_buffer_put(struct i2s *i2s, struct i2s_sample_buffer *buffer)
{
int sr;
assert(buffer != NULL && i2s != NULL);
if (i2s->client) {
/* If callback returns 1, buffer is not added to the pool */
if (i2s->client->sample_buffer_ready_cb(i2s, buffer) == 1) {
return;
}
}
OS_ENTER_CRITICAL(sr);
i2s_add_to_user_queue(i2s, buffer);
OS_EXIT_CRITICAL(sr);
}
void
i2s_driver_state_changed(struct i2s *i2s, enum i2s_state state)
{
if (i2s->state != state) {
i2s->state = state;
if (i2s->client) {
i2s->client->state_changed_cb(i2s, state);
}
}
}