This example libiio program is meant to exercise the features of IIO functionality on the AD9361 found on the AD-FMCOMMS2-EBZ, AD-FMCOMMS3-EBZ, and the ADRV9361-Z7035 RF SOM.
#include "iiostream-common.h"
#include <iio/iio.h>
#include <iio/iio-debug.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#define MHZ(x) ((long long)(x*1000000.0 + .5))
#define GHZ(x) ((long long)(x*1000000000.0 + .5))
#define IIO_ENSURE(expr) { \
if (!(expr)) { \
(void) fprintf(stderr, "assertion failed (%s:%d)\n", __FILE__, __LINE__); \
(void) abort(); \
} \
}
#define BLOCK_SIZE (1024 * 1024)
enum iodev { RX, TX };
struct stream_cfg {
long long bw_hz;
long long fs_hz;
long long lo_hz;
const char* rfport;
};
static char tmpstr[64];
static struct iio_context *ctx = NULL;
static struct iio_channel *rx0_i = NULL;
static struct iio_channel *rx0_q = NULL;
static struct iio_channel *tx0_i = NULL;
static struct iio_channel *tx0_q = NULL;
static struct iio_buffer *rxbuf = NULL;
static struct iio_buffer *txbuf = NULL;
static struct iio_stream *rxstream = NULL;
static struct iio_stream *txstream = NULL;
static struct iio_channels_mask *rxmask = NULL;
static struct iio_channels_mask *txmask = NULL;
static void shutdown(void)
{
printf("* Destroying streams\n");
if (rxstream) {iio_stream_destroy(rxstream); }
if (txstream) { iio_stream_destroy(txstream); }
printf("* Destroying buffers\n");
if (rxbuf) { iio_buffer_destroy(rxbuf); }
if (txbuf) { iio_buffer_destroy(txbuf); }
printf("* Destroying channel masks\n");
if (rxmask) { iio_channels_mask_destroy(rxmask); }
if (txmask) { iio_channels_mask_destroy(txmask); }
printf("* Destroying context\n");
if (ctx) { iio_context_destroy(ctx); }
exit(0);
}
static void handle_sig(int sig)
{
printf("Waiting for process to finish... Got signal %d\n", sig);
stop_stream();
}
static void errchk(int v, const char* what) {
if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); shutdown(); }
}
static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val)
{
errchk(iio_channel_attr_write_longlong(chn, what, val), what);
}
static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str)
{
errchk(iio_channel_attr_write_string(chn, what, str), what);
}
static char* get_ch_name(const char* type, int id)
{
snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
return tmpstr;
}
static struct iio_device* get_ad9361_phy(void)
{
struct iio_device *dev = iio_context_find_device(ctx, "ad9361-phy");
IIO_ENSURE(dev && "No ad9361-phy found");
return dev;
}
static bool get_ad9361_stream_dev(enum iodev d, struct iio_device **dev)
{
switch (d) {
case TX: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); return *dev != NULL;
case RX: *dev = iio_context_find_device(ctx, "cf-ad9361-lpc"); return *dev != NULL;
default: IIO_ENSURE(0); return false;
}
}
static bool get_ad9361_stream_ch(enum iodev d, struct iio_device *dev, int chid, struct iio_channel **chn)
{
*chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), d == TX);
if (!*chn)
*chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), d == TX);
return *chn != NULL;
}
static bool get_phy_chan(enum iodev d, int chid, struct iio_channel **chn)
{
switch (d) {
case RX: *chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("voltage", chid), false); return *chn != NULL;
case TX: *chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("voltage", chid), true); return *chn != NULL;
default: IIO_ENSURE(0); return false;
}
}
static bool get_lo_chan(enum iodev d, struct iio_channel **chn)
{
switch (d) {
case RX: *chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("altvoltage", 0), true); return *chn != NULL;
case TX: *chn = iio_device_find_channel(get_ad9361_phy(), get_ch_name("altvoltage", 1), true); return *chn != NULL;
default: IIO_ENSURE(0); return false;
}
}
bool cfg_ad9361_streaming_ch(struct stream_cfg *cfg, enum iodev type, int chid)
{
struct iio_channel *chn = NULL;
printf("* Acquiring AD9361 phy channel %d\n", chid);
if (!get_phy_chan(type, chid, &chn)) { return false; }
int ret = iio_channel_attr_write_string(chn, "rf_port_select", cfg->rfport);
if (ret == -EINVAL)
printf("Error writing %s to channel \"rf_port_select\"\nThis error can be ignored for PlutoSDR and derivatives.\n",
cfg->rfport);
else
errchk(ret, "rf_port_select");
wr_ch_lli(chn, "rf_bandwidth", cfg->bw_hz);
wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz);
printf("* Acquiring AD9361 %s lo channel\n", type == TX ? "TX" : "RX");
if (!get_lo_chan(type, &chn)) { return false; }
wr_ch_lli(chn, "frequency", cfg->lo_hz);
return true;
}
int main (int argc, char **argv)
{
struct iio_device *tx;
struct iio_device *rx;
size_t nrx = 0;
size_t ntx = 0;
size_t rx_sample_sz, tx_sample_sz;
struct stream_cfg rxcfg;
struct stream_cfg txcfg;
int err;
signal(SIGINT, handle_sig);
rxcfg.bw_hz = MHZ(2);
rxcfg.fs_hz = MHZ(2.5);
rxcfg.lo_hz = GHZ(2.5);
rxcfg.rfport = "A_BALANCED";
txcfg.bw_hz = MHZ(1.5);
txcfg.fs_hz = MHZ(2.5);
txcfg.lo_hz = GHZ(2.5);
txcfg.rfport = "A";
printf("* Acquiring IIO context\n");
if (argc == 1) {
IIO_ENSURE((ctx = iio_create_context(NULL, NULL)) && "No context");
}
else if (argc == 2) {
IIO_ENSURE((ctx = iio_create_context(NULL, argv[1])) && "No context");
}
IIO_ENSURE(iio_context_get_devices_count(ctx) > 0 && "No devices");
printf("* Acquiring AD9361 streaming devices\n");
IIO_ENSURE(get_ad9361_stream_dev(TX, &tx) && "No tx dev found");
IIO_ENSURE(get_ad9361_stream_dev(RX, &rx) && "No rx dev found");
printf("* Configuring AD9361 for streaming\n");
IIO_ENSURE(cfg_ad9361_streaming_ch(&rxcfg, RX, 0) && "RX port 0 not found");
IIO_ENSURE(cfg_ad9361_streaming_ch(&txcfg, TX, 0) && "TX port 0 not found");
printf("* Initializing AD9361 IIO streaming channels\n");
IIO_ENSURE(get_ad9361_stream_ch(RX, rx, 0, &rx0_i) && "RX chan i not found");
IIO_ENSURE(get_ad9361_stream_ch(RX, rx, 1, &rx0_q) && "RX chan q not found");
IIO_ENSURE(get_ad9361_stream_ch(TX, tx, 0, &tx0_i) && "TX chan i not found");
IIO_ENSURE(get_ad9361_stream_ch(TX, tx, 1, &tx0_q) && "TX chan q not found");
rxmask = iio_create_channels_mask(iio_device_get_channels_count(rx));
if (!rxmask) {
fprintf(stderr, "Unable to alloc channels mask\n");
shutdown();
}
txmask = iio_create_channels_mask(iio_device_get_channels_count(tx));
if (!txmask) {
fprintf(stderr, "Unable to alloc channels mask\n");
shutdown();
}
printf("* Enabling IIO streaming channels\n");
iio_channel_enable(rx0_i, rxmask);
iio_channel_enable(rx0_q, rxmask);
iio_channel_enable(tx0_i, txmask);
iio_channel_enable(tx0_q, txmask);
printf("* Creating non-cyclic IIO buffers with 1 MiS\n");
rxbuf = iio_device_create_buffer(rx, 0, rxmask);
err = iio_err(rxbuf);
if (err) {
rxbuf = NULL;
dev_perror(rx, err, "Could not create RX buffer");
shutdown();
}
txbuf = iio_device_create_buffer(tx, 0, txmask);
err = iio_err(txbuf);
if (err) {
txbuf = NULL;
dev_perror(tx, err, "Could not create TX buffer");
shutdown();
}
rxstream = iio_buffer_create_stream(rxbuf, 4, BLOCK_SIZE);
err = iio_err(rxstream);
if (err) {
rxstream = NULL;
dev_perror(rx, iio_err(rxstream), "Could not create RX stream");
shutdown();
}
txstream = iio_buffer_create_stream(txbuf, 4, BLOCK_SIZE);
err = iio_err(txstream);
if (err) {
txstream = NULL;
dev_perror(tx, iio_err(txstream), "Could not create TX stream");
shutdown();
}
tx_sample_sz = iio_device_get_sample_size(tx, rxmask);
rx_sample_sz = iio_device_get_sample_size(rx, txmask);
printf("* Starting IO streaming (press CTRL+C to cancel)\n");
stream(rx_sample_sz, tx_sample_sz, BLOCK_SIZE,
rxstream, txstream, rx0_i, tx0_i);
shutdown();
return 0;
}