diff options
-rw-r--r-- | decoder.c | 4 | ||||
-rw-r--r-- | instance.c | 398 | ||||
-rw-r--r-- | libsigrokdecode-internal.h | 24 | ||||
-rw-r--r-- | libsigrokdecode.h | 37 | ||||
-rw-r--r-- | type_decoder.c | 338 | ||||
-rw-r--r-- | util.c | 78 |
6 files changed, 873 insertions, 6 deletions
@@ -682,8 +682,8 @@ SRD_API int srd_decoder_load(const char *module_name) * PDs of different API versions are incompatible and cannot work. */ apiver = srd_decoder_apiver(d); - if (apiver != 2) { - srd_exception_catch("Only PD API version 2 is supported, " + if (apiver != 2 && apiver != 3) { + srd_exception_catch("Only PD API version 2/3 is supported, " "decoder %s has version %ld", module_name, apiver); fail_txt = "API version mismatch"; goto err_out; @@ -354,6 +354,18 @@ SRD_API struct srd_decoder_inst *srd_inst_new(struct srd_session *sess, return NULL; } + di->condition_list = NULL; + di->match_array = NULL; + di->start_samplenum = 0; + di->end_samplenum = 0; + di->inbuf = NULL; + di->inbuflen = 0; + di->cur_samplenum = 0; + di->old_pins_array = NULL; + di->thread_handle = NULL; + di->got_new_samples = FALSE; + di->handled_all_samples = FALSE; + /* Instance takes input from a frontend by default. */ sess->di_list = g_slist_append(sess->di_list, di); @@ -493,6 +505,56 @@ SRD_PRIV struct srd_decoder_inst *srd_inst_find_by_obj(const GSList *stack, return di; } +/** + * Set the list of initial (assumed) pin values. + * + * If the list already exists, do nothing. + * + * @param di Decoder instance to use. Must not be NULL. + * + * @private + */ +static void set_initial_pin_values(struct srd_decoder_inst *di) +{ + int i; + GString *s; + PyObject *py_initial_pins; + + if (!di || !di->py_inst) { + srd_err("Invalid decoder instance."); + return; + } + + /* Nothing to do if di->old_pins_array is already != NULL. */ + if (di->old_pins_array) { + srd_dbg("Initial pins already set, nothing to do."); + return; + } + + /* Create an array of old (previous sample) pins, init to 0. */ + di->old_pins_array = g_array_sized_new(FALSE, TRUE, sizeof(uint8_t), di->dec_num_channels); + g_array_set_size(di->old_pins_array, di->dec_num_channels); + + /* Check if the decoder has set self.initial_pins. */ + if (!PyObject_HasAttrString(di->py_inst, "initial_pins")) { + srd_dbg("Initial pins: all 0 (self.initial_pins not set)."); + return; + } + + /* Get self.initial_pins. */ + py_initial_pins = PyObject_GetAttrString(di->py_inst, "initial_pins"); + + /* Fill di->old_pins_array based on self.initial_pins. */ + s = g_string_sized_new(100); + for (i = 0; i < di->dec_num_channels; i++) { + di->old_pins_array->data[i] = PyLong_AsLong(PyList_GetItem(py_initial_pins, i)); + g_string_append_printf(s, "%d, ", di->old_pins_array->data[i]); + } + s = g_string_truncate(s, s->len - 2); + srd_dbg("Initial pins: %s.", s->str); + g_string_free(s, TRUE); +} + /** @private */ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di) { @@ -504,6 +566,7 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di) srd_dbg("Calling start() method on protocol decoder instance %s.", di->inst_id); + /* Run self.start(). */ if (!(py_res = PyObject_CallMethod(di->py_inst, "start", NULL))) { srd_exception_catch("Protocol decoder instance %s", di->inst_id); @@ -511,6 +574,15 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di) } Py_DecRef(py_res); + /* Set the initial pins based on self.initial_pins. */ + set_initial_pin_values(di); + + /* Set self.samplenum to 0. */ + PyObject_SetAttrString(di->py_inst, "samplenum", PyLong_FromLong(0)); + + /* Set self.matches to None. */ + PyObject_SetAttrString(di->py_inst, "matches", Py_None); + /* Start all the PDs stacked on top of this one. */ for (l = di->next_di; l; l = l->next) { next_di = l->data; @@ -522,6 +594,304 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di) } /** + * Check whether the specified sample matches the specified term. + * + * In the case of SRD_TERM_SKIP, this function can modify + * term->num_samples_already_skipped. + * + * @param old_sample The value of the previous sample (0/1). + * @param sample The value of the current sample (0/1). + * @param term The term that should be checked for a match. Must not be NULL. + * + * @retval TRUE The current sample matches the specified term. + * @retval FALSE The current sample doesn't match the specified term, or an + * invalid term was provided. + * + * @private + */ +static gboolean sample_matches(uint8_t old_sample, uint8_t sample, struct srd_term *term) +{ + if (!term) + return FALSE; + + switch (term->type) { + case SRD_TERM_HIGH: + if (sample == 1) + return TRUE; + break; + case SRD_TERM_LOW: + if (sample == 0) + return TRUE; + break; + case SRD_TERM_RISING_EDGE: + if (old_sample == 0 && sample == 1) + return TRUE; + break; + case SRD_TERM_FALLING_EDGE: + if (old_sample == 1 && sample == 0) + return TRUE; + break; + case SRD_TERM_EITHER_EDGE: + if ((old_sample == 1 && sample == 0) || (old_sample == 0 && sample == 1)) + return TRUE; + break; + case SRD_TERM_NO_EDGE: + if ((old_sample == 0 && sample == 0) || (old_sample == 1 && sample == 1)) + return TRUE; + break; + case SRD_TERM_SKIP: + if (term->num_samples_already_skipped == term->num_samples_to_skip) + return TRUE; + term->num_samples_already_skipped++; + break; + default: + srd_err("Unknown term type %d.", term->type); + break; + } + + return FALSE; +} + +SRD_PRIV void match_array_free(struct srd_decoder_inst *di) +{ + if (!di || !di->match_array) + return; + + g_array_free(di->match_array, TRUE); + di->match_array = NULL; +} + +SRD_PRIV void condition_list_free(struct srd_decoder_inst *di) +{ + GSList *l, *ll; + + if (!di) + return; + + for (l = di->condition_list; l; l = l->next) { + ll = l->data; + if (ll) + g_slist_free_full(ll, g_free); + } + + di->condition_list = NULL; +} + +static gboolean have_non_null_conds(const struct srd_decoder_inst *di) +{ + GSList *l, *cond; + + if (!di) + return FALSE; + + for (l = di->condition_list; l; l = l->next) { + cond = l->data; + if (cond) + return TRUE; + } + + return FALSE; +} + +static void update_old_pins_array(struct srd_decoder_inst *di, + const uint8_t *sample_pos) +{ + uint8_t sample; + int i, byte_offset, bit_offset; + + if (!di || !di->dec_channelmap || !sample_pos) + return; + + for (i = 0; i < di->dec_num_channels; i++) { + byte_offset = di->dec_channelmap[i] / 8; + bit_offset = di->dec_channelmap[i] % 8; + sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0; + di->old_pins_array->data[i] = sample; + } +} + +static gboolean term_matches(const struct srd_decoder_inst *di, + struct srd_term *term, const uint8_t *sample_pos) +{ + uint8_t old_sample, sample; + int byte_offset, bit_offset, ch; + + if (!di || !di->dec_channelmap || !term || !sample_pos) + return FALSE; + + /* Overwritten below (or ignored for SRD_TERM_SKIP). */ + old_sample = sample = 0; + + if (term->type != SRD_TERM_SKIP) { + ch = term->channel; + byte_offset = di->dec_channelmap[ch] / 8; + bit_offset = di->dec_channelmap[ch] % 8; + sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0; + old_sample = di->old_pins_array->data[ch]; + } + + return sample_matches(old_sample, sample, term); +} + +static gboolean all_terms_match(const struct srd_decoder_inst *di, + const GSList *cond, const uint8_t *sample_pos) +{ + const GSList *l; + struct srd_term *term; + + if (!di || !cond || !sample_pos) + return FALSE; + + for (l = cond; l; l = l->next) { + term = l->data; + if (!term_matches(di, term, sample_pos)) + return FALSE; + } + + return TRUE; +} + +static gboolean at_least_one_condition_matched( + const struct srd_decoder_inst *di, unsigned int num_conditions) +{ + unsigned int i; + + if (!di) + return FALSE; + + for (i = 0; i < num_conditions; i++) { + if (di->match_array->data[i]) + return TRUE; + } + + return FALSE; +} + +static gboolean find_match(struct srd_decoder_inst *di) +{ + static uint64_t s = 0; + uint64_t i, j, num_samples_to_process; + GSList *l, *cond; + const uint8_t *sample_pos; + unsigned int num_conditions; + + /* Check whether the condition list is NULL/empty. */ + if (!di->condition_list) { + srd_dbg("NULL/empty condition list, automatic match."); + return TRUE; + } + + /* Check whether we have any non-NULL conditions. */ + if (!have_non_null_conds(di)) { + srd_dbg("Only NULL conditions in list, automatic match."); + return TRUE; + } + + num_samples_to_process = di->end_samplenum - di->cur_samplenum; + num_conditions = g_slist_length(di->condition_list); + + /* di->match_array is NULL here. Create a new GArray. */ + di->match_array = g_array_sized_new(FALSE, TRUE, sizeof(gboolean), num_conditions); + g_array_set_size(di->match_array, num_conditions); + + for (i = 0, s = 0; i < num_samples_to_process; i++, s++, (di->cur_samplenum)++) { + + sample_pos = di->inbuf + ((di->cur_samplenum - di->start_samplenum) * di->data_unitsize); + + /* Check whether the current sample matches at least one of the conditions (logical OR). */ + /* IMPORTANT: We need to check all conditions, even if there was a match already! */ + for (l = di->condition_list, j = 0; l; l = l->next, j++) { + cond = l->data; + if (!cond) + continue; + /* All terms in 'cond' must match (logical AND). */ + di->match_array->data[j] = all_terms_match(di, cond, sample_pos); + } + + update_old_pins_array(di, sample_pos); + + /* If at least one condition matched we're done. */ + if (at_least_one_condition_matched(di, num_conditions)) + return TRUE; + } + + return FALSE; +} + +/** + * Process available samples and check if they match the defined conditions. + * + * This function returns if there is an error, or when a match is found, or + * when all samples have been processed (whether a match was found or not). + * + * @param di The decoder instance to use. Must not be NULL. + * @param found_match Will be set to TRUE if at least one condition matched, + * FALSE otherwise. Must not be NULL. + * + * @retval SRD_OK No errors occured, see found_match for the result. + * @retval SRD_ERR_ARG Invalid arguments. + * + * @private + */ +SRD_PRIV int process_samples_until_condition_match(struct srd_decoder_inst *di, gboolean *found_match) +{ + if (!di || !found_match) + return SRD_ERR_ARG; + + /* Check if any of the current condition(s) match. */ + while (TRUE) { + /* Feed the (next chunk of the) buffer to find_match(). */ + *found_match = find_match(di); + + /* Did we handle all samples yet? */ + if (di->cur_samplenum >= di->end_samplenum) { + srd_dbg("Done, handled all samples (%" PRIu64 "/%" PRIu64 ").", + di->cur_samplenum, di->end_samplenum); + return SRD_OK; + } + + /* If we didn't find a match, continue looking. */ + if (!(*found_match)) + continue; + + /* At least one condition matched, return. */ + return SRD_OK; + } + + return SRD_OK; +} + +/** + * Worker thread (per PD-stack). + * + * @param data Pointer to the lowest-level PD's device instance. + * Must not be NULL. + * + * @return NULL if there was an error. + */ +static gpointer di_thread(gpointer data) +{ + PyObject *py_res; + struct srd_decoder_inst *di; + + if (!data) + return NULL; + + di = data; + + /* Call self.decode(). Only returns if the PD throws an exception. */ + Py_IncRef(di->py_inst); + if (!(py_res = PyObject_CallMethod(di->py_inst, "decode", NULL))) { + srd_exception_catch("Protocol decoder instance %s: ", di->inst_id); + exit(1); /* TODO: Proper shutdown. This is a hack. */ + return NULL; + } + Py_DecRef(py_res); + + return NULL; +} + +/** * Decode a chunk of samples. * * @param di The decoder instance to call. Must not be NULL. @@ -537,7 +907,7 @@ SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di) * * @private */ -SRD_PRIV int srd_inst_decode(const struct srd_decoder_inst *di, +SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, uint64_t start_samplenum, uint64_t end_samplenum, const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize) { @@ -563,7 +933,7 @@ SRD_PRIV int srd_inst_decode(const struct srd_decoder_inst *di, return SRD_ERR_ARG; } - ((struct srd_decoder_inst *)di)->data_unitsize = unitsize; + di->data_unitsize = unitsize; srd_dbg("Decoding: start sample %" PRIu64 ", end sample %" PRIu64 " (%" PRIu64 " samples, %" PRIu64 " bytes, unitsize = " @@ -596,6 +966,30 @@ SRD_PRIV int srd_inst_decode(const struct srd_decoder_inst *di, return SRD_ERR_PYTHON; } Py_DecRef(py_res); + } else { + /* If this is the first call, start the worker thread. */ + if (!di->thread_handle) + di->thread_handle = g_thread_new("di_thread", + di_thread, di); + + /* Push the new sample chunk to the worker thread. */ + g_mutex_lock(&di->data_mutex); + di->start_samplenum = start_samplenum; + di->end_samplenum = end_samplenum; + di->inbuf = inbuf; + di->inbuflen = inbuflen; + di->got_new_samples = TRUE; + di->handled_all_samples = FALSE; + + /* Signal the thread that we have new data. */ + g_cond_signal(&di->got_new_samples_cond); + g_mutex_unlock(&di->data_mutex); + + /* When all samples in this chunk were handled, return. */ + g_mutex_lock(&di->data_mutex); + while (!di->handled_all_samples) + g_cond_wait(&di->handled_all_samples_cond, &di->data_mutex); + g_mutex_unlock(&di->data_mutex); } return SRD_OK; diff --git a/libsigrokdecode-internal.h b/libsigrokdecode-internal.h index 8259b97..c006574 100644 --- a/libsigrokdecode-internal.h +++ b/libsigrokdecode-internal.h @@ -28,6 +28,23 @@ #include <Python.h> /* First, so we avoid a _POSIX_C_SOURCE warning. */ #include "libsigrokdecode.h" +enum { + SRD_TERM_HIGH, + SRD_TERM_LOW, + SRD_TERM_RISING_EDGE, + SRD_TERM_FALLING_EDGE, + SRD_TERM_EITHER_EDGE, + SRD_TERM_NO_EDGE, + SRD_TERM_SKIP, +}; + +struct srd_term { + int type; + int channel; + uint64_t num_samples_to_skip; + uint64_t num_samples_already_skipped; +}; + /* Custom Python types: */ typedef struct { @@ -62,9 +79,12 @@ SRD_PRIV struct srd_pd_callback *srd_pd_output_callback_find(struct srd_session SRD_PRIV struct srd_decoder_inst *srd_inst_find_by_obj( const GSList *stack, const PyObject *obj); SRD_PRIV int srd_inst_start(struct srd_decoder_inst *di); -SRD_PRIV int srd_inst_decode(const struct srd_decoder_inst *di, +SRD_PRIV void match_array_free(struct srd_decoder_inst *di); +SRD_PRIV void condition_list_free(struct srd_decoder_inst *di); +SRD_PRIV int srd_inst_decode(struct srd_decoder_inst *di, uint64_t start_samplenum, uint64_t end_samplenum, const uint8_t *inbuf, uint64_t inbuflen, uint64_t unitsize); +SRD_PRIV int process_samples_until_condition_match(struct srd_decoder_inst *di, gboolean *found_match); SRD_PRIV void srd_inst_free(struct srd_decoder_inst *di); SRD_PRIV void srd_inst_free_all(struct srd_session *sess, GSList *stack); @@ -102,6 +122,8 @@ PyMODINIT_FUNC PyInit_sigrokdecode(void); SRD_PRIV PyObject *py_import_by_name(const char *name); SRD_PRIV int py_attr_as_str(PyObject *py_obj, const char *attr, char **outstr); SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, char **outstr); +SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, char **outstr); +SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out); SRD_PRIV int py_str_as_str(PyObject *py_str, char **outstr); SRD_PRIV int py_strseq_to_char(PyObject *py_strseq, char ***out_strv); SRD_PRIV GVariant *py_obj_to_variant(PyObject *py_obj); diff --git a/libsigrokdecode.h b/libsigrokdecode.h index 03a5de0..a4d28d4 100644 --- a/libsigrokdecode.h +++ b/libsigrokdecode.h @@ -228,6 +228,43 @@ struct srd_decoder_inst { int data_unitsize; uint8_t *channel_samples; GSList *next_di; + + /** List of conditions a PD wants to wait for. */ + GSList *condition_list; + + /** Array of booleans denoting which conditions matched. */ + GArray *match_array; + + /** Absolute start sample number. */ + uint64_t start_samplenum; + + /** Absolute end sample number. */ + uint64_t end_samplenum; + + /** Pointer to the buffer/chunk of input samples. */ + const uint8_t *inbuf; + + /** Length (in bytes) of the input sample buffer. */ + uint64_t inbuflen; + + /** Absolute current samplenumber. */ + uint64_t cur_samplenum; + + /** Array of "old" (previous sample) pin values. */ + GArray *old_pins_array; + + /** Handle for this PD stack's worker thread. */ + GThread *thread_handle; + + /** Indicates whether new samples are available for processing. */ + gboolean got_new_samples; + + /** Indicates whether the worker thread has handled all samples. */ + gboolean handled_all_samples; + + GCond got_new_samples_cond; + GCond handled_all_samples_cond; + GMutex data_mutex; }; struct srd_pd_output { diff --git a/type_decoder.c b/type_decoder.c index 228cd44..dda0de2 100644 --- a/type_decoder.c +++ b/type_decoder.c @@ -369,16 +369,352 @@ static PyObject *Decoder_register(PyObject *self, PyObject *args, return py_new_output_id; } +static int get_term_type(const char *v) +{ + switch (v[0]) { + case 'h': + return SRD_TERM_HIGH; + case 'l': + return SRD_TERM_LOW; + case 'r': + return SRD_TERM_RISING_EDGE; + case 'f': + return SRD_TERM_FALLING_EDGE; + case 'e': + return SRD_TERM_EITHER_EDGE; + case 'n': + return SRD_TERM_NO_EDGE; + } + + return -1; +} + +/** + * Get the pin values at the current sample number. + * + * @param di The decoder instance to use. Must not be NULL. + * The number of channels must be >= 1. + * + * @return A newly allocated PyTuple containing the pin values at the + * current sample number. + */ +static PyObject *get_current_pinvalues(const struct srd_decoder_inst *di) +{ + int i; + uint8_t sample; + const uint8_t *sample_pos; + int byte_offset, bit_offset; + PyObject *py_pinvalues; + + if (!di) { + srd_err("Invalid decoder instance."); + return NULL; + } + + py_pinvalues = PyTuple_New(di->dec_num_channels); + + for (i = 0; i < di->dec_num_channels; i++) { + /* A channelmap value of -1 means "unused optional channel". */ + if (di->dec_channelmap[i] == -1) { + /* Value of unused channel is 0xff, instead of 0 or 1. */ + PyTuple_SetItem(py_pinvalues, i, PyLong_FromLong(0xff)); + } else { + sample_pos = di->inbuf + ((di->cur_samplenum - di->start_samplenum) * di->data_unitsize); + byte_offset = di->dec_channelmap[i] / 8; + bit_offset = di->dec_channelmap[i] % 8; + sample = *(sample_pos + byte_offset) & (1 << bit_offset) ? 1 : 0; + PyTuple_SetItem(py_pinvalues, i, PyLong_FromLong(sample)); + } + } + + Py_IncRef(py_pinvalues); + + return py_pinvalues; +} + +/** + * Create a list of terms in the specified condition. + * + * If there are no terms in the condition, 'term_list' will be NULL. + * + * @param py_dict A Python dict containing terms. Must not be NULL. + * @param term_list Pointer to a GSList which will be set to the newly + * created list of terms. Must not be NULL. + * + * @return SRD_OK upon success, a negative error code otherwise. + */ +static int create_term_list(PyObject *py_dict, GSList **term_list) +{ + Py_ssize_t pos = 0; + PyObject *py_key, *py_value; + struct srd_term *term; + uint64_t num_samples_to_skip; + char *term_str; + + if (!py_dict || !term_list) + return SRD_ERR_ARG; + + /* "Create" an empty GSList of terms. */ + *term_list = NULL; + + /* Iterate over all items in the current dict. */ + while (PyDict_Next(py_dict, &pos, &py_key, &py_value)) { + /* Check whether the current key is a string or a number. */ + if (PyLong_Check(py_key)) { + /* The key is a number. */ + /* TODO: Check if the number is a valid channel. */ + /* Get the value string. */ + if ((py_pydictitem_as_str(py_dict, py_key, &term_str)) != SRD_OK) { + srd_err("Failed to get the value."); + return SRD_ERR; + } + term = g_malloc0(sizeof(struct srd_term)); + term->type = get_term_type(term_str); + term->channel = PyLong_AsLong(py_key); + g_free(term_str); + } else if (PyUnicode_Check(py_key)) { + /* The key is a string. */ + /* TODO: Check if it's "skip". */ + if ((py_pydictitem_as_long(py_dict, py_key, &num_samples_to_skip)) != SRD_OK) { + srd_err("Failed to get number of samples to skip."); + return SRD_ERR; + } + term = g_malloc0(sizeof(struct srd_term)); + term->type = SRD_TERM_SKIP; + term->num_samples_to_skip = num_samples_to_skip; + term->num_samples_already_skipped = 0; + } else { + srd_err("Term key is neither a string nor a number."); + return SRD_ERR; + } + + /* Add the term to the list of terms. */ + *term_list = g_slist_append(*term_list, term); + } + + return SRD_OK; +} + +/** + * Replace the current condition list with the new one. + * + * @param self TODO. Must not be NULL. + * @param args TODO. Must not be NULL. + * + * @retval SRD_OK The new condition list was set successfully. + * @retval SRD_ERR There was an error setting the new condition list. + * The contents of di->condition_list are undefined. + * @retval 9999 TODO. + */ +static int set_new_condition_list(PyObject *self, PyObject *args) +{ + struct srd_decoder_inst *di; + GSList *term_list; + PyObject *py_conditionlist, *py_conds, *py_dict; + int i, num_conditions, ret; + + if (!self || !args) + return SRD_ERR_ARG; + + /* Get the decoder instance. */ + if (!(di = srd_inst_find_by_obj(NULL, self))) { + PyErr_SetString(PyExc_Exception, "decoder instance not found"); + return SRD_ERR; + } + + /* Parse the argument of self.wait() into 'py_conds'. */ + if (!PyArg_ParseTuple(args, "O", &py_conds)) { + /* Let Python raise this exception. */ + return SRD_ERR; + } + + /* Check whether 'py_conds' is a dict or a list. */ + if (PyList_Check(py_conds)) { + /* 'py_conds' is a list. */ + py_conditionlist = py_conds; + num_conditions = PyList_Size(py_conditionlist); + if (num_conditions == 0) + return 9999; /* The PD invoked self.wait([]). */ + } else if (PyDict_Check(py_conds)) { + /* 'py_conds' is a dict. */ + if (PyDict_Size(py_conds) == 0) + return 9999; /* The PD invoked self.wait({}). */ + /* Make a list and put the dict in there for convenience. */ + py_conditionlist = PyList_New(1); + PyList_SetItem(py_conditionlist, 0, py_conds); + num_conditions = 1; + } else { + srd_err("Condition list is neither a list nor a dict."); + return SRD_ERR; + } + + /* Free the old condition list. */ + condition_list_free(di); + + ret = SRD_OK; + + /* Iterate over the conditions, set di->condition_list accordingly. */ + for (i = 0; i < num_conditions; i++) { + /* Get a condition (dict) from the condition list. */ + py_dict = PyList_GetItem(py_conditionlist, i); + if (!PyDict_Check(py_dict)) { + srd_err("Condition is not a dict."); + ret = SRD_ERR; + break; + } + + /* Create the list of terms in this condition. */ + if ((ret = create_term_list(py_dict, &term_list)) < 0) + break; + + /* Add the new condition to the PD instance's condition list. */ + di->condition_list = g_slist_append(di->condition_list, term_list); + } + + Py_DecRef(py_conditionlist); + + return ret; +} + +static PyObject *Decoder_wait(PyObject *self, PyObject *args) +{ + int ret; + unsigned int i; + gboolean found_match; + struct srd_decoder_inst *di; + PyObject *py_pinvalues, *py_matched; + + if (!self || !args) + return NULL; + + if (!(di = srd_inst_find_by_obj(NULL, self))) { + PyErr_SetString(PyExc_Exception, "decoder instance not found"); + Py_RETURN_NONE; + } + + ret = set_new_condition_list(self, args); + + if (ret == 9999) { + /* Empty condition list, automatic match. */ + PyObject_SetAttrString(di->py_inst, "matched", Py_None); + /* Leave self.samplenum unchanged (== di->cur_samplenum). */ + return get_current_pinvalues(di); + } + + while (1) { + /* Wait for new samples to process. */ + g_mutex_lock(&di->data_mutex); + while (!di->got_new_samples) + g_cond_wait(&di->got_new_samples_cond, &di->data_mutex); + + /* Check whether any of the current condition(s) match. */ + ret = process_samples_until_condition_match(di, &found_match); + + /* If there's a match, set self.samplenum etc. and return. */ + if (found_match) { + /* Set self.samplenum to the (absolute) sample number that matched. */ + PyObject_SetAttrString(di->py_inst, "samplenum", + PyLong_FromLong(di->cur_samplenum)); + + if (di->match_array && di->match_array->len > 0) { + py_matched = PyTuple_New(di->match_array->len); + for (i = 0; i < di->match_array->len; i++) + PyTuple_SetItem(py_matched, i, PyBool_FromLong(di->match_array->data[i])); + PyObject_SetAttrString(di->py_inst, "matched", py_matched); + match_array_free(di); + } else { + PyObject_SetAttrString(di->py_inst, "matched", Py_None); + } + + py_pinvalues = get_current_pinvalues(di); + + g_mutex_unlock(&di->data_mutex); + + return py_pinvalues; + } + + /* No match, reset state for the next chunk. */ + di->got_new_samples = FALSE; + di->handled_all_samples = TRUE; + di->start_samplenum = 0; + di->end_samplenum = 0; + di->inbuf = NULL; + di->inbuflen = 0; + + /* Signal the main thread that we handled all samples. */ + g_cond_signal(&di->handled_all_samples_cond); + + g_mutex_unlock(&di->data_mutex); + } + + Py_RETURN_NONE; +} + +/** + * Return whether the specified channel was supplied to the decoder. + * + * @param self TODO. Must not be NULL. + * @param args TODO. Must not be NULL. + * + * @retval Py_True The channel has been supplied by the frontend. + * @retval Py_False The channel has been supplied by the frontend. + * @retval NULL An error occurred. + */ +static PyObject *Decoder_has_channel(PyObject *self, PyObject *args) +{ + int idx, max_idx; + struct srd_decoder_inst *di; + PyObject *py_channel; + + if (!self || !args) + return NULL; + + if (!(di = srd_inst_find_by_obj(NULL, self))) { + PyErr_SetString(PyExc_Exception, "decoder instance not found"); + return NULL; + } + + /* Parse the argument of self.has_channel() into 'py_channel'. */ + if (!PyArg_ParseTuple(args, "O", &py_channel)) { + /* Let Python raise this exception. */ + return NULL; + } + + if (!PyLong_Check(py_channel)) { + PyErr_SetString(PyExc_Exception, "channel index not a number"); + return NULL; + } + + idx = PyLong_AsLong(py_channel); + max_idx = g_slist_length(di->decoder->channels) + + g_slist_length(di->decoder->opt_channels) - 1; + + if (idx < 0 || idx > max_idx) { + srd_err("Invalid channel index %d/%d.", idx, max_idx); + PyErr_SetString(PyExc_Exception, "invalid channel"); + return NULL; + } + + return (di->dec_channelmap[idx] == -1) ? Py_False : Py_True; +} + static PyMethodDef Decoder_methods[] = { {"put", Decoder_put, METH_VARARGS, "Accepts a dictionary with the following keys: startsample, endsample, data"}, {"register", (PyCFunction)Decoder_register, METH_VARARGS|METH_KEYWORDS, "Register a new output stream"}, + {"wait", Decoder_wait, METH_VARARGS, + "Wait for one or more conditions to occur"}, + {"has_channel", Decoder_has_channel, METH_VARARGS, + "Report whether a channel was supplied"}, {NULL, NULL, 0, NULL} }; -/** Create the sigrokdecode.Decoder type. +/** + * Create the sigrokdecode.Decoder type. + * * @return The new type object. + * * @private */ SRD_PRIV PyObject *srd_Decoder_type_new(void) @@ -113,6 +113,84 @@ SRD_PRIV int py_dictitem_as_str(PyObject *py_obj, const char *key, } /** + * Get the value of a Python dictionary item, returned as a newly + * allocated char *. + * + * @param py_obj The dictionary to probe. + * @param py_key Key of the item to retrieve. + * @param outstr Pointer to char * storage to be filled in. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * The 'outstr' argument points to a malloc()ed string upon success. + * + * @private + */ +SRD_PRIV int py_pydictitem_as_str(PyObject *py_obj, PyObject *py_key, + char **outstr) +{ + PyObject *py_value; + + if (!py_obj || !py_key || !outstr) + return SRD_ERR_ARG; + + if (!PyDict_Check(py_obj)) { + srd_dbg("Object is not a dictionary."); + return SRD_ERR_PYTHON; + } + + if (!(py_value = PyDict_GetItem(py_obj, py_key))) { + srd_dbg("Dictionary has no such key."); + return SRD_ERR_PYTHON; + } + + if (!PyUnicode_Check(py_value)) { + srd_dbg("Dictionary value should be a string."); + return SRD_ERR_PYTHON; + } + + return py_str_as_str(py_value, outstr); +} + +/** + * Get the value of a Python dictionary item, returned as a newly + * allocated char *. + * + * @param py_obj The dictionary to probe. + * @param py_key Key of the item to retrieve. + * @param out TODO. + * + * @return SRD_OK upon success, a (negative) error code otherwise. + * + * @private + */ +SRD_PRIV int py_pydictitem_as_long(PyObject *py_obj, PyObject *py_key, uint64_t *out) +{ + PyObject *py_value; + + if (!py_obj || !py_key || !out) + return SRD_ERR_ARG; + + if (!PyDict_Check(py_obj)) { + srd_dbg("Object is not a dictionary."); + return SRD_ERR_PYTHON; + } + + if (!(py_value = PyDict_GetItem(py_obj, py_key))) { + srd_dbg("Dictionary has no such key."); + return SRD_ERR_PYTHON; + } + + if (!PyLong_Check(py_value)) { + srd_dbg("Dictionary value should be a long."); + return SRD_ERR_PYTHON; + } + + *out = PyLong_AsUnsignedLongLong(py_value); + + return SRD_OK; +} + +/** * Get the value of a Python unicode string object, returned as a newly * allocated char *. * |