void rxtx_task_exec_stop(struct rxtx_data *rxtx, unsigned char *requests, struct bladerf *dev) { int status; *requests = rxtx_get_requests(rxtx, RXTX_TASK_REQ_STOP | RXTX_TASK_REQ_SHUTDOWN); pthread_mutex_lock(&rxtx->data_mgmt.lock); bladerf_deinit_stream(rxtx->data_mgmt.stream); rxtx->data_mgmt.stream = NULL; pthread_mutex_unlock(&rxtx->data_mgmt.lock); pthread_mutex_lock(&rxtx->file_mgmt.file_lock); if (rxtx->file_mgmt.file != NULL) { fclose(rxtx->file_mgmt.file); rxtx->file_mgmt.file = NULL; } pthread_mutex_unlock(&rxtx->file_mgmt.file_lock); if (*requests & RXTX_TASK_REQ_SHUTDOWN) { rxtx_set_state(rxtx, RXTX_STATE_SHUTDOWN); } else { rxtx_set_state(rxtx, RXTX_STATE_IDLE); } status = bladerf_enable_module(dev, rxtx->module, false); if (status < 0) { set_last_error(&rxtx->last_error, ETYPE_BLADERF, status); } *requests = 0; rxtx_release_wait(rxtx); }
static void *rx_callback(struct bladerf *dev, struct bladerf_stream *stream, struct bladerf_metadata *meta, void *samples, size_t num_samples, void *user_data) { struct rx_callback_data *cb_data = (struct rx_callback_data *)user_data; struct rxtx_data *rx = cb_data->rx; unsigned char requests; void *ret; size_t n_to_write; /* Stop stream on STOP or SHUTDOWN, but only clear STOP. This will keep * the SHUTDOWN request around so we can read it when determining * our state transition */ requests = rxtx_get_requests(rx, RXTX_TASK_REQ_STOP); if (requests & (RXTX_TASK_REQ_STOP | RXTX_TASK_REQ_SHUTDOWN)) { return NULL; } pthread_mutex_lock(&rx->data_mgmt.lock); if (!cb_data->inf) { n_to_write = min_sz(cb_data->samples_left, num_samples); cb_data->samples_left -= n_to_write; } else { n_to_write = num_samples; } /* We assume we have IQ pairs */ assert((n_to_write & 1) == 0); if (n_to_write > 0) { sc16q12_sample_fixup(samples, n_to_write); /* Write the samples out */ cb_data->write_samples(rx, (int16_t*)samples, n_to_write); /* Fetch the next buffer */ rx->data_mgmt.next_idx++; rx->data_mgmt.next_idx %= rx->data_mgmt.num_buffers; ret = rx->data_mgmt.buffers[rx->data_mgmt.next_idx]; } else { /* We've already written the request number of samples */ ret = NULL; } pthread_mutex_unlock(&rx->data_mgmt.lock); return ret; }
void rxtx_task_exec_stop(struct rxtx_data *rxtx, unsigned char *requests) { *requests = rxtx_get_requests(rxtx, RXTX_TASK_REQ_STOP | RXTX_TASK_REQ_SHUTDOWN); MUTEX_LOCK(&rxtx->file_mgmt.file_lock); if (rxtx->file_mgmt.file != NULL) { fclose(rxtx->file_mgmt.file); rxtx->file_mgmt.file = NULL; } MUTEX_UNLOCK(&rxtx->file_mgmt.file_lock); if (*requests & RXTX_TASK_REQ_SHUTDOWN) { rxtx_set_state(rxtx, RXTX_STATE_SHUTDOWN); } else { rxtx_set_state(rxtx, RXTX_STATE_IDLE); } *requests = 0; rxtx_release_wait(rxtx); }
static int tx_task_exec_running(struct rxtx_data *tx, struct cli_state *s) { int status = 0; unsigned int samples_per_buffer; int16_t *tx_buffer; struct tx_params *tx_params = tx->params; unsigned int repeats_remaining; unsigned int delay_us; unsigned int delay_samples; unsigned int delay_samples_remaining; bool repeat_infinite; unsigned int timeout_ms; unsigned int sample_rate; enum state { INIT, READ_FILE, DELAY, PAD_TRAILING, DONE }; enum state state = INIT; /* Fetch the parameters required for the TX operation */ MUTEX_LOCK(&tx->param_lock); repeats_remaining = tx_params->repeat; delay_us = tx_params->repeat_delay; MUTEX_UNLOCK(&tx->param_lock); repeat_infinite = (repeats_remaining == 0); MUTEX_LOCK(&tx->data_mgmt.lock); samples_per_buffer = (unsigned int)tx->data_mgmt.samples_per_buffer; timeout_ms = tx->data_mgmt.timeout_ms; MUTEX_UNLOCK(&tx->data_mgmt.lock); status = bladerf_get_sample_rate(s->dev, tx->module, &sample_rate); if (status != 0) { set_last_error(&tx->last_error, ETYPE_BLADERF, status); return CLI_RET_LIBBLADERF; } /* Compute delay time as a sample count */ delay_samples = (unsigned int)((uint64_t)sample_rate * delay_us / 1000000); delay_samples_remaining = delay_samples; /* Allocate a buffer to hold each block of samples to transmit */ tx_buffer = (int16_t*) malloc(samples_per_buffer * 2 * sizeof(int16_t)); if (tx_buffer == NULL) { status = CLI_RET_MEM; set_last_error(&tx->last_error, ETYPE_ERRNO, errno == 0 ? ENOMEM : errno); } /* Keep writing samples while there is more data to send and no failures * have occurred */ while (state != DONE && status == 0) { unsigned char requests; unsigned int buffer_samples_remaining = samples_per_buffer; int16_t *tx_buffer_current = tx_buffer; /* Stop stream on STOP or SHUTDOWN, but only clear STOP. This will keep * the SHUTDOWN request around so we can read it when determining * our state transition */ requests = rxtx_get_requests(tx, RXTX_TASK_REQ_STOP); if (requests & (RXTX_TASK_REQ_STOP | RXTX_TASK_REQ_SHUTDOWN)) { break; } /* Keep adding to the buffer until it is full or a failure occurs */ while (buffer_samples_remaining > 0 && status == 0 && state != DONE) { size_t samples_populated = 0; switch (state) { case INIT: case READ_FILE: MUTEX_LOCK(&tx->file_mgmt.file_lock); /* Read from the input file */ samples_populated = fread(tx_buffer_current, 2 * sizeof(int16_t), buffer_samples_remaining, tx->file_mgmt.file); assert(samples_populated <= UINT_MAX); /* If the end of the file was reached, determine whether * to delay, re-read from the file, or pad the rest of the * buffer and finish */ if (feof(tx->file_mgmt.file)) { repeats_remaining--; if ((repeats_remaining > 0) || repeat_infinite) { if (delay_samples != 0) { delay_samples_remaining = delay_samples; state = DELAY; } } else { state = PAD_TRAILING; } /* Clear the EOF condition and rewind the file */ clearerr(tx->file_mgmt.file); rewind(tx->file_mgmt.file); } /* Check for errors */ else if (ferror(tx->file_mgmt.file)) { status = errno; set_last_error(&tx->last_error, ETYPE_ERRNO, status); } MUTEX_UNLOCK(&tx->file_mgmt.file_lock); break; case DELAY: /* Insert as many zeros as are necessary to realize the * specified repeat delay */ samples_populated = uint_min(buffer_samples_remaining, delay_samples_remaining); memset(tx_buffer_current, 0, samples_populated * 2 * sizeof(uint16_t)); delay_samples_remaining -= (unsigned int)samples_populated; if (delay_samples_remaining == 0) { state = READ_FILE; } break; case PAD_TRAILING: /* Populate the remainder of the buffer with zeros */ memset(tx_buffer_current, 0, buffer_samples_remaining * 2 * sizeof(uint16_t)); state = DONE; break; case DONE: default: break; } /* Advance the buffer pointer. * Remember, two int16_t's make up 1 sample in the SC16Q11 format */ buffer_samples_remaining -= (unsigned int)samples_populated; tx_buffer_current += (2 * samples_populated); } /* If there were no errors, transmit the data buffer */ if (status == 0) { bladerf_sync_tx(s->dev, tx_buffer, samples_per_buffer, NULL, timeout_ms); } } free(tx_buffer); return status; }
/* This callback reads SC16 Q11 data from a file (assumed to be little endian) * and ships this data off in the provided sample buffers */ static void *tx_callback(struct bladerf *dev, struct bladerf_stream *stream, struct bladerf_metadata *meta, void *samples, size_t num_samples, void *user_data) { struct tx_callback_data *cb_data = (struct tx_callback_data*)user_data; struct rxtx_data *tx = cb_data->tx; struct tx_params *tx_params = tx->params; unsigned int delay = cb_data->delay; unsigned char requests; /* Requests from main control thread */ size_t read_status; /* Status from read() calls */ size_t n_read; /* Number of bytes read from file */ size_t to_read; /* Temp var, # of samples to read */ bool zero_pad; /* We need to zero-pad the rest of the buffer */ int16_t *samples_int16 = NULL; /* Stop stream on STOP or SHUTDOWN, but only clear STOP. This will keep * the SHUTDOWN request around so we can read it when determining * our state transition */ requests = rxtx_get_requests(tx, RXTX_TASK_REQ_STOP); if (cb_data->done || requests & (RXTX_TASK_REQ_STOP | RXTX_TASK_REQ_SHUTDOWN)) { return NULL; } /* If we have a delay scheduled...wait around * TODO replace this with a buffer filled with the appropriate number * of 0's based upon the sample rate */ if (delay) { usleep(delay); cb_data->delay = 0; } pthread_mutex_lock(&tx->data_mgmt.lock); pthread_mutex_lock(&tx->file_mgmt.file_lock); /* Get the next buffer */ samples_int16 = (int16_t*)tx->data_mgmt.buffers[tx->data_mgmt.next_idx]; tx->data_mgmt.next_idx++; tx->data_mgmt.next_idx %= tx->data_mgmt.num_buffers; n_read = 0; zero_pad = false; /* Keep reading from the file until we have enough data, or have a * a condition in which we'll just zero pad the rest of the buffer */ while (n_read < tx->data_mgmt.samples_per_buffer && !zero_pad) { to_read = 2 * tx->data_mgmt.samples_per_buffer - n_read; read_status = fread(samples_int16 + n_read, sizeof(int16_t), to_read, tx->file_mgmt.file); if (read_status != to_read && ferror(tx->file_mgmt.file)) { set_last_error(&tx->last_error, ETYPE_CLI, CMD_RET_FILEOP); samples_int16 = NULL; goto tx_callback_out; } n_read += read_status; /* Hit the end of the file */ if (feof(tx->file_mgmt.file)) { /* We're going to repeat from the start of the file */ if (cb_data->repeat_inf || --cb_data->repeats_left > 0) { pthread_mutex_lock(&tx->param_lock); cb_data->delay = tx_params->repeat_delay; pthread_mutex_unlock(&tx->param_lock); if (fseek(tx->file_mgmt.file, 0, SEEK_SET) < 0) { set_last_error(&tx->last_error, ETYPE_ERRNO, errno); samples_int16 = NULL; goto tx_callback_out; } /* We have to delay first, so we'll zero out and get some * data in the next callback */ if (cb_data->delay != 0) { zero_pad = true; } } else { /* No repeats left. Finish off whatever we have and note * that the next callback should start shutting things down */ zero_pad = true; cb_data->done = true; } } } tx_callback_out: pthread_mutex_unlock(&tx->file_mgmt.file_lock); if (zero_pad) { memset(samples_int16 + n_read, 0, tx->data_mgmt.samples_per_buffer - n_read); } pthread_mutex_unlock(&tx->data_mgmt.lock); return samples_int16; }