Hi, I am working with the Linux IIO interface to directly access Pluto SDR hardware, aiming to minimize the internal processing delay as much as possible. While exploring the documentation, I came across the high-speed interface mmap()
, which is mentioned as being provided for local backend processing on the Pluto SDR-related website. However, I’ve encountered issues when trying to use mmap()
for signal transmission directly with the Linux IIO.
While I deploying the .c cross-compiled file to the Pluto SDR and try to make it running, it fails with an "Invalid argument" error, showing errno: 22 in the debug output.
I double-checked the parameters I’m passing to mmap()
:
- The length is an integer multiple of the page size.
- The file descriptor (
fd
) is a non-negative value (specifically, 3).
Still, I am unsure if I am using the mmap()
function correctly or if I’m missing something. Below is the source code for reference. Could you please help me identify the issue and let me know if I am using the high-speed interface properly?
Thank you very much for your help!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
// the system path for the devices
#define SYSFS_PATH_PHY_CORE "/sys/bus/iio/devices/iio:device0" // iio:device0 ==> for RF port, bandwidth, LO frequency configuration
#define SYSFS_PATH_DDS_CORE "/sys/bus/iio/devices/iio:device3" // iio:device3 ==> for signal transmission (DAC)
#define IIO_DEVICE_DDS_CORE "/dev/iio:device3" // The path for the DAC device, for mmap()
#define BUFFER_LENGTH 4096 // buffer size (configurable)
// Read sysfs properties
void read_sysfs(const char *path, char *value, size_t len)
{
FILE *f = fopen(path, "r");
if (!f)
{
perror("Failed to open sysfs file for reading");
exit(EXIT_FAILURE);
}
fgets(value, len, f);
fclose(f);
}
// write sysfs properties
void write_sysfs(const char *path, const char *value)
{
FILE *f = fopen(path, "w");
if (!f)
{
perror("Failed to open sysfs file for writing");
exit(EXIT_FAILURE);
}
fprintf(f, "%s", value);
fclose(f);
}
// get the page size
size_t get_page_size()
{
return sysconf(_SC_PAGESIZE);
}
// disable the previously scaned elements and buffers (if already been enabled)
void disable_previous_resources()
{
write_sysfs(SYSFS_PATH_DDS_CORE "/scan_elements/out_voltage0_en", "0");
write_sysfs(SYSFS_PATH_DDS_CORE "/scan_elements/out_voltage1_en", "0");
write_sysfs(SYSFS_PATH_DDS_CORE "/buffer/enable", "0");
}
// set the Tx LO frequency
void set_tx_lo_frequency(long long frequency)
{
char freq_str[32];
snprintf(freq_str, sizeof(freq_str), "%lld", frequency);
write_sysfs(SYSFS_PATH_PHY_CORE "/out_altvoltage1_TX_LO_frequency", freq_str);
}
// set the bandwidth for transmission
void set_tx_bandwidth(long long bandwidth)
{
char bw_str[32];
snprintf(bw_str, sizeof(bw_str), "%lld", bandwidth);
write_sysfs(SYSFS_PATH_PHY_CORE "/out_voltage_rf_bandwidth", bw_str);
}
// set the transmission port
void set_tx_port(const char *port)
{
write_sysfs(SYSFS_PATH_PHY_CORE "/out_voltage0_rf_port_select", port);
}
// enable DDS for transmission
void enable_dds_core()
{
write_sysfs(SYSFS_PATH_DDS_CORE "/scan_elements/out_voltage0_en", "1");
write_sysfs(SYSFS_PATH_DDS_CORE "/scan_elements/out_voltage1_en", "1");
}
// disable DDS for transmission
void disable_dds_core()
{
write_sysfs(SYSFS_PATH_DDS_CORE "/scan_elements/out_voltage0_en", "0");
write_sysfs(SYSFS_PATH_DDS_CORE "/scan_elements/out_voltage1_en", "0");
}
// enable the buffer
void enable_buffer()
{
write_sysfs(SYSFS_PATH_DDS_CORE "/buffer/length", "4096"); // set the buffer size (integer multiple of page size)
write_sysfs(SYSFS_PATH_DDS_CORE "/buffer/enable", "1"); // enable the buffer
}
// disable the buffer
void disable_buffer()
{
write_sysfs(SYSFS_PATH_DDS_CORE "/buffer/enable", "0"); // write to disable the buffer
}
// using mmap() to transmit the data
void *mmap_device(int fd, size_t length)
{
// get the page size to make sure the buffer size is the integer multiple of the page size
size_t page_size = get_page_size();
if (length % page_size != 0)
{
fprintf(stderr, "Buffer length must be a multiple of the page size (%zu)\n", page_size);
close(fd);
exit(EXIT_FAILURE);
}
// using mmap() to map the device memory to the user space
void *mapped_addr = mmap(NULL, length, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
if (mapped_addr == MAP_FAILED)
{
perror("Failed to mmap IIO device buffer");
printf("errno: %d\n", errno);
close(fd);
exit(EXIT_FAILURE);
}
return mapped_addr;
}
// define the signal to be transmitted (sine-wave)
void generate_signal(int16_t *buffer, size_t samples, double frequency, double sample_rate)
{
double phase = 0.0;
double phase_increment = 2 * M_PI * frequency / sample_rate;
// generate the I/Q samples for the sine-wave signal
for (size_t i = 0; i < samples; i++)
{
buffer[2 * i] = (int16_t)(32767.0 * sin(phase)); // I
buffer[2 * i + 1] = (int16_t)(32767.0 * cos(phase)); // Q
phase += phase_increment;
if (phase >= 2 * M_PI)
{
phase -= 2 * M_PI;
}
}
}
int main()
{
// disable the previous resources to avoid device busy problems
disable_previous_resources();
// set the TX LO frequency to be 400 MHz
set_tx_lo_frequency(400000000LL);
// set the transmission bandwidth to 20 MHz
set_tx_bandwidth(20000000LL);
// A set the transmission port, "A"
set_tx_port("A");
// enable the DAC-DDS for transmission
enable_dds_core();
// enable the buffer
enable_buffer();
// open the DAC-DDS core to get the file descriptor
int fd = open(IIO_DEVICE_DDS_CORE, O_RDWR | O_NONBLOCK);
if (fd < 0)
{
perror("Failed to open IIO device");
disable_dds_core(); // disable DAC-DDS once failing to open DAC-DDS
return EXIT_FAILURE;
}
// get the page size and set the mapping length
size_t page_size = get_page_size();
size_t length = BUFFER_LENGTH * sizeof(int16_t) * 2;
// make sure the buffer size is the integer times of the page size
if (length % page_size != 0)
{
length = ((length / page_size) + 1) * page_size;
}
// using mmap to map the device buffer to the user space
int16_t *buffer = (int16_t *)mmap_device(fd, length);
// set the frequency = 1kHz, sample rate = 60 Msps
double frequency = 1000.0; // 1kHz
double sample_rate = 60000000.0; // 60 Msps
generate_signal(buffer, BUFFER_LENGTH / 2, frequency, sample_rate);
printf("Starting to transmit data...\n");
// unmap, disable the buffer and DAC-DDS core
munmap(buffer, length); // unmap
disable_buffer();
disable_dds_core();
close(fd);
return 0;
}