+ "a list of dict elements.", d->name, attr);
+ goto err_out;
+ }
+ pdch = g_malloc0(sizeof(struct srd_channel));
+ /* Add to list right away so it doesn't get lost. */
+ pdchl = g_slist_prepend(pdchl, pdch);
+
+ if (py_dictitem_as_str(py_entry, "id", &pdch->id) != SRD_OK)
+ goto err_out;
+ if (py_dictitem_as_str(py_entry, "name", &pdch->name) != SRD_OK)
+ goto err_out;
+ if (py_dictitem_as_str(py_entry, "desc", &pdch->desc) != SRD_OK)
+ goto err_out;
+
+ pdch->order = offset + i;
+ }
+
+ Py_DECREF(py_channellist);
+ *out_pdchl = pdchl;
+
+ return SRD_OK;
+
+except_out:
+ srd_exception_catch("Failed to get %s list of %s decoder",
+ attr, d->name);
+err_out:
+ g_slist_free_full(pdchl, &channel_free);
+ Py_XDECREF(py_channellist);
+
+ return SRD_ERR_PYTHON;
+}
+
+static int get_options(struct srd_decoder *d)
+{
+ PyObject *py_opts, *py_opt, *py_str, *py_values, *py_default, *py_item;
+ GSList *options;
+ struct srd_decoder_option *o;
+ GVariant *gvar;
+ ssize_t opt, i;
+
+ if (!PyObject_HasAttrString(d->py_dec, "options"))
+ /* No options, that's fine. */
+ return SRD_OK;
+
+ options = NULL;
+
+ /* If present, options must be a tuple. */
+ py_opts = PyObject_GetAttrString(d->py_dec, "options");
+ if (!py_opts)
+ goto except_out;
+
+ if (!PyTuple_Check(py_opts)) {
+ srd_err("Protocol decoder %s: options attribute is not "
+ "a tuple.", d->id);
+ goto err_out;
+ }
+
+ for (opt = PyTuple_Size(py_opts) - 1; opt >= 0; opt--) {
+ py_opt = PyTuple_GetItem(py_opts, opt);
+ if (!py_opt)
+ goto except_out;
+
+ if (!PyDict_Check(py_opt)) {
+ srd_err("Protocol decoder %s options: each option "
+ "must consist of a dictionary.", d->name);
+ goto err_out;
+ }
+
+ o = g_malloc0(sizeof(struct srd_decoder_option));
+ /* Add to list right away so it doesn't get lost. */
+ options = g_slist_prepend(options, o);
+
+ py_str = PyDict_GetItemString(py_opt, "id");
+ if (!py_str) {
+ srd_err("Protocol decoder %s option %zd has no id.",
+ d->name, opt);
+ goto err_out;
+ }
+ if (py_str_as_str(py_str, &o->id) != SRD_OK)