static void *rx_callback(struct bladerf *dev, struct bladerf_stream *stream, struct bladerf_metadata *meta, void *samples, size_t num_samples, void *user_data) { unsigned int requests; /* Pending requests */ unsigned int next_idx; unsigned int samples_idx; void *next_buf = NULL; /* Next buffer to submit for reception */ struct bladerf_sync *s = (struct bladerf_sync *)user_data; struct sync_worker *w = s->worker; struct buffer_mgmt *b = &s->buf_mgmt; /* Check if the caller has requested us to shut down. We'll keep the * SHUTDOWN bit set through our transition into the IDLE state so we * can act on it there. */ pthread_mutex_lock(&w->request_lock); requests = w->requests; pthread_mutex_unlock(&w->request_lock); if (requests & SYNC_WORKER_STOP) { log_verbose("%s worker: Got STOP request upon entering callback. " "Ending stream.\n", MODULE_STR(s)); return NULL; } pthread_mutex_lock(&b->lock); /* Get the index of the buffer we've been notified about having been * completed */ samples_idx = sync_buf2idx(b, samples); if (b->status[b->prod_i] == SYNC_BUFFER_EMPTY) { next_idx = b->prod_i; b->prod_i = (next_idx + 1) % b->num_buffers; b->status[samples_idx] = SYNC_BUFFER_FULL; b->status[next_idx] = SYNC_BUFFER_IN_FLIGHT; pthread_cond_signal(&b->buf_ready); } else { /* TODO propgate back the RX Overrun to the sync_rx() caller */ log_debug("RX overrun @ buffer %u\r\n", samples_idx); next_idx = samples_idx; b->status[samples_idx] = SYNC_BUFFER_IN_FLIGHT; } next_buf = b->buffers[next_idx]; log_verbose("%s worker: buf[%u] = full, buf[%u] = in_flight\n", MODULE_STR(s), samples_idx, next_idx); pthread_mutex_unlock(&b->lock); return next_buf; }
void *sync_worker_task(void *arg) { sync_worker_state state = SYNC_WORKER_STATE_IDLE; struct bladerf_sync *s = (struct bladerf_sync *)arg; log_verbose("%s worker: task started\n", MODULE_STR(s)); set_state(s->worker, state); log_verbose("%s worker: task state set\n", MODULE_STR(s)); while (state != SYNC_WORKER_STATE_STOPPED) { switch (state) { case SYNC_WORKER_STATE_STARTUP: assert(!"Worker in unexepected state, shutting down. (STARTUP)"); set_state(s->worker, SYNC_WORKER_STATE_SHUTTING_DOWN); break; case SYNC_WORKER_STATE_IDLE: state = exec_idle_state(s); set_state(s->worker, state); break; case SYNC_WORKER_STATE_RUNNING: exec_running_state(s); state = SYNC_WORKER_STATE_IDLE; set_state(s->worker, state); break; case SYNC_WORKER_STATE_SHUTTING_DOWN: log_verbose("%s worker: Shutting down...\n", MODULE_STR(s)); state = SYNC_WORKER_STATE_STOPPED; set_state(s->worker, state); break; case SYNC_WORKER_STATE_STOPPED: assert(!"Worker in unexepected state: STOPPED"); break; default: assert(!"Worker in unexepected state, shutting down. (UNKNOWN)"); set_state(s->worker, SYNC_WORKER_STATE_SHUTTING_DOWN); break; } } return NULL; }
static sync_worker_state exec_idle_state(struct bladerf_sync *s) { sync_worker_state next_state = SYNC_WORKER_STATE_IDLE; pthread_mutex_lock(&s->worker->request_lock); while (s->worker->requests == 0) { log_verbose("%s worker: Waiting for pending requests\n", MODULE_STR(s)); pthread_cond_wait(&s->worker->requests_pending, &s->worker->request_lock); } if (s->worker->requests & SYNC_WORKER_STOP) { log_verbose("%s worker: Got request to stop\n", module2str(s->stream_config.module)); next_state = SYNC_WORKER_STATE_SHUTTING_DOWN; } else if (s->worker->requests & SYNC_WORKER_START) { log_verbose("%s worker: Got request to start\n", module2str(s->stream_config.module)); next_state = SYNC_WORKER_STATE_RUNNING; } else { log_warning("Invalid request value encountered: 0x%08X\n", s->worker->requests); } s->worker->requests = 0; pthread_mutex_unlock(&s->worker->request_lock); return next_state; }
static void *tx_callback(struct bladerf *dev, struct bladerf_stream *stream, struct bladerf_metadata *meta, void *samples, size_t num_samples, void *user_data) { unsigned int requests; /* Pending requests */ unsigned int completed_idx; /* Index of completed buffer */ struct bladerf_sync *s = (struct bladerf_sync *)user_data; struct sync_worker *w = s->worker; struct buffer_mgmt *b = &s->buf_mgmt; /* Check if the caller has requested us to shut down. We'll keep the * SHUTDOWN bit set through our transition into the IDLE state so we * can act on it there. */ pthread_mutex_lock(&w->request_lock); requests = w->requests; pthread_mutex_unlock(&w->request_lock); if (requests & SYNC_WORKER_STOP) { log_verbose("%s worker: Got STOP request upon entering callback. " "Ending stream.\r\n", MODULE_STR(s)); return NULL; } /* Mark the last transfer as being completed. Note that the first * callbacks we get have samples=NULL */ if (samples != NULL) { pthread_mutex_lock(&b->lock); completed_idx = sync_buf2idx(b, samples); assert(b->status[completed_idx] == SYNC_BUFFER_IN_FLIGHT); b->status[completed_idx] = SYNC_BUFFER_EMPTY; pthread_cond_signal(&b->buf_ready); pthread_mutex_unlock(&b->lock); log_verbose("%s worker: Buffer %u emptied.\r\n", MODULE_STR(s), completed_idx); } return BLADERF_STREAM_NO_DATA; }
static void exec_running_state(struct bladerf_sync *s) { int status; unsigned int i; pthread_mutex_lock(&s->buf_mgmt.lock); if (s->stream_config.module == BLADERF_MODULE_TX) { /* If we've previously timed out on a stream, we'll likely have some * stale buffers marked "in-flight" that have since been cancelled. */ for (i = 0; i < s->buf_mgmt.num_buffers; i++) { if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) { s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY; } } pthread_cond_signal(&s->buf_mgmt.buf_ready); } else { assert(s->stream_config.module == BLADERF_MODULE_RX); s->buf_mgmt.prod_i = s->stream_config.num_xfers; for (i = 0; i < s->buf_mgmt.num_buffers; i++) { if (i < s->stream_config.num_xfers) { s->buf_mgmt.status[i] = SYNC_BUFFER_IN_FLIGHT; } else if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) { s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY; } } } pthread_mutex_unlock(&s->buf_mgmt.lock); status = bladerf_stream(s->worker->stream, s->stream_config.module); log_verbose("%s worker: stream ended with: %s\n", MODULE_STR(s), bladerf_strerror(status)); /* Save off the result of running the stream so we can report what * happened to the API caller */ pthread_mutex_lock(&s->worker->state_lock); s->worker->err_code = status; pthread_mutex_unlock(&s->worker->state_lock); /* Wake the API-side if an error occurred, so that it can propagate * the stream error code back to the API caller */ if (status != 0) { pthread_mutex_lock(&s->buf_mgmt.lock); pthread_cond_signal(&s->buf_mgmt.buf_ready); pthread_mutex_unlock(&s->buf_mgmt.lock); } }
static void exec_running_state(struct bladerf_sync *s) { int status; unsigned int i; pthread_mutex_lock(&s->buf_mgmt.lock); if (s->stream_config.module == BLADERF_MODULE_TX) { /* If we've previously timed out on a stream, we'll likely have some * stale buffers marked "in-flight" that have since been cancelled. */ for (i = 0; i < s->buf_mgmt.num_buffers; i++) { if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) { s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY; } } pthread_cond_signal(&s->buf_mgmt.buf_ready); } else { assert(s->stream_config.module == BLADERF_MODULE_RX); s->buf_mgmt.prod_i = s->stream_config.num_xfers; for (i = 0; i < s->buf_mgmt.num_buffers; i++) { if (i < s->stream_config.num_xfers) { s->buf_mgmt.status[i] = SYNC_BUFFER_IN_FLIGHT; } else if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) { s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY; } } } pthread_mutex_unlock(&s->buf_mgmt.lock); status = bladerf_stream(s->worker->stream, s->stream_config.module); log_verbose("%s worker: stream ended with: %s\n", MODULE_STR(s), bladerf_strerror(status)); /* Suppress warning if log_verbose is disabled */ (void)status; }
static void exec_running_state(struct bladerf_sync *s) { int status; status = bladerf_stream(s->worker->stream, s->stream_config.module); log_verbose("%s worker: stream ended with: %s\n", MODULE_STR(s), bladerf_strerror(status)); /* Save off the result of running the stream so we can report what * happened to the API caller */ pthread_mutex_lock(&s->worker->state_lock); s->worker->err_code = status; pthread_mutex_unlock(&s->worker->state_lock); /* Wake the API-side if an error occurred, so that it can propagate * the stream error code back to the API caller */ if (status != 0) { pthread_mutex_lock(&s->buf_mgmt.lock); pthread_cond_signal(&s->buf_mgmt.buf_ready); pthread_mutex_unlock(&s->buf_mgmt.lock); } }
static void *rx_callback(struct bladerf *dev, struct bladerf_stream *stream, struct bladerf_metadata *meta, void *samples, size_t num_samples, void *user_data) { unsigned int requests; /* Pending requests */ unsigned int next_idx; unsigned int samples_idx; void *next_buf = NULL; /* Next buffer to submit for reception */ struct bladerf_sync *s = (struct bladerf_sync *)user_data; struct sync_worker *w = s->worker; struct buffer_mgmt *b = &s->buf_mgmt; /* Check if the caller has requested us to shut down. We'll keep the * SHUTDOWN bit set through our transition into the IDLE state so we * can act on it there. */ pthread_mutex_lock(&w->request_lock); requests = w->requests; pthread_mutex_unlock(&w->request_lock); if (requests & SYNC_WORKER_STOP) { log_verbose("%s worker: Got STOP request upon entering callback. " "Ending stream.\n", MODULE_STR(s)); return NULL; } pthread_mutex_lock(&b->lock); /* Get the index of the buffer that was just filled */ samples_idx = sync_buf2idx(b, samples); if (b->resubmit_count == 0) { if (b->status[b->prod_i] == SYNC_BUFFER_EMPTY) { /* This buffer is now ready for the consumer */ b->status[samples_idx] = SYNC_BUFFER_FULL; pthread_cond_signal(&b->buf_ready); /* Update the state of the buffer being submitted next */ next_idx = b->prod_i; b->status[next_idx] = SYNC_BUFFER_IN_FLIGHT; next_buf = b->buffers[next_idx]; /* Advance to the next buffer for the next callback */ b->prod_i = (next_idx + 1) % b->num_buffers; log_verbose("%s worker: buf[%u] = full, buf[%u] = in_flight\n", MODULE_STR(s), samples_idx, next_idx); } else { /* TODO propgate back the RX Overrun to the sync_rx() caller */ log_debug("RX overrun @ buffer %u\r\n", samples_idx); next_buf = samples; b->resubmit_count = s->stream_config.num_xfers - 1; } } else { /* We're still recovering from an overrun at this point. Just * turn around and resubmit this buffer */ next_buf = samples; b->resubmit_count--; log_verbose("Resubmitting buffer %u (%u resubmissions left)\r\n", samples_idx, b->resubmit_count); } pthread_mutex_unlock(&b->lock); return next_buf; }
static sync_worker_state exec_idle_state(struct bladerf_sync *s) { sync_worker_state next_state = SYNC_WORKER_STATE_IDLE; unsigned int requests; unsigned int i; pthread_mutex_lock(&s->worker->request_lock); while (s->worker->requests == 0) { log_verbose("%s worker: Waiting for pending requests\n", MODULE_STR(s)); pthread_cond_wait(&s->worker->requests_pending, &s->worker->request_lock); } requests = s->worker->requests; s->worker->requests = 0; pthread_mutex_unlock(&s->worker->request_lock); if (requests & SYNC_WORKER_STOP) { log_verbose("%s worker: Got request to stop\n", module2str(s->stream_config.module)); next_state = SYNC_WORKER_STATE_SHUTTING_DOWN; } else if (requests & SYNC_WORKER_START) { log_verbose("%s worker: Got request to start\n", module2str(s->stream_config.module)); pthread_mutex_lock(&s->buf_mgmt.lock); if (s->stream_config.module == BLADERF_MODULE_TX) { /* If we've previously timed out on a stream, we'll likely have some * stale buffers marked "in-flight" that have since been cancelled. */ for (i = 0; i < s->buf_mgmt.num_buffers; i++) { if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) { s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY; } } pthread_cond_signal(&s->buf_mgmt.buf_ready); } else { assert(s->stream_config.module == BLADERF_MODULE_RX); s->buf_mgmt.prod_i = s->stream_config.num_xfers; for (i = 0; i < s->buf_mgmt.num_buffers; i++) { if (i < s->stream_config.num_xfers) { s->buf_mgmt.status[i] = SYNC_BUFFER_IN_FLIGHT; } else if (s->buf_mgmt.status[i] == SYNC_BUFFER_IN_FLIGHT) { s->buf_mgmt.status[i] = SYNC_BUFFER_EMPTY; } } } pthread_mutex_unlock(&s->buf_mgmt.lock); next_state = SYNC_WORKER_STATE_RUNNING; } else { log_warning("Invalid request value encountered: 0x%08X\n", s->worker->requests); } return next_state; }
int sync_worker_init(struct bladerf_sync *s) { int status = 0; s->worker = (struct sync_worker*) calloc(1, sizeof(*s->worker)); if (s->worker == NULL) { status = BLADERF_ERR_MEM; goto worker_init_out; } s->worker->state = SYNC_WORKER_STATE_STARTUP; s->worker->err_code = 0; s->worker->cb = s->stream_config.module == BLADERF_MODULE_RX ? rx_callback : tx_callback; status = bladerf_init_stream(&s->worker->stream, s->dev, s->worker->cb, &s->buf_mgmt.buffers, s->buf_mgmt.num_buffers, s->stream_config.format, s->stream_config.samples_per_buffer, s->stream_config.num_xfers, s); if (status != 0) { log_debug("%s worker: Failed to init stream: %s\n", MODULE_STR(s), bladerf_strerror(status)); goto worker_init_out; } status = pthread_mutex_init(&s->worker->state_lock, NULL); if (status != 0) { status = BLADERF_ERR_UNEXPECTED; goto worker_init_out; } status = pthread_cond_init(&s->worker->state_changed, NULL); if (status != 0) { status = BLADERF_ERR_UNEXPECTED; goto worker_init_out; } status = pthread_mutex_init(&s->worker->request_lock, NULL); if (status != 0) { status = BLADERF_ERR_UNEXPECTED; goto worker_init_out; } status = pthread_cond_init(&s->worker->requests_pending, NULL); if (status != 0) { status = BLADERF_ERR_UNEXPECTED; goto worker_init_out; } status = pthread_create(&s->worker->thread, NULL, sync_worker_task, s); if (status != 0) { status = BLADERF_ERR_UNEXPECTED; goto worker_init_out; } /* Wait until the worker thread has initialized and is ready to go */ status = sync_worker_wait_for_state(s->worker, SYNC_WORKER_STATE_IDLE, 1000); if (status != 0) { status = BLADERF_ERR_TIMEOUT; goto worker_init_out; } worker_init_out: if (status != 0) { free(s->worker); s->worker = NULL; } return status; }