Create your hardware in libsigrok
2025-08-03
In short, you need to build libsigrok then sigrok-cli
Step 1: Add your device to Makefile.am
if HW_QUANTR
src_libdrivers_la_SOURCES += \
src/hardware/quantr/protocol.h \
src/hardware/quantr/protocol.c \
src/hardware/quantr/api.c
endif

Step 2: in configure.ac
SR_DRIVER([Quantr], [quantr], [serial_comm])
Step 3: Create /src/hardware/quantr folder
add api.c, protocol.c and protocol.h
api.c:
#include <config.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
#include "protocol.h"
static const uint32_t scanopts[] = {
SR_CONF_CONN,
SR_CONF_SERIALCOMM,
};
static const uint32_t devopts[] = {
SR_CONF_LOGIC_ANALYZER,
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
static const uint64_t samplerates[] = {
SR_KHZ(1),
SR_KHZ(10),
SR_KHZ(100),
SR_MHZ(1),
SR_MHZ(10),
};
SR_PRIV struct sr_dev_driver quantr_driver_info = {
.name = "quantr",
.longname = "Quantr Device",
.api_version = 1,
.init = quantr_init,
.cleanup = quantr_cleanup,
.scan = quantr_scan,
.dev_list = quantr_dev_list,
.dev_clear = quantr_dev_clear,
.config_get = quantr_config_get,
.config_set = quantr_config_set,
.config_list = quantr_config_list,
.dev_open = quantr_dev_open,
.dev_close = quantr_dev_close,
.dev_acquisition_start = quantr_dev_acquisition_start,
.dev_acquisition_stop = quantr_dev_acquisition_stop,
.context = NULL,
};
SR_REGISTER_DEV_DRIVER(quantr_driver_info);
protocol.c:
#include "protocol.h"
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
#define LOG_PREFIX "quantr"
static const uint32_t scanopts[] = {
SR_CONF_CONN,
SR_CONF_SERIALCOMM,
};
static const uint32_t devopts[] = {
SR_CONF_LOGIC_ANALYZER,
SR_CONF_SAMPLERATE | SR_CONF_GET | SR_CONF_SET | SR_CONF_LIST,
};
static const uint64_t samplerates[] = {
SR_KHZ(1),
SR_KHZ(10),
SR_KHZ(100),
SR_MHZ(1),
SR_MHZ(10),
};
/* Initialize the driver */
SR_PRIV int quantr_init(struct sr_dev_driver *di, struct sr_context *sr_ctx)
{
return std_init(di, sr_ctx);
}
/* Cleanup resources */
SR_PRIV int quantr_cleanup(const struct sr_dev_driver *di)
{
return std_cleanup(di);
}
/* Scan for devices */
SR_PRIV GSList *quantr_scan(struct sr_dev_driver *di, GSList *options)
{
GSList *devices = NULL;
struct sr_dev_inst *sdi;
struct dev_context *devc;
struct sr_serial_dev_inst *serial;
const char *conn;
const char *serialcomm;
(void)di;
/* Parse options */
conn = NULL;
serialcomm = "9600/8n1"; /* Default serial settings */
if (sr_serial_extract_options(options, &conn, &serialcomm) != SR_OK)
return NULL;
if (!conn)
return NULL;
/* Try to open the serial port */
serial = sr_serial_dev_inst_new(conn, serialcomm);
if (!serial)
return NULL;
if (serial_open(serial, SERIAL_RDWR) != SR_OK) {
sr_serial_dev_inst_free(serial);
return NULL;
}
/* Try to communicate with the device to verify it's there */
/* You would typically send an identification command here */
/* Create device instance */
sdi = g_malloc0(sizeof(struct sr_dev_inst));
sdi->status = SR_ST_INACTIVE;
sdi->vendor = g_strdup("Quantr");
sdi->model = g_strdup("Device");
sdi->inst_type = SR_INST_SERIAL;
sdi->conn = serial;
if (!sdi) {
serial_close(serial);
sr_serial_dev_inst_free(serial);
return NULL;
}
devc = g_malloc0(sizeof(struct dev_context));
devc->serial = serial;
devc->samplerate = 1000000; /* Default 1MHz */
devc->num_channels = 8; /* Default 8 channels */
devc->buffer_size = 4096;
devc->buffer = g_malloc(devc->buffer_size);
sdi->priv = devc;
serial_close(serial);
devices = g_slist_append(devices, sdi);
return devices;
}
/* List devices */
SR_PRIV GSList *quantr_dev_list(const struct sr_dev_driver *di)
{
return ((struct sr_dev_driver *)di)->context;
}
/* Clear device instances */
SR_PRIV int quantr_dev_clear(const struct sr_dev_driver *di)
{
return std_dev_clear(di);
}
/* Get configuration */
SR_PRIV int quantr_config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc = sdi->priv;
(void)cg;
switch (key) {
case SR_CONF_SAMPLERATE:
*data = g_variant_new_uint64(devc->samplerate);
return SR_OK;
default:
return SR_ERR_NA;
}
}
/* Set configuration */
SR_PRIV int quantr_config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
struct dev_context *devc = sdi->priv;
(void)cg;
if (sdi->status != SR_ST_ACTIVE)
return SR_ERR_DEV_CLOSED;
switch (key) {
case SR_CONF_SAMPLERATE:
devc->samplerate = g_variant_get_uint64(data);
return SR_OK;
default:
return SR_ERR_NA;
}
}
/* List configuration options */
SR_PRIV int quantr_config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg)
{
(void)sdi;
(void)cg;
switch (key) {
case SR_CONF_SCAN_OPTIONS:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
scanopts, ARRAY_SIZE(scanopts), sizeof(uint32_t));
return SR_OK;
case SR_CONF_DEVICE_OPTIONS:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
devopts, ARRAY_SIZE(devopts), sizeof(uint32_t));
return SR_OK;
case SR_CONF_SAMPLERATE:
*data = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT64,
samplerates, ARRAY_SIZE(samplerates), sizeof(uint64_t));
return SR_OK;
default:
return SR_ERR_NA;
}
}
/* Open device */
SR_PRIV int quantr_dev_open(struct sr_dev_inst *sdi)
{
struct dev_context *devc = sdi->priv;
int ret;
ret = serial_open(devc->serial, SERIAL_RDWR);
if (ret != SR_OK)
return ret;
sdi->status = SR_ST_ACTIVE;
return SR_OK;
}
/* Close device */
SR_PRIV int quantr_dev_close(struct sr_dev_inst *sdi)
{
struct dev_context *devc = sdi->priv;
if (devc->serial) {
serial_close(devc->serial);
}
sdi->status = SR_ST_INACTIVE;
return SR_OK;
}
/* Start acquisition */
SR_PRIV int quantr_dev_acquisition_start(const struct sr_dev_inst *sdi)
{
struct dev_context *devc = sdi->priv;
int ret;
if (!devc->serial)
return SR_ERR;
/* Send start command to device via serial port */
ret = serial_write_blocking(devc->serial, "START\n", 6, 1000);
if (ret < 0)
return SR_ERR;
devc->acquisition_running = 1;
/* Set up polling or event-driven data reading here */
/* For now, this is just a skeleton */
return SR_OK;
}
/* Stop acquisition */
SR_PRIV int quantr_dev_acquisition_stop(struct sr_dev_inst *sdi)
{
struct dev_context *devc = sdi->priv;
int ret;
if (!devc->serial || !devc->acquisition_running)
return SR_OK;
/* Send stop command to device via serial port */
ret = serial_write_blocking(devc->serial, "STOP\n", 5, 1000);
if (ret < 0)
return SR_ERR;
devc->acquisition_running = 0;
return SR_OK;
}
protocol.h:
#ifndef LIBSIGROK_HARDWARE_QUANTR_PROTOCOL_H
#define LIBSIGROK_HARDWARE_QUANTR_PROTOCOL_H
#include <stdint.h>
#include <libsigrok/libsigrok.h>
#include "libsigrok-internal.h"
/* Device-specific context */
struct dev_context {
struct sr_serial_dev_inst *serial;
uint64_t samplerate;
int num_channels;
uint8_t *buffer;
size_t buffer_size;
int acquisition_running;
};
/* Function prototypes */
SR_PRIV int quantr_init(struct sr_dev_driver *di, struct sr_context *sr_ctx);
SR_PRIV int quantr_cleanup(const struct sr_dev_driver *di);
SR_PRIV GSList *quantr_scan(struct sr_dev_driver *di, GSList *options);
SR_PRIV GSList *quantr_dev_list(const struct sr_dev_driver *di);
SR_PRIV int quantr_dev_clear(const struct sr_dev_driver *di);
SR_PRIV int quantr_config_get(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg);
SR_PRIV int quantr_config_set(uint32_t key, GVariant *data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg);
SR_PRIV int quantr_config_list(uint32_t key, GVariant **data, const struct sr_dev_inst *sdi, const struct sr_channel_group *cg);
SR_PRIV int quantr_dev_open(struct sr_dev_inst *sdi);
SR_PRIV int quantr_dev_close(struct sr_dev_inst *sdi);
SR_PRIV int quantr_dev_acquisition_start(const struct sr_dev_inst *sdi);
SR_PRIV int quantr_dev_acquisition_stop(struct sr_dev_inst *sdi);
#endif
Step 4: Build libsigrok
./autogen.sh
./configure
make -j
sudo make install
Step 5: Build sigrok-cli
./autogen.sh
./configure
make -j
sudo make install
Step 6: Test
Then test it by "sigrok-cli -L", you should able to see "Quantr Device"
!!! if it linked to wrong libusb, use "sudo install_name_tool -change /src/staging/libusb/macos64/lib/libusb-1.0.0.dylib /usr/local/lib/libusb-1.0.0.dylib /usr/local/lib/libsigrok.4.dylib" to fix it

sigrok-cli --driver quantr:conn=/dev/tty.usbmodem144401 --scan
sigrok-cli --driver quantr:conn=/dev/tty.usbmodem144401:serialcomm=2000000/8n1 --scan
sigrok-cli --driver quantr:conn=/dev/tty.usbmodem3788388D30321:serialcomm=2000000/8n1 --channels CH0=Vcc,CH1=CS,CH2=MISO,CH3=MOSI,CH4=CLK --output-format binary --config samplerate=50m --continuous

sigrok-cli --driver quantr:conn=/dev/tty.usbmodem3864356A32321 --samples 100 --config samplerate=5000 -O ascii:width=100:charset='_"\/'

sigrok-cli --driver quantr:conn=/dev/tty.usbmodem3864356A32321 --samples 100 --config samplerate=5000 -O bits:width=100

Specific channels
sudo sigrok-cli -d quantr::conn=/dev/tty.usbmodem3864356A32321 -P quantr-sigrok-decoder:freq=100k:data0=CH16:data1=CH17:data2=CH18:data3=CH19:data4=CH20:data5=CH21:data6=CH22:data7=CH23 --samples 100 --config samplerate=100kHz -O bits:width=100 -A quantr-sigrok-decoder=hex