Skip to content
rigexpert edited this page May 30, 2024 · 11 revisions

The driver API is what vendors write their hardware support modules with. To support hardware in SoapySDR, simply do the following:

  • Copy the ExampleDriver into your devices's build tree
  • Rename the "MyDevice" files and classes for your product
  • Implement overloads for a custom SoapySDR::Device class
  • Implement the discovery function to locate devices on the system
  • Implement the factory function to instantiate the device object

The registration API is the entry point into SoapySDR. Every device must register a discovery function that can find your device on the system; and a factory function that can instantiate an instance of your overloaded Device class.

The discovery function finds devices on the system that are supported by this driver. It returns "KwargsList" or a list of keyword-argument maps, one map for each discovered device. Each map identifies a device on the system with arbitrary keywords which can represent anything that helps to identify the device - like device nodes to serial numbers. Run SoapySDRUtil --find to print available devices discovered by your new function.

The discovery function takes hints in the form of an input keyword map. The implementation of the discovery function should use these as hints to filter for specific devices. For example, if an input keyword specified a serial, than the discovery function should only return results with that serial. Run SoapySDRUtil --find="key=value" to test the the filtering capabilities.

The factory function instantiates a device class instance using arguments from one of the discovered keyword-argument maps. The map should be used in a meaningful way to open the relevant internal device nodes, handles, or sockets. The function should return an instance of the overloaded device class. Run SoapySDRUtil --make="key=value" to test the factory function.

Notes:

  • Dont forget the static SoapySDR::Registry line, this tells SoapySDR about your discovery and factory functions.
  • Use a comma for multiple keywords on the command line: "key0=value0,key1=value1"

This section describes the necessary calls needed for creating drivers for SDR applications. Developers should overload these SoapySDR::Device class calls when creating hardware support modules to ensure that most SDR applications will be able to function properly. The organization below follows the same listing of API calls found in Device.hpp.

Fill in getDriverKey() and getHardwareKey(). These are simply string identifiers that applications may use to identify the driver and hardware to take device-specific actions.

Implement getNumChannels() and getFullDuplex(). You may also choose to implement setFrontendMapping() if your device supports configurable mappings between the RF frontend and the DSP units.

Implement setupStream(), closeStream(), activateStream(), deactivateStream(). Typically setup/close should handle lengthy allocation and cleanup procedures, while activate/deactivate may be called multiple times and should be lightweight on and off switches. In addition, activate also supports hooks for boards with timestamp and burst controls. Most streams should support the "CF32" (complex floats) and "CS16" (complex shorts) formats.

Implement readStream() and writeStream() based on device support for transmit and/or receive. For best results, never return more than the specified number of input elements, and always respect the specified timeout.

If your stream operates on underlying bounded buffers, then its best to implement getStreamMTU(). MTU stands for maximum transmission unit. Some SDR applications use this value to optimize the transfer sizes.

The getStreamFormats() and getNativeStreamFormat() calls are optional. However, they provide useful hints and using SoapyRemote, and are very straightforward to implement.

This entire section is optional. The direct buffer access API is an alternative to read/writeStream() that gives the caller direct access to DMA buffers (rather than passing in user buffers). Is not consistently implemented across all devices, and chiefly it is used by C and C++ API users. The buffer API is mentioned here, because if these calls are applicable to your device, you may want to implement them, and then implement the read/writeStream() calls on top of the buffer API. Several devices do this: SoapyRTLSDR, SoapyHackRF, SoapyEVB7.

Implement listAntennas() and setAntenna() for all available RF frontend switches. Some devices have no configurable RF switching and do not implement these calls.

Corrections are completely optional and board specific. Adding corrections hooks can be useful for python-based calibration apps.

Implement listGains(), setGain(dir, chan, name, value), and getGainRange(dir, chan, name) for every amplification and attenuation element used in the specified channel. Units are in dB.

Typically, attenuators are represented by a negative range such as [-18 to 0 dB], and LNAs and PAs are represented by a positive range such as [0 to 31 dB].

The setGain(dir, chan, value) variant attempts to set an overall gain across all elements in the channel. Overload this variant as well to implement a preferable distribution method.

Implement setFrequency(dir, chan, name, frequency, args), listFrequencies(), and getFrequencyRange(dir, chan, name) for every tunable element used in the specified channel. Units are in Hz. Many devices will use "RF" for the tunable LO in the RF front-end, and "BB" for a CORDIC in the digital baseband.

The setFrequency(dir, chan, frequency) variant attempts to set the overall frequency of the channel by tuning all elements. Overload this variant as well to implement a preferable distribution method.

The setSampleRate() call is a requirement. We also recommend implementing listSampleRates().

This section only applies if your device has configurable filters. If so, implement setBandwidth(), and getBandwidthRange() is recommended.

This section only applies if your device has configurable clocking. Implement setMasterClockRate() to support configurable clock rates. Implement listClockSources() and setClockSource() for selecting from multiple clock sources such as external references and TCXOs.

This section only applies if your device supports time synchronization. Implement hasHardwareTime(), getHardwareTime(), and setHardwareTime() for the device's hardware timestamp. Implement listTimeSources() and setTimeSource() for selecting from multiple timestamp synchronization sources.

The sensor API support arbitrary readback for status monitoring purposes like RSSI indicator and reference locked. Implement listSensors(), getSensorInfo(), and readSensor() for every supported sensor source.

Clone this wiki locally