static int get_options(struct srd_decoder *d)
{
- PyObject *py_opts, *py_keys, *py_values, *py_val, *py_desc, *py_default;
- Py_ssize_t i;
+ PyObject *py_opts, *py_opt, *py_val, *py_default, *py_item;
+ Py_ssize_t opt, i;
struct srd_decoder_option *o;
- gint64 def_long;
- int num_keys, overflow, ret;
- char *key, *def_str;
-
- ret = SRD_ERR_PYTHON;
- key = NULL;
+ GVariant *gvar;
+ gint64 lval;
+ double dval;
+ int overflow;
+ char *sval;
if (!PyObject_HasAttrString(d->py_dec, "options"))
- /* That's fine. */
+ /* No options, that's fine. */
return SRD_OK;
- /* If present, options must be a dictionary. */
+ /* If present, options must be a tuple. */
py_opts = PyObject_GetAttrString(d->py_dec, "options");
- if (!PyDict_Check(py_opts)) {
- srd_err("Protocol decoder %s options attribute is not "
- "a dictionary.", d->name);
- goto err_out;
+ if (!PyTuple_Check(py_opts)) {
+ srd_err("Protocol decoder %s: options attribute is not "
+ "a tuple.", d->id);
+ return SRD_ERR_PYTHON;
}
- py_keys = PyDict_Keys(py_opts);
- py_values = PyDict_Values(py_opts);
- num_keys = PyList_Size(py_keys);
- for (i = 0; i < num_keys; i++) {
- py_str_as_str(PyList_GetItem(py_keys, i), &key);
- srd_dbg("option '%s'", key);
- py_val = PyList_GetItem(py_values, i);
- if (!PyList_Check(py_val) || PyList_Size(py_val) != 2) {
- srd_err("Protocol decoder %s option '%s' value must be "
- "a list with two elements.", d->name, key);
- goto err_out;
- }
- py_desc = PyList_GetItem(py_val, 0);
- if (!PyUnicode_Check(py_desc)) {
- srd_err("Protocol decoder %s option '%s' has no "
- "description.", d->name, key);
- goto err_out;
+ for (opt = 0; opt < PyTuple_Size(py_opts); opt++) {
+ py_opt = PyTuple_GetItem(py_opts, opt);
+ if (!PyDict_Check(py_opt)) {
+ srd_err("Protocol decoder %s options: each option "
+ "must consist of a dictionary.", d->name);
+ return SRD_ERR_PYTHON;
}
- py_default = PyList_GetItem(py_val, 1);
- if (!PyUnicode_Check(py_default) && !PyLong_Check(py_default)) {
- srd_err("Protocol decoder %s option '%s' has default "
- "of unsupported type '%s'.", d->name, key,
- Py_TYPE(py_default)->tp_name);
- goto err_out;
+ if (!(py_val = PyDict_GetItemString(py_opt, "id"))) {
+ srd_err("Protocol decoder %s option %d has no "
+ "id.", d->name);
+ return SRD_ERR_PYTHON;
}
- if (!(o = g_try_malloc(sizeof(struct srd_decoder_option)))) {
- srd_err("option malloc failed");
- goto err_out;
+ o = g_malloc0(sizeof(struct srd_decoder_option));
+ py_str_as_str(py_val, &o->id);
+
+ if ((py_val = PyDict_GetItemString(py_opt, "desc")))
+ py_str_as_str(py_val, &o->desc);
+
+ if ((py_default = PyDict_GetItemString(py_opt, "default"))) {
+ if (PyUnicode_Check(py_default)) {
+ /* UTF-8 string */
+ py_str_as_str(py_default, &sval);
+ o->def = g_variant_new_string(sval);
+ g_free(sval);
+ } else if (PyLong_Check(py_default)) {
+ /* Long */
+ lval = PyLong_AsLongAndOverflow(py_default, &overflow);
+ if (overflow) {
+ /* Value is < LONG_MIN or > LONG_MAX */
+ PyErr_Clear();
+ srd_err("Protocol decoder %s option 'default' has "
+ "invalid default value.", d->name);
+ return SRD_ERR_PYTHON;
+ }
+ o->def = g_variant_new_int64(lval);
+ } else if (PyFloat_Check(py_default)) {
+ /* Float */
+ if ((dval = PyFloat_AsDouble(py_default)) == -1.0) {
+ PyErr_Clear();
+ srd_err("Protocol decoder %s option 'default' has "
+ "invalid default value.", d->name);
+ return SRD_ERR_PYTHON;
+ }
+ o->def = g_variant_new_double(dval);
+ } else {
+ srd_err("Protocol decoder %s option 'default' has "
+ "value of unsupported type '%s'.", d->name,
+ Py_TYPE(py_default)->tp_name);
+ return SRD_ERR_PYTHON;
+ }
+ g_variant_ref_sink(o->def);
}
- o->id = g_strdup(key);
- py_str_as_str(py_desc, &o->desc);
- if (PyUnicode_Check(py_default)) {
- /* UTF-8 string */
- py_str_as_str(py_default, &def_str);
- o->def = g_variant_new_string(def_str);
- g_free(def_str);
- } else {
- /* Long */
- def_long = PyLong_AsLongAndOverflow(py_default, &overflow);
- if (overflow) {
- /* Value is < LONG_MIN or > LONG_MAX */
- PyErr_Clear();
- srd_err("Protocol decoder %s option '%s' has "
- "invalid default value.", d->name, key);
- goto err_out;
+
+ if ((py_val = PyDict_GetItemString(py_opt, "values"))) {
+ /* A default is required if a list of values is
+ * given, since it's used to verify their type. */
+ if (!o->def) {
+ srd_err("No default for option '%s'", o->id);
+ return SRD_ERR_PYTHON;
+ }
+ if (!PyTuple_Check(py_val)) {
+ srd_err("Option '%s' values should be a tuple.", o->id);
+ return SRD_ERR_PYTHON;
+ }
+ for (i = 0; i < PyTuple_Size(py_val); i++) {
+ py_item = PyTuple_GetItem(py_val, i);
+ if (Py_TYPE(py_default) != Py_TYPE(py_item)) {
+ srd_err("All values for option '%s' must be "
+ "of the same type as the default.",
+ o->id);
+ return SRD_ERR_PYTHON;
+ }
+ if (PyUnicode_Check(py_item)) {
+ /* UTF-8 string */
+ py_str_as_str(py_item, &sval);
+ gvar = g_variant_new_string(sval);
+ g_variant_ref_sink(gvar);
+ g_free(sval);
+ o->values = g_slist_append(o->values, gvar);
+ } else if (PyLong_Check(py_item)) {
+ /* Long */
+ lval = PyLong_AsLongAndOverflow(py_default, &overflow);
+ if (overflow) {
+ /* Value is < LONG_MIN or > LONG_MAX */
+ PyErr_Clear();
+ srd_err("Protocol decoder %s option 'values' "
+ "has invalid value.", d->name);
+ return SRD_ERR_PYTHON;
+ }
+ gvar = g_variant_new_int64(lval);
+ g_variant_ref_sink(gvar);
+ o->values = g_slist_append(o->values, gvar);
+ } else if (PyFloat_Check(py_default)) {
+ /* Float */
+ if ((dval = PyFloat_AsDouble(py_default)) == -1.0) {
+ PyErr_Clear();
+ srd_err("Protocol decoder %s option 'default' has "
+ "invalid default value.", d->name);
+ return SRD_ERR_PYTHON;
+ }
+ gvar = g_variant_new_double(dval);
+ g_variant_ref_sink(gvar);
+ o->values = g_slist_append(o->values, gvar);
+ }
}
- o->def = g_variant_new_int64(def_long);
}
- g_variant_ref_sink(o->def);
d->options = g_slist_append(d->options, o);
- g_free(key);
- key = NULL;
}
- Py_DecRef(py_keys);
- Py_DecRef(py_values);
-
- ret = SRD_OK;
-
-err_out:
- Py_XDECREF(py_opts);
- g_free(key);
- return ret;
+ return SRD_OK;
}
/**
}
Py_CLEAR(py_method);
+ /* Store required fields in newly allocated strings. */
+ if (py_attr_as_str(d->py_dec, "id", &(d->id)) != SRD_OK)
+ goto err_out;
+
+ if (py_attr_as_str(d->py_dec, "name", &(d->name)) != SRD_OK)
+ goto err_out;
+
+ if (py_attr_as_str(d->py_dec, "longname", &(d->longname)) != SRD_OK)
+ goto err_out;
+
+ if (py_attr_as_str(d->py_dec, "desc", &(d->desc)) != SRD_OK)
+ goto err_out;
+
+ if (py_attr_as_str(d->py_dec, "license", &(d->license)) != SRD_OK)
+ goto err_out;
+
+ /* All options and their default values. */
if (get_options(d) != SRD_OK)
goto err_out;
p->order += g_slist_length(d->probes);
}
- /* Store required fields in newly allocated strings. */
- if (py_attr_as_str(d->py_dec, "id", &(d->id)) != SRD_OK)
- goto err_out;
-
- if (py_attr_as_str(d->py_dec, "name", &(d->name)) != SRD_OK)
- goto err_out;
-
- if (py_attr_as_str(d->py_dec, "longname", &(d->longname)) != SRD_OK)
- goto err_out;
-
- if (py_attr_as_str(d->py_dec, "desc", &(d->desc)) != SRD_OK)
- goto err_out;
-
- if (py_attr_as_str(d->py_dec, "license", &(d->license)) != SRD_OK)
- goto err_out;
-
/* Convert annotation class attribute to GSList of char **. */
d->annotations = NULL;
if (PyObject_HasAttrString(d->py_dec, "annotations")) {
{'id': 'can_rx', 'name': 'CAN RX', 'desc': 'CAN bus line'},
]
optional_probes = []
- options = {
- 'bitrate': ['Bitrate', 1000000], # 1Mbit/s
- 'sample_point': ['Sample point', 70], # 70%
- }
+ options = (
+ {'id': 'bitrate', 'desc': 'Bitrate', 'default': 1000000}, # 1Mbit/s
+ {'id': 'sample_point', 'desc': 'Sample point', 'default': 70}, # 70%
+ )
annotations = [
['data', 'CAN payload data'],
['sof', 'Start of frame'],
{'id': 'sda', 'name': 'SDA', 'desc': 'Serial data line'},
]
optional_probes = []
- options = {
- 'address_format': ['Displayed slave address format', 'shifted'],
- }
+ options = (
+ {'id': 'address_format', 'desc': 'Displayed slave address format',
+ 'default': 'shifted', 'values': ('shifted', 'unshifted')},
+ )
annotations = [
['start', 'Start condition'],
['repeat-start', 'Repeat start condition'],
outputs = ['i2c']
probes = []
optional_probes = []
- options = {
- 'address': ['Address to filter out of the I²C stream', 0],
- 'direction': ['Direction to filter (read/write/both)', 'both']
- }
+ options = (
+ {'id': 'address', 'desc': 'Address to filter out of the I²C stream',
+ 'default': 0},
+ {'id': 'direction', 'desc': 'Direction to filter', 'default': 'both',
+ 'values': ('read', 'write', 'both')}
+ )
annotations = []
def __init__(self, **kwargs):
{'id': 'a1', 'name': 'A1', 'desc': 'I²C slave address input 1'},
{'id': 'a2', 'name': 'A2', 'desc': 'I²C slave address input 2'},
]
- options = {
- 'sensor': ['Sensor type', 'lm75'],
- 'resolution': ['Resolution', 9], # 9-12 bit, sensor/config dependent
- }
+ options = (
+ {'id': 'sensor', 'desc': 'Sensor type', 'default': 'lm75'},
+ {'id': 'resolution', 'desc': 'Resolution', 'default': 9,
+ 'values': (9, 10, 11, 12)},
+ )
annotations = [
['celsius', 'Temperature in degrees Celsius'],
['kelvin', 'Temperature in Kelvin'],
optional_probes = [
{'id': 'pwr', 'name': 'PWR', 'desc': '1-Wire power supply pin'},
]
- options = {
- 'overdrive': ['Overdrive mode', 'no'],
+ options = (
+ {'id': 'overdrive',
+ 'desc': 'Overdrive mode',
+ 'default': 'no'},
# Time options (specified in microseconds):
- 'cnt_normal_bit': ['Normal mode sample bit time (µs)', 15],
- 'cnt_normal_slot': ['Normal mode data slot time (µs)', 60],
- 'cnt_normal_presence': ['Normal mode sample presence time (µs)', 75],
- 'cnt_normal_reset': ['Normal mode reset time (µs)', 480],
- 'cnt_overdrive_bit': ['Overdrive mode sample bit time (µs)', 2],
- # 'cnt_overdrive_slot': ['Overdrive mode data slot time (µs)', 7.3],
- 'cnt_overdrive_slot': ['Overdrive mode data slot time (µs)', 7],
- 'cnt_overdrive_presence': ['Overdrive mode sample presence time (µs)', 10],
- 'cnt_overdrive_reset': ['Overdrive mode reset time (µs)', 48],
- }
+ {'id': 'cnt_normal_bit',
+ 'desc': 'Normal mode sample bit time (μs)',
+ 'default': 15},
+ {'id': 'cnt_normal_slot',
+ 'desc': 'Normal mode data slot time (μs)',
+ 'default': 60},
+ {'id': 'cnt_normal_presence',
+ 'desc': 'Normal mode sample presence time (μs)',
+ 'default': 75},
+ {'id': 'cnt_normal_reset',
+ 'desc': 'Normal mode reset time (μs)',
+ 'default': 480},
+ {'id': 'cnt_overdrive_bit',
+ 'desc': 'Overdrive mode sample bit time (μs)',
+ 'default': 2},
+ {'id': 'cnt_overdrive_slot',
+ 'desc': 'Overdrive mode data slot time (μs)',
+ 'default': 7.3},
+ {'id': 'cnt_overdrive_presence',
+ 'desc': 'Overdrive mode sample presence time (μs)',
+ 'default': 10},
+ {'id': 'cnt_overdrive_reset',
+ 'desc': 'Overdrive mode reset time (μs)',
+ 'default': 48},
+ )
annotations = [
['bit', 'Bit'],
['warnings', 'Warnings'],
outputs = ['parallel']
probes = []
optional_probes = probe_list(8)
- options = {
- 'clock_edge': ['Clock edge to sample on', 'rising'],
- 'wordsize': ['Word size of the data', 1],
- 'endianness': ['Endianness of the data', 'little'],
- 'format': ['Data format', 'hex'],
- }
+ options = (
+ {'id': 'clock_edge', 'desc': 'Clock edge to sample on',
+ 'default': 'rising', 'values': ('rising', 'falling')},
+ {'id': 'wordsize', 'desc': 'Word size of the data',
+ 'default': 1},
+ {'id': 'endianness', 'desc': 'Endianness of the data',
+ 'default': 'little', 'values': ('little', 'big')},
+ )
annotations = [
['items', 'Items'],
['words', 'Words'],
{'id': 'mosi', 'name': 'MOSI', 'desc': 'Master out, slave in'},
{'id': 'cs', 'name': 'CS#', 'desc': 'Chip-select'},
]
- options = {
- 'cs_polarity': ['CS# polarity', 'active-low'],
- 'cpol': ['Clock polarity', 0],
- 'cpha': ['Clock phase', 0],
- 'bitorder': ['Bit order within the SPI data', 'msb-first'],
- 'wordsize': ['Word size of SPI data', 8], # 1-64?
- 'format': ['Data format', 'hex'],
- }
+ options = (
+ {'id': 'cs_polarity', 'desc': 'CS# polarity', 'default': 'active-low',
+ 'values': ('active-low', 'active-high')},
+ {'id': 'cpol', 'desc': 'Clock polarity', 'default': 0,
+ 'values': (0, 1)},
+ {'id': 'cpha', 'desc': 'Clock phase', 'default': 0,
+ 'values': (0, 1)},
+ {'id': 'bitorder', 'desc': 'Bit order within the SPI data',
+ 'default': 'msb-first', 'values': ('msb-first', 'lsb-first')},
+ {'id': 'wordsize', 'desc': 'Word size of SPI data', 'default': 8},
+ )
annotations = [
['miso-data', 'MISO data'],
['mosi-data', 'MOSI data'],
{'id': 'rx', 'name': 'RX', 'desc': 'UART receive line'},
{'id': 'tx', 'name': 'TX', 'desc': 'UART transmit line'},
]
- options = {
- 'baudrate': ['Baud rate', 115200],
- 'num_data_bits': ['Data bits', 8], # Valid: 5-9.
- 'parity_type': ['Parity type', 'none'],
- 'parity_check': ['Check parity?', 'yes'], # TODO: Bool supported?
- 'num_stop_bits': ['Stop bit(s)', '1'], # String! 0, 0.5, 1, 1.5.
- 'bit_order': ['Bit order', 'lsb-first'],
- 'format': ['Data format', 'ascii'], # ascii/dec/hex/oct/bin
+ options = (
+ {'id': 'baudrate', 'desc': 'Baud rate', 'default': 115200},
+ {'id': 'num_data_bits', 'desc': 'Data bits', 'default': 8,
+ 'values': (5, 6, 7, 8, 9)},
+ {'id': 'parity_type', 'desc': 'Parity type', 'default': 'none',
+ 'values': ('none', 'odd', 'even', 'zero', 'one')},
+ {'id': 'parity_check', 'desc': 'Check parity?', 'default': 'yes',
+ 'values': ('yes', 'no')},
+ {'id': 'num_stop_bits', 'desc': 'Stop bits', 'default': 1.0,
+ 'values': (0.0, 0.5, 1.0, 1.5)},
+ {'id': 'bit_order', 'desc': 'Bit order', 'default': 'lsb-first',
+ 'values': ('lsb-first', 'msb-first')},
+ {'id': 'format', 'desc': 'Data format', 'default': 'ascii',
+ 'values': ('ascii', 'dec', 'hex', 'oct', 'bin')},
# TODO: Options to invert the signal(s).
- }
+ )
annotations = [
['rx-data', 'RX data'],
['tx-data', 'TX data'],
outputs = ['usb_packet']
probes = []
optional_probes = []
- options = {
- 'signalling': ['Signalling', 'full-speed'],
- }
+ options = (
+ {'id': 'signalling', 'desc': 'Signalling', 'default': 'full-speed'},
+ )
annotations = [
['sync-ok', 'SYNC'],
['sync-err', 'SYNC (error)'],
{'id': 'dm', 'name': 'D-', 'desc': 'USB D- signal'},
]
optional_probes = []
- options = {
- 'signalling': ['Signalling', 'full-speed'],
- }
+ options = (
+ {'id': 'signalling', 'desc': 'Signalling',
+ 'default': 'full-speed', 'values': ('full-speed', 'low-speed')},
+ )
annotations = [
['sym', 'Symbol'],
['sop', 'Start of packet (SOP)'],
SRD_API int srd_inst_option_set(struct srd_decoder_inst *di,
GHashTable *options)
{
- PyObject *py_dec_options, *py_dec_optkeys, *py_di_options, *py_optval;
- PyObject *py_optlist, *py_classval;
- Py_UNICODE *py_ustr;
+ struct srd_decoder_option *sdo;
+ PyObject *py_di_options, *py_optval;
GVariant *value;
- unsigned long long int val_ull;
+ GSList *l;
+ double val_double;
gint64 val_int;
- int num_optkeys, ret, size, i;
+ int ret;
const char *val_str;
- char *dbg, *key;
if (!di) {
srd_err("Invalid decoder instance.");
}
ret = SRD_ERR_PYTHON;
- key = NULL;
- py_dec_options = py_dec_optkeys = py_di_options = py_optval = NULL;
- py_optlist = py_classval = NULL;
- py_dec_options = PyObject_GetAttrString(di->decoder->py_dec, "options");
-
- /* All of these are synthesized objects, so they're good. */
- py_dec_optkeys = PyDict_Keys(py_dec_options);
- num_optkeys = PyList_Size(py_dec_optkeys);
+ py_optval = NULL;
/*
- * The 'options' dictionary is a class variable, but we need to
+ * The 'options' tuple is a class variable, but we need to
* change it. Changing it directly will affect the entire class,
* so we need to create a new object for it, and populate that
* instead.
Py_DECREF(py_di_options);
py_di_options = PyDict_New();
PyObject_SetAttrString(di->py_inst, "options", py_di_options);
- for (i = 0; i < num_optkeys; i++) {
- /* Get the default class value for this option. */
- py_str_as_str(PyList_GetItem(py_dec_optkeys, i), &key);
- if (!(py_optlist = PyDict_GetItemString(py_dec_options, key)))
- goto err_out;
- if (!(py_classval = PyList_GetItem(py_optlist, 1)))
- goto err_out;
- if (!PyUnicode_Check(py_classval) && !PyLong_Check(py_classval)) {
- srd_err("Options of type %s are not yet supported.",
- Py_TYPE(py_classval)->tp_name);
- goto err_out;
- }
-
- if ((value = g_hash_table_lookup(options, key))) {
- dbg = g_variant_print(value, TRUE);
- srd_dbg("got option '%s' = %s", key, dbg);
- g_free(dbg);
- /* An override for this option was provided. */
- if (PyUnicode_Check(py_classval)) {
- if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
- srd_err("Option '%s' requires a string value.", key);
- goto err_out;
- }
- val_str = g_variant_get_string(value, NULL);
- if (!(py_optval = PyUnicode_FromString(val_str))) {
- /* Some UTF-8 encoding error. */
- PyErr_Clear();
- srd_err("Option '%s' requires a UTF-8 string value.", key);
- goto err_out;
- }
- } else if (PyLong_Check(py_classval)) {
- if (!g_variant_is_of_type(value, G_VARIANT_TYPE_INT64)) {
- srd_err("Option '%s' requires an integer value.", key);
- goto err_out;
- }
- val_int = g_variant_get_int64(value);
- if (!(py_optval = PyLong_FromLong(val_int))) {
- /* ValueError Exception */
- PyErr_Clear();
- srd_err("Option '%s' has invalid integer value.", key);
- goto err_out;
- }
- }
- g_hash_table_remove(options, key);
- } else {
- /* Use the class default for this option. */
- if (PyUnicode_Check(py_classval)) {
- /* Make a brand new copy of the string. */
- py_ustr = PyUnicode_AS_UNICODE(py_classval);
- size = PyUnicode_GET_SIZE(py_classval);
- py_optval = PyUnicode_FromUnicode(py_ustr, size);
- } else if (PyLong_Check(py_classval)) {
- /* Make a brand new copy of the integer. */
- val_ull = PyLong_AsUnsignedLongLong(py_classval);
- if (val_ull == (unsigned long long)-1) {
- /* OverFlowError exception */
- PyErr_Clear();
- srd_err("Invalid integer value for %s: "
- "expected integer.", key);
- goto err_out;
- }
- if (!(py_optval = PyLong_FromUnsignedLongLong(val_ull)))
- goto err_out;
- }
- }
- /*
- * If we got here, py_optval holds a known good new reference
- * to the instance option to set.
- */
- if (PyDict_SetItemString(py_di_options, key, py_optval) == -1)
+ for (l = di->decoder->options; l; l = l->next) {
+ sdo = l->data;
+ if ((value = g_hash_table_lookup(options, sdo->id))) {
+ /* A value was supplied for this option. */
+ if (!g_variant_type_equal(g_variant_get_type(value),
+ g_variant_get_type(sdo->def))) {
+ srd_err("Option '%s' should have the same type "
+ "as the default value.", sdo->id);
+ goto err_out;
+ }
+ } else {
+ /* Use default for this option. */
+ value = sdo->def;
+ }
+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) {
+ val_str = g_variant_get_string(value, NULL);
+ if (!(py_optval = PyUnicode_FromString(val_str))) {
+ /* Some UTF-8 encoding error. */
+ PyErr_Clear();
+ srd_err("Option '%s' requires a UTF-8 string value.", sdo->id);
+ goto err_out;
+ }
+ } else if (g_variant_is_of_type(value, G_VARIANT_TYPE_INT64)) {
+ val_int = g_variant_get_int64(value);
+ if (!(py_optval = PyLong_FromLong(val_int))) {
+ /* ValueError Exception */
+ PyErr_Clear();
+ srd_err("Option '%s' has invalid integer value.", sdo->id);
+ goto err_out;
+ }
+ } else if (g_variant_is_of_type(value, G_VARIANT_TYPE_DOUBLE)) {
+ val_double = g_variant_get_int64(value);
+ if (!(py_optval = PyFloat_FromDouble(val_double))) {
+ /* ValueError Exception */
+ PyErr_Clear();
+ srd_err("Option '%s' has invalid float value.", sdo->id);
+ goto err_out;
+ }
+ }
+ if (PyDict_SetItemString(py_di_options, sdo->id, py_optval) == -1)
goto err_out;
- g_free(key);
- key = NULL;
- }
+ /* Not harmful even if we used the default. */
+ g_hash_table_remove(options, sdo->id);
+ }
+ if (g_hash_table_size(options) != 0)
+ srd_warn("Unknown options specified for '%s'", di->inst_id);
ret = SRD_OK;
err_out:
- Py_XDECREF(py_di_options);
- Py_XDECREF(py_dec_optkeys);
- Py_XDECREF(py_dec_options);
- g_free(key);
+ Py_XDECREF(py_optval);
if (PyErr_Occurred()) {
srd_exception_catch("Stray exception in srd_inst_option_set().");
ret = SRD_ERR_PYTHON;
char *id;
char *desc;
GVariant *def;
+ GSList *values;
};
struct srd_decoder_annotation_row {