void rxtx_print_state(struct rxtx_data *rxtx, const char *prefix, const char *suffix) { enum rxtx_state state = rxtx_get_state(rxtx); switch (state) { case RXTX_STATE_IDLE: printf("%sIdle%s", prefix, suffix); break; case RXTX_STATE_START: printf("%sStarting%s", prefix, suffix); break; case RXTX_STATE_RUNNING: printf("%sRunning%s", prefix, suffix); break; case RXTX_STATE_STOP: printf("%sStopping%s", prefix, suffix); break; case RXTX_STATE_SHUTDOWN: printf("%sShutting down%s", prefix, suffix); break; case RXTX_STATE_FAIL: printf("%sFailed Initialization%s", prefix, suffix); break; default: printf("%sInvalid/Unknown (BUG)%s", prefix, suffix); } }
int rxtx_cmd_start_check(struct cli_state *s, struct rxtx_data *rxtx, const char *argv0) { int status = CLI_RET_UNKNOWN; bool have_file; if (rxtx_get_state(rxtx) != RXTX_STATE_IDLE) { return CLI_RET_STATE; } else { MUTEX_LOCK(&rxtx->file_mgmt.file_meta_lock); have_file = (rxtx->file_mgmt.path != NULL); MUTEX_UNLOCK(&rxtx->file_mgmt.file_meta_lock); if (!have_file) { cli_err(s, argv0, "File not configured\n"); status = CLI_RET_INVPARAM; } else { status = validate_stream_params(s, rxtx, argv0); } if (status == 0) { check_samplerate(s, rxtx); } } return status; }
void rxtx_shutdown(struct rxtx_data *rxtx) { if (rxtx_get_state(rxtx) != RXTX_STATE_FAIL) { rxtx_submit_request(rxtx, RXTX_TASK_REQ_SHUTDOWN); pthread_join(rxtx->task_mgmt.thread, NULL); } free(rxtx->file_mgmt.path); }
int rxtx_cmd_stop(struct cli_state *s, struct rxtx_data *rxtx) { int status; if (rxtx_get_state(rxtx) != RXTX_STATE_RUNNING) { status = CLI_RET_STATE; } else { rxtx_submit_request(rxtx, RXTX_TASK_REQ_STOP); status = 0; } return status; }
int rxtx_cmd_stop(struct cli_state *s, struct rxtx_data *rxtx) { int status; if (!cli_device_is_opened(s)) { status = CMD_RET_NODEV; } else if (rxtx_get_state(rxtx) != RXTX_STATE_RUNNING) { status = CMD_RET_STATE; } else { rxtx_submit_request(rxtx, RXTX_TASK_REQ_STOP); status = 0; } return status; }
int rxtx_cmd_start_check(struct cli_state *s, struct rxtx_data *rxtx, const char *argv0) { int status = CMD_RET_UNKNOWN; int fpga_status; bool have_file; if (!cli_device_is_opened(s)) { return CMD_RET_NODEV; } else if (rxtx_get_state(rxtx) != RXTX_STATE_IDLE) { return CMD_RET_STATE; } else { fpga_status = bladerf_is_fpga_configured(s->dev); if (fpga_status < 0) { s->last_lib_error = fpga_status; status = CMD_RET_LIBBLADERF; } else if (fpga_status != 1) { status = CMD_RET_NOFPGA; } else { pthread_mutex_lock(&rxtx->file_mgmt.file_meta_lock); have_file = (rxtx->file_mgmt.path != NULL); pthread_mutex_unlock(&rxtx->file_mgmt.file_meta_lock); if (!have_file) { cli_err(s, argv0, "File not configured"); status = CMD_RET_INVPARAM; } else { status = validate_stream_params(s, rxtx, argv0); } if (status == 0) { check_samplerate(s, rxtx); } } } return status; }
void *rx_task(void *cli_state_arg) { int status = 0; unsigned char requests; enum rxtx_state task_state; struct cli_state *cli_state = (struct cli_state *) cli_state_arg; struct rxtx_data *rx = cli_state->rx; struct rx_params *rx_params = rx->params; struct rx_callback_data cb_data; task_state = rxtx_get_state(rx); assert(task_state == RXTX_STATE_INIT); set_last_error(&rx->last_error, ETYPE_BLADERF, 0); requests = 0; while (task_state != RXTX_STATE_SHUTDOWN) { switch (task_state) { case RXTX_STATE_INIT: rxtx_set_state(rx, RXTX_STATE_IDLE); break; case RXTX_STATE_IDLE: rxtx_task_exec_idle(rx, &requests); break; case RXTX_STATE_START: { /* This should be set to an appropriate value upon * encountering an error condition */ enum error_type err_type = ETYPE_BUG; /* Clear the last error */ set_last_error(&rx->last_error, ETYPE_ERRNO, 0); /* Set up count of samples to receive */ pthread_mutex_lock(&rx->param_lock); cb_data.samples_left = rx_params->n_samples; pthread_mutex_unlock(&rx->param_lock); cb_data.rx = rx; cb_data.inf = cb_data.samples_left == 0; /* Choose the callback appropriate for the desired file type */ pthread_mutex_lock(&rx->file_mgmt.file_meta_lock); switch (rx->file_mgmt.format) { case RXTX_FMT_CSV_SC16Q12: cb_data.write_samples = rx_write_csv_sc16q12; break; case RXTX_FMT_BIN_SC16Q12: cb_data.write_samples = rx_write_bin_sc16q12; break; default: status = CMD_RET_INVPARAM; set_last_error(&rx->last_error, ETYPE_CLI, status); rxtx_set_state(rx, RXTX_STATE_IDLE); } /* Open the specified file */ if (status == 0) { assert(rx->file_mgmt.path); } pthread_mutex_unlock(&rx->file_mgmt.file_meta_lock); /* Set up the reception stream and buffer information */ if (status == 0) { pthread_mutex_lock(&rx->data_mgmt.lock); rx->data_mgmt.next_idx = 0; status = bladerf_init_stream(&rx->data_mgmt.stream, cli_state->dev, rx_callback, &rx->data_mgmt.buffers, rx->data_mgmt.num_buffers, BLADERF_FORMAT_SC16_Q12, rx->data_mgmt.samples_per_buffer, rx->data_mgmt.num_transfers, &cb_data); if (status < 0) { err_type = ETYPE_BLADERF; } pthread_mutex_unlock(&rx->data_mgmt.lock); } if (status == 0) { rxtx_set_state(rx, RXTX_STATE_RUNNING); } else { bladerf_deinit_stream(rx->data_mgmt.stream); rx->data_mgmt.stream = NULL; set_last_error(&rx->last_error, err_type, status); rxtx_set_state(rx, RXTX_STATE_IDLE); } } break; case RXTX_STATE_RUNNING: rxtx_task_exec_running(rx, cli_state); break; case RXTX_STATE_STOP: rxtx_task_exec_stop(rx, &requests); break; case RXTX_STATE_SHUTDOWN: break; default: /* Bug - can only get here with a corrupted value */ assert(0); } task_state = rxtx_get_state(rx); } return NULL; }
int rxtx_handle_wait(struct cli_state *s, struct rxtx_data *rxtx, int argc, char **argv) { int status; bool ok; unsigned int timeout_ms = 0; struct timespec timeout_abs; enum rxtx_state state; static const struct numeric_suffix times[] = { { "ms", 1 }, { "s", 1000 }, { "m", 60 * 1000 }, { "h", 60 * 60 * 1000 }, }; if (argc < 2 || argc > 3) { return CMD_RET_NARGS; } /* The start cmd should have waited until we entered the RUNNING state */ state = rxtx_get_state(rxtx); if (state != RXTX_STATE_RUNNING) { return 0; } if (argc == 3) { timeout_ms = str2uint_suffix(argv[2], 0, UINT_MAX, times, sizeof(times)/sizeof(times[0]), &ok); if (!ok) { cli_err(s, argv[0], "Invalid wait timeout: \"%s\"\n", argv[2]); return CMD_RET_INVPARAM; } } if (timeout_ms != 0) { const unsigned int timeout_sec = timeout_ms / 1000; status = clock_gettime(CLOCK_REALTIME, &timeout_abs); if (status != 0) { return CMD_RET_UNKNOWN; } timeout_abs.tv_sec += timeout_sec; timeout_abs.tv_nsec += (timeout_ms % 1000) * 1000 * 1000; if (timeout_abs.tv_nsec >= NSEC_PER_SEC) { timeout_abs.tv_sec += timeout_abs.tv_nsec / NSEC_PER_SEC; timeout_abs.tv_nsec %= NSEC_PER_SEC; } pthread_mutex_lock(&rxtx->task_mgmt.lock); rxtx->task_mgmt.main_task_waiting = true; while (rxtx->task_mgmt.main_task_waiting) { status = pthread_cond_timedwait(&rxtx->task_mgmt.signal_done, &rxtx->task_mgmt.lock, &timeout_abs); if (status == ETIMEDOUT) { rxtx->task_mgmt.main_task_waiting = false; } } pthread_mutex_unlock(&rxtx->task_mgmt.lock); /* Expected and OK condition */ if (status == ETIMEDOUT) { status = 0; } } else { pthread_mutex_lock(&rxtx->task_mgmt.lock); rxtx->task_mgmt.main_task_waiting = true; while (rxtx->task_mgmt.main_task_waiting) { status = pthread_cond_wait(&rxtx->task_mgmt.signal_done, &rxtx->task_mgmt.lock); } pthread_mutex_unlock(&rxtx->task_mgmt.lock); } if (status != 0) { status = CMD_RET_UNKNOWN; } return status; }
bool rxtx_task_running(struct rxtx_data *rxtx) { return rxtx_get_state(rxtx) == RXTX_STATE_RUNNING; }
int rxtx_handle_wait(struct cli_state *s, struct rxtx_data *rxtx, int argc, char **argv) { int status = CLI_RET_UNKNOWN; bool ok; unsigned int timeout_ms = 0; struct timespec timeout_abs; enum rxtx_state state; static const struct numeric_suffix times[] = { { "ms", 1 }, { "s", 1000 }, { "m", 60 * 1000 }, { "h", 60 * 60 * 1000 }, }; if (argc < 2 || argc > 3) { return CLI_RET_NARGS; } /* The start cmd should have waited until we entered the RUNNING state */ state = rxtx_get_state(rxtx); if (state != RXTX_STATE_RUNNING) { return 0; } if (argc == 3) { timeout_ms = str2uint_suffix(argv[2], 0, UINT_MAX, times, sizeof(times)/sizeof(times[0]), &ok); if (!ok) { cli_err(s, argv[0], "Invalid wait timeout: \"%s\"\n", argv[2]); return CLI_RET_INVPARAM; } } /* Release the device lock (acquired in cmd_handle()) while we wait * because we won't be doing device-control operations during this time. * This ensures that when the associated rx/tx task completes, it will be * able to acquire the device control lock to release this wait. */ MUTEX_UNLOCK(&s->dev_lock); if (timeout_ms != 0) { const unsigned int timeout_sec = timeout_ms / 1000; status = clock_gettime(CLOCK_REALTIME, &timeout_abs); if (status != 0) { goto out; } timeout_abs.tv_sec += timeout_sec; timeout_abs.tv_nsec += (timeout_ms % 1000) * 1000 * 1000; if (timeout_abs.tv_nsec >= NSEC_PER_SEC) { timeout_abs.tv_sec += timeout_abs.tv_nsec / NSEC_PER_SEC; timeout_abs.tv_nsec %= NSEC_PER_SEC; } MUTEX_LOCK(&rxtx->task_mgmt.lock); rxtx->task_mgmt.main_task_waiting = true; while (rxtx->task_mgmt.main_task_waiting) { status = pthread_cond_timedwait(&rxtx->task_mgmt.signal_done, &rxtx->task_mgmt.lock, &timeout_abs); if (status == ETIMEDOUT) { rxtx->task_mgmt.main_task_waiting = false; } } MUTEX_UNLOCK(&rxtx->task_mgmt.lock); } else { MUTEX_LOCK(&rxtx->task_mgmt.lock); rxtx->task_mgmt.main_task_waiting = true; while (rxtx->task_mgmt.main_task_waiting) { status = pthread_cond_wait(&rxtx->task_mgmt.signal_done, &rxtx->task_mgmt.lock); } MUTEX_UNLOCK(&rxtx->task_mgmt.lock); } out: /* Re-acquire the device control lock, as the top-level command handler * will be unlocking this when it's done */ MUTEX_LOCK(&s->dev_lock); /* Expected and OK condition */ if (status == ETIMEDOUT) { status = 0; } if (status != 0) { status = CLI_RET_UNKNOWN; } return status; }
void *tx_task(void *cli_state_arg) { int status = 0; int disable_status; unsigned char requests; enum rxtx_state task_state; struct cli_state *cli_state = (struct cli_state *) cli_state_arg; struct rxtx_data *tx = cli_state->tx; MUTEX *dev_lock = &cli_state->dev_lock; /* We expect to be in the IDLE state when this is kicked off. We could * also get into the shutdown state if the program exits before we * finish up initialization */ task_state = rxtx_get_state(tx); assert(task_state == RXTX_STATE_INIT); set_last_error(&tx->last_error, ETYPE_BLADERF, 0); requests = 0; while (task_state != RXTX_STATE_SHUTDOWN) { task_state = rxtx_get_state(tx); switch (task_state) { case RXTX_STATE_INIT: rxtx_set_state(tx, RXTX_STATE_IDLE); break; case RXTX_STATE_IDLE: rxtx_task_exec_idle(tx, &requests); break; case RXTX_STATE_START: { enum error_type err_type = ETYPE_BUG; /* Clear out the last error */ set_last_error(&tx->last_error, ETYPE_ERRNO, 0); /* Bug catcher */ MUTEX_LOCK(&tx->file_mgmt.file_meta_lock); assert(tx->file_mgmt.file != NULL); MUTEX_UNLOCK(&tx->file_mgmt.file_meta_lock); /* Initialize the TX synchronous data configuration */ status = bladerf_sync_config(cli_state->dev, BLADERF_MODULE_TX, BLADERF_FORMAT_SC16_Q11, tx->data_mgmt.num_buffers, tx->data_mgmt.samples_per_buffer, tx->data_mgmt.num_transfers, tx->data_mgmt.timeout_ms); if (status < 0) { err_type = ETYPE_BLADERF; } if (status == 0) { rxtx_set_state(tx, RXTX_STATE_RUNNING); } else { set_last_error(&tx->last_error, err_type, status); rxtx_set_state(tx, RXTX_STATE_IDLE); } } break; case RXTX_STATE_RUNNING: MUTEX_LOCK(dev_lock); status = bladerf_enable_module(cli_state->dev, tx->module, true); MUTEX_UNLOCK(dev_lock); if (status < 0) { set_last_error(&tx->last_error, ETYPE_BLADERF, status); } else { status = tx_task_exec_running(tx, cli_state); if (status < 0) { set_last_error(&tx->last_error, ETYPE_BLADERF, status); } MUTEX_LOCK(dev_lock); disable_status = bladerf_enable_module(cli_state->dev, tx->module, false); MUTEX_UNLOCK(dev_lock); if (status == 0 && disable_status < 0) { set_last_error( &tx->last_error, ETYPE_BLADERF, disable_status); } } rxtx_set_state(tx, RXTX_STATE_STOP); break; case RXTX_STATE_STOP: rxtx_task_exec_stop(tx, &requests); break; case RXTX_STATE_SHUTDOWN: break; default: /* Bug */ assert(0); rxtx_set_state(tx, RXTX_STATE_IDLE); } } return NULL; }
void *tx_task(void *cli_state_arg) { int status = 0; unsigned char requests; enum rxtx_state task_state; struct cli_state *cli_state = (struct cli_state *) cli_state_arg; struct rxtx_data *tx = cli_state->tx; struct tx_params *tx_params = tx->params; struct tx_callback_data cb_data; /* We expect to be in the IDLE state when this is kicked off. We could * also get into the shutdown state if the program exits before we * finish up initialization */ task_state = rxtx_get_state(tx); assert(task_state == RXTX_STATE_INIT); set_last_error(&tx->last_error, ETYPE_BLADERF, 0); requests = 0; while (task_state != RXTX_STATE_SHUTDOWN) { task_state = rxtx_get_state(tx); switch (task_state) { case RXTX_STATE_INIT: rxtx_set_state(tx, RXTX_STATE_IDLE); break; case RXTX_STATE_IDLE: rxtx_task_exec_idle(tx, &requests); break; case RXTX_STATE_START: { enum error_type err_type = ETYPE_BUG; /* Clear out the last error */ set_last_error(&tx->last_error, ETYPE_ERRNO, 0); /* Set up repeat and delay parameters for this run */ cb_data.tx = tx; cb_data.delay = 0; cb_data.done = false; pthread_mutex_lock(&tx->param_lock); cb_data.repeats_left = tx_params->repeat; pthread_mutex_unlock(&tx->param_lock); if (cb_data.repeats_left == 0) { cb_data.repeat_inf = true; } else { cb_data.repeat_inf = false; } /* Bug catcher */ pthread_mutex_lock(&tx->file_mgmt.file_meta_lock); assert(tx->file_mgmt.file != NULL); pthread_mutex_unlock(&tx->file_mgmt.file_meta_lock); /* Initialize the stream */ status = bladerf_init_stream(&tx->data_mgmt.stream, cli_state->dev, tx_callback, &tx->data_mgmt.buffers, tx->data_mgmt.num_buffers, BLADERF_FORMAT_SC16_Q11, tx->data_mgmt.samples_per_buffer, tx->data_mgmt.num_transfers, &cb_data); if (status < 0) { err_type = ETYPE_BLADERF; } if (status == 0) { rxtx_set_state(tx, RXTX_STATE_RUNNING); } else { bladerf_deinit_stream(tx->data_mgmt.stream); tx->data_mgmt.stream = NULL; set_last_error(&tx->last_error, err_type, status); rxtx_set_state(tx, RXTX_STATE_IDLE); } } break; case RXTX_STATE_RUNNING: rxtx_task_exec_running(tx, cli_state); break; case RXTX_STATE_STOP: rxtx_task_exec_stop(tx, &requests); break; case RXTX_STATE_SHUTDOWN: break; default: /* Bug */ assert(0); rxtx_set_state(tx, RXTX_STATE_IDLE); } } return NULL; }