void sync_worker_deinit(struct sync_worker *w, pthread_mutex_t *lock, pthread_cond_t *cond) { int status; if (w == NULL) { log_debug("%s called with NULL ptr\n", __FUNCTION__); return; } log_verbose("%s: Requesting worker %p to stop...\n", __FUNCTION__, w); sync_worker_submit_request(w, SYNC_WORKER_STOP); if (lock != NULL && cond != NULL) { pthread_mutex_lock(lock); pthread_cond_signal(cond); pthread_mutex_unlock(lock); } status = sync_worker_wait_for_state(w, SYNC_WORKER_STATE_STOPPED, 3000); if (status != 0) { log_warning("Timed out while stopping worker. Canceling thread.\n"); pthread_cancel(w->thread); } pthread_join(w->thread, NULL); log_verbose("%s: Worker joined.\n", __FUNCTION__); bladerf_deinit_stream(w->stream); free(w); }
int sync_rx(struct bladerf *dev, void *samples, unsigned num_samples, struct bladerf_metadata *user_meta, unsigned int timeout_ms) { struct bladerf_sync *s = dev->sync[BLADERF_MODULE_RX]; struct buffer_mgmt *b; int status = 0; bool exit_early = false; bool copied_data = false; unsigned int samples_returned = 0; uint8_t *samples_dest = (uint8_t*)samples; uint8_t *buf_src = NULL; unsigned int samples_to_copy = 0; unsigned int samples_per_buffer = 0; uint64_t target_timestamp = UINT64_MAX; if (s == NULL || samples == NULL) { log_debug("NULL pointer passed to %s\n", __FUNCTION__); return BLADERF_ERR_INVAL; } else if (s->stream_config.format == BLADERF_FORMAT_SC16_Q11_META) { if (user_meta == NULL) { log_debug("NULL metadata pointer passed to %s\n", __FUNCTION__); return BLADERF_ERR_INVAL; } else { user_meta->status = 0; target_timestamp = user_meta->timestamp; } } b = &s->buf_mgmt; samples_per_buffer = s->stream_config.samples_per_buffer; log_verbose("%s: Requests %u samples.\n", __FUNCTION__, num_samples); while (!exit_early && samples_returned < num_samples && status == 0) { switch (s->state) { case SYNC_STATE_CHECK_WORKER: { int stream_error; sync_worker_state worker_state = sync_worker_get_state(s->worker, &stream_error); /* Propagate stream error back to the caller. * They can call this function again to restart the stream and * try again. */ if (stream_error != 0) { status = stream_error; } else { if (worker_state == SYNC_WORKER_STATE_IDLE) { log_debug("%s: Worker is idle. Going to reset buf " "mgmt.\n", __FUNCTION__); s->state = SYNC_STATE_RESET_BUF_MGMT; } else if (worker_state == SYNC_WORKER_STATE_RUNNING) { s->state = SYNC_STATE_WAIT_FOR_BUFFER; } else { status = BLADERF_ERR_UNEXPECTED; log_debug("%s: Unexpected worker state=%d\n", __FUNCTION__, worker_state); } } break; } case SYNC_STATE_RESET_BUF_MGMT: MUTEX_LOCK(&b->lock); /* When the RX stream starts up, it will submit the first T * transfers, so the consumer index must be reset to 0 */ b->cons_i = 0; MUTEX_UNLOCK(&b->lock); log_debug("%s: Reset buf_mgmt consumer index\n", __FUNCTION__); s->state = SYNC_STATE_START_WORKER; break; case SYNC_STATE_START_WORKER: sync_worker_submit_request(s->worker, SYNC_WORKER_START); status = sync_worker_wait_for_state( s->worker, SYNC_WORKER_STATE_RUNNING, SYNC_WORKER_START_TIMEOUT_MS); if (status == 0) { s->state = SYNC_STATE_WAIT_FOR_BUFFER; log_debug("%s: Worker is now running.\n", __FUNCTION__); } else { log_debug("%s: Failed to start worker, (%d)\n", __FUNCTION__, status); } break; case SYNC_STATE_WAIT_FOR_BUFFER: MUTEX_LOCK(&b->lock); /* Check the buffer state, as the worker may have produced one * since we last queried the status */ if (b->status[b->cons_i] == SYNC_BUFFER_FULL) { s->state = SYNC_STATE_BUFFER_READY; log_verbose("%s: buffer %u is ready to consume\n", __FUNCTION__, b->cons_i); } else { status = wait_for_buffer(b, timeout_ms, __FUNCTION__, b->cons_i); if (status == 0) { if (b->status[b->cons_i] != SYNC_BUFFER_FULL) { s->state = SYNC_STATE_CHECK_WORKER; } else { s->state = SYNC_STATE_BUFFER_READY; log_verbose("%s: buffer %u is ready to consume\n", __FUNCTION__, b->cons_i); } } } MUTEX_UNLOCK(&b->lock); break; case SYNC_STATE_BUFFER_READY: MUTEX_LOCK(&b->lock); b->status[b->cons_i] = SYNC_BUFFER_PARTIAL; b->partial_off = 0; MUTEX_UNLOCK(&b->lock); switch (s->stream_config.format) { case BLADERF_FORMAT_SC16_Q11: s->state = SYNC_STATE_USING_BUFFER; break; case BLADERF_FORMAT_SC16_Q11_META: s->state = SYNC_STATE_USING_BUFFER_META; s->meta.curr_msg_off = 0; s->meta.msg_num = 0; break; default: assert(!"Invalid stream format"); status = BLADERF_ERR_UNEXPECTED; } break; case SYNC_STATE_USING_BUFFER: /* SC16Q11 buffers w/o metadata */ MUTEX_LOCK(&b->lock); buf_src = (uint8_t*)b->buffers[b->cons_i]; samples_to_copy = uint_min(num_samples - samples_returned, samples_per_buffer - b->partial_off); memcpy(samples_dest + samples2bytes(s, samples_returned), buf_src + samples2bytes(s, b->partial_off), samples2bytes(s, samples_to_copy)); b->partial_off += samples_to_copy; samples_returned += samples_to_copy; log_verbose("%s: Provided %u samples to caller\n", __FUNCTION__, samples_to_copy); /* We've finished consuming this buffer and can start looking * for available samples in the next buffer */ if (b->partial_off >= samples_per_buffer) { /* Check for symptom of out-of-bounds accesses */ assert(b->partial_off == samples_per_buffer); advance_rx_buffer(b); s->state = SYNC_STATE_WAIT_FOR_BUFFER; } MUTEX_UNLOCK(&b->lock); break; case SYNC_STATE_USING_BUFFER_META: /* SC16Q11 buffers w/ metadata */ MUTEX_LOCK(&b->lock); switch (s->meta.state) { case SYNC_META_STATE_HEADER: assert(s->meta.msg_num < s->meta.msg_per_buf); buf_src = (uint8_t*)b->buffers[b->cons_i]; s->meta.curr_msg = buf_src + dev->msg_size * s->meta.msg_num; s->meta.msg_timestamp = metadata_get_timestamp(s->meta.curr_msg); s->meta.msg_flags = metadata_get_flags(s->meta.curr_msg); s->meta.curr_msg_off = 0; /* We've encountered a discontinuity and need to return * what we have so far, setting the status flags */ if (copied_data && s->meta.msg_timestamp != s->meta.curr_timestamp) { user_meta->status |= BLADERF_META_STATUS_OVERRUN; exit_early = true; log_debug("Sample discontinuity detected @ " "buffer %u, message %u: Expected t=%llu, " "got t=%llu\n", b->cons_i, s->meta.msg_num, (unsigned long long)s->meta.curr_timestamp, (unsigned long long)s->meta.msg_timestamp); } else { log_verbose("Got header for message %u: " "t_new=%u, t_old=%u\n", s->meta.msg_num, s->meta.msg_timestamp, s->meta.curr_timestamp); } s->meta.curr_timestamp = s->meta.msg_timestamp; s->meta.state = SYNC_META_STATE_SAMPLES; break; case SYNC_META_STATE_SAMPLES: if (!copied_data && (user_meta->flags & BLADERF_META_FLAG_RX_NOW) == 0 && target_timestamp < s->meta.curr_timestamp) { log_debug("Current timestamp is %llu, " "target=%llu (user=%llu)\n", (unsigned long long)s->meta.curr_timestamp, (unsigned long long)target_timestamp, (unsigned long long)user_meta->timestamp); status = BLADERF_ERR_TIME_PAST; } else if ((user_meta->flags & BLADERF_META_FLAG_RX_NOW) || target_timestamp == s->meta.curr_timestamp) { /* Copy the request amount up to the end of a * this message in the current buffer */ samples_to_copy = uint_min(num_samples - samples_returned, left_in_msg(s)); memcpy(samples_dest + samples2bytes(s, samples_returned), s->meta.curr_msg + METADATA_HEADER_SIZE + samples2bytes(s, s->meta.curr_msg_off), samples2bytes(s, samples_to_copy)); samples_returned += samples_to_copy; s->meta.curr_msg_off += samples_to_copy; if (!copied_data && (user_meta->flags & BLADERF_META_FLAG_RX_NOW)) { /* Provide the user with the timestamp at the * first returned sample when the * NOW flag has been provided */ user_meta->timestamp = s->meta.curr_timestamp; log_verbose("Updated user meta timestamp with: " "%llu\n", (unsigned long long) user_meta->timestamp); } copied_data = true; s->meta.curr_timestamp += samples_to_copy; /* We've begun copying samples, so our target will * just keep tracking the current timestamp. */ target_timestamp = s->meta.curr_timestamp; log_verbose("After copying samples, t=%llu\n", (unsigned long long)s->meta.curr_timestamp); if (left_in_msg(s) == 0) { assert(s->meta.curr_msg_off == s->meta.samples_per_msg); s->meta.state = SYNC_META_STATE_HEADER; s->meta.msg_num++; if (s->meta.msg_num >= s->meta.msg_per_buf) { assert(s->meta.msg_num == s->meta.msg_per_buf); advance_rx_buffer(b); s->meta.msg_num = 0; s->state = SYNC_STATE_WAIT_FOR_BUFFER; } } } else { const uint64_t time_delta = target_timestamp - s->meta.curr_timestamp; uint64_t left_in_buffer = (uint64_t) s->meta.samples_per_msg * (s->meta.msg_per_buf - s->meta.msg_num); /* Account for current position in buffer */ left_in_buffer -= s->meta.curr_msg_off; if (time_delta >= left_in_buffer) { /* Discard the remainder of this buffer */ advance_rx_buffer(b); s->state = SYNC_STATE_WAIT_FOR_BUFFER; s->meta.state = SYNC_META_STATE_HEADER; log_verbose("%s: Discarding rest of buffer.\n", __FUNCTION__); } else if (time_delta <= left_in_msg(s)) { /* Fast forward within the current message */ assert(time_delta <= SIZE_MAX); s->meta.curr_msg_off += (size_t) time_delta; s->meta.curr_timestamp += time_delta; log_verbose("%s: Seeking within message (t=%llu)\n", __FUNCTION__, s->meta.curr_timestamp); } else { s->meta.state = SYNC_META_STATE_HEADER; s->meta.msg_num += timestamp_to_msg(s, time_delta); log_verbose("%s: Seeking to message %u.\n", __FUNCTION__, s->meta.msg_num); } } break; default: assert(!"Invalid state"); status = BLADERF_ERR_UNEXPECTED; } MUTEX_UNLOCK(&b->lock); break; } } if (user_meta) { user_meta->actual_count = samples_returned; } return status; }