diff options
Diffstat (limited to 'Radio/HW/BladeRF/src/streaming/async.c')
-rw-r--r-- | Radio/HW/BladeRF/src/streaming/async.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/streaming/async.c b/Radio/HW/BladeRF/src/streaming/async.c new file mode 100644 index 0000000..cffa9d2 --- /dev/null +++ b/Radio/HW/BladeRF/src/streaming/async.c @@ -0,0 +1,294 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2014 Nuand LLC + * + * 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 library 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 + */ + +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> + +#include "log.h" + +#include "backend/usb/usb.h" + +#include "async.h" +#include "board/board.h" +#include "helpers/timeout.h" +#include "helpers/have_cap.h" + +int async_init_stream(struct bladerf_stream **stream, + struct bladerf *dev, + bladerf_stream_cb callback, + void ***buffers, + size_t num_buffers, + bladerf_format format, + size_t samples_per_buffer, + size_t num_transfers, + void *user_data) +{ + struct bladerf_stream *lstream; + size_t buffer_size_bytes; + size_t i; + int status = 0; + + if (num_transfers > num_buffers) { + log_error("num_transfers must be <= num_buffers\n"); + return BLADERF_ERR_INVAL; + } + + if (samples_per_buffer < 1024 || samples_per_buffer % 1024 != 0) { + log_error("samples_per_buffer must be multiples of 1024\n"); + return BLADERF_ERR_INVAL; + } + + /* Create a stream and populate it with the appropriate information */ + lstream = malloc(sizeof(struct bladerf_stream)); + + if (!lstream) { + return BLADERF_ERR_MEM; + } + + MUTEX_INIT(&lstream->lock); + + if (pthread_cond_init(&lstream->can_submit_buffer, NULL) != 0) { + free(lstream); + return BLADERF_ERR_UNEXPECTED; + } + + if (pthread_cond_init(&lstream->stream_started, NULL) != 0) { + free(lstream); + return BLADERF_ERR_UNEXPECTED; + } + + lstream->dev = dev; + lstream->error_code = 0; + lstream->state = STREAM_IDLE; + lstream->samples_per_buffer = samples_per_buffer; + lstream->num_buffers = num_buffers; + lstream->format = format; + lstream->transfer_timeout = BULK_TIMEOUT_MS; + lstream->cb = callback; + lstream->user_data = user_data; + lstream->buffers = NULL; + + if (format == BLADERF_FORMAT_PACKET_META) { + if (!have_cap_dev(dev, BLADERF_CAP_FW_SHORT_PACKET)) { + log_error("Firmware does not support short packets. " + "Upgrade to at least firmware version 2.4.0."); + return BLADERF_ERR_UNSUPPORTED; + } + + if (!have_cap_dev(dev, BLADERF_CAP_FPGA_PACKET_META)) { + log_error("FPGA does not support packet meta format. " + "Upgrade to at least FPGA version 0.12.0 ."); + return BLADERF_ERR_UNSUPPORTED; + } + } + + if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) { + if (!have_cap_dev(dev, BLADERF_CAP_FPGA_8BIT_SAMPLES)) { + log_error("FPGA does not support 8bit mode. " + "Upgrade to at least FPGA version 0.15.0.\n"); + return BLADERF_ERR_UNSUPPORTED; + } + } + + switch(format) { + case BLADERF_FORMAT_SC8_Q7: + case BLADERF_FORMAT_SC8_Q7_META: + buffer_size_bytes = sc8q7_to_bytes(samples_per_buffer); + break; + + case BLADERF_FORMAT_SC16_Q11: + case BLADERF_FORMAT_SC16_Q11_META: + buffer_size_bytes = sc16q11_to_bytes(samples_per_buffer); + break; + + case BLADERF_FORMAT_PACKET_META: + buffer_size_bytes = samples_per_buffer; + break; + + default: + status = BLADERF_ERR_INVAL; + break; + } + + if (!status) { + lstream->buffers = calloc(num_buffers, sizeof(lstream->buffers[0])); + if (lstream->buffers) { + for (i = 0; i < num_buffers && !status; i++) { + lstream->buffers[i] = calloc(1, buffer_size_bytes); + if (!lstream->buffers[i]) { + status = BLADERF_ERR_MEM; + } + } + } else { + status = BLADERF_ERR_MEM; + } + } + + /* Clean up everything we've allocated if we hit any errors */ + if (status) { + + if (lstream->buffers) { + for (i = 0; i < num_buffers; i++) { + free(lstream->buffers[i]); + } + + free(lstream->buffers); + } + + free(lstream); + } else { + /* Perform any backend-specific stream initialization */ + status = dev->backend->init_stream(lstream, num_transfers); + + if (status < 0) { + async_deinit_stream(lstream); + *stream = NULL; + } else { + /* Update the caller's pointers */ + *stream = lstream; + + if (buffers) { + *buffers = lstream->buffers; + } + } + } + + return status; +} + +int async_set_transfer_timeout(struct bladerf_stream *stream, + unsigned int transfer_timeout_ms) +{ + MUTEX_LOCK(&stream->lock); + stream->transfer_timeout = transfer_timeout_ms; + MUTEX_UNLOCK(&stream->lock); + + return 0; +} + +int async_get_transfer_timeout(struct bladerf_stream *stream, + unsigned int *transfer_timeout_ms) +{ + MUTEX_LOCK(&stream->lock); + *transfer_timeout_ms = stream->transfer_timeout; + MUTEX_UNLOCK(&stream->lock); + + return 0; +} + +int async_run_stream(struct bladerf_stream *stream, bladerf_channel_layout layout) +{ + int status; + struct bladerf *dev = stream->dev; + + MUTEX_LOCK(&stream->lock); + stream->layout = layout; + stream->state = STREAM_RUNNING; + pthread_cond_signal(&stream->stream_started); + MUTEX_UNLOCK(&stream->lock); + + status = dev->backend->stream(stream, layout); + + /* Backend return value takes precedence over stream error status */ + return status == 0 ? stream->error_code : status; +} + +int async_submit_stream_buffer(struct bladerf_stream *stream, + void *buffer, size_t *length, + unsigned int timeout_ms, + bool nonblock) +{ + int status = 0; + struct timespec timeout_abs; + + MUTEX_LOCK(&stream->lock); + + if (buffer != BLADERF_STREAM_SHUTDOWN) { + if (stream->state != STREAM_RUNNING && timeout_ms != 0) { + status = populate_abs_timeout(&timeout_abs, timeout_ms); + if (status != 0) { + log_debug("Failed to populate timeout value\n"); + goto error; + } + } + + while (stream->state != STREAM_RUNNING) { + log_debug("Buffer submitted while stream's not running. " + "Waiting for stream to start.\n"); + + if (timeout_ms == 0) { + status = pthread_cond_wait(&stream->stream_started, + &stream->lock); + } else { + status = pthread_cond_timedwait(&stream->stream_started, + &stream->lock, &timeout_abs); + } + + if (status == ETIMEDOUT) { + status = BLADERF_ERR_TIMEOUT; + log_debug("%s: %u ms timeout expired", + __FUNCTION__, timeout_ms); + goto error; + } else if (status != 0) { + status = BLADERF_ERR_UNEXPECTED; + goto error; + } + } + } + + status = stream->dev->backend->submit_stream_buffer(stream, buffer, + length, timeout_ms, nonblock); + +error: + MUTEX_UNLOCK(&stream->lock); + return status; +} + +void async_deinit_stream(struct bladerf_stream *stream) +{ + size_t i; + + if (!stream) { + log_debug("%s called with NULL stream\n", __FUNCTION__); + return; + } + + while(stream->state != STREAM_DONE && stream->state != STREAM_IDLE) { + log_verbose( "Stream not done...\n" ); + usleep(1000000); + } + + /* Free up the backend data */ + stream->dev->backend->deinit_stream(stream); + + /* Free up the buffers */ + for (i = 0; i < stream->num_buffers; i++) { + free(stream->buffers[i]); + } + + /* Free up the pointer to the buffers */ + free(stream->buffers); + + /* Free up the stream itself */ + free(stream); +} + |