+/**
+ * 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
+ */
+__attribute__((always_inline))
+static inline gboolean sample_matches(uint8_t old_sample, uint8_t sample, struct srd_term *term)
+{
+ /* Caller ensures term != NULL. */
+
+ 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;
+}
+
+/** @private */
+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;
+}
+
+/** @private */
+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);
+ }
+
+ g_slist_free(di->condition_list);
+ 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;
+
+ oldpins_array_seed(di);
+ for (i = 0; i < di->dec_num_channels; i++) {
+ if (di->dec_channelmap[i] == -1)
+ continue; /* Ignore unused optional channels. */
+ 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 void update_old_pins_array_initial_pins(struct srd_decoder_inst *di)
+{
+ uint8_t sample;
+ int i, byte_offset, bit_offset;
+ const uint8_t *sample_pos;
+
+ if (!di || !di->dec_channelmap)
+ return;
+
+ sample_pos = di->inbuf + ((di->abs_cur_samplenum - di->abs_start_samplenum) * di->data_unitsize);
+
+ oldpins_array_seed(di);
+ for (i = 0; i < di->dec_num_channels; i++) {
+ if (di->old_pins_array->data[i] != SRD_INITIAL_PIN_SAME_AS_SAMPLE0)
+ continue;
+ if (di->dec_channelmap[i] == -1)
+ continue; /* Ignore unused optional channels. */
+ 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;
+
+ /* Caller ensures di, di->dec_channelmap, term, sample_pos != NULL. */
+
+ if (term->type == SRD_TERM_SKIP)
+ return sample_matches(0, 0, term);
+
+ 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;
+
+ /* Caller ensures di, cond, sample_pos != NULL. */
+
+ for (l = cond; l; l = l->next) {
+ term = l->data;
+ if (term->type == SRD_TERM_ALWAYS_FALSE)
+ return FALSE;
+ 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;
+
+ /* Caller ensures di != NULL. */
+
+ 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)
+{
+ uint64_t i, j, num_samples_to_process;
+ GSList *l, *cond;
+ const uint8_t *sample_pos;
+ unsigned int num_conditions;
+
+ /* Caller ensures di != NULL. */
+
+ /* 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->abs_end_samplenum - di->abs_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);
+
+ /* Sample 0: Set di->old_pins_array for SRD_INITIAL_PIN_SAME_AS_SAMPLE0 pins. */
+ if (di->abs_cur_samplenum == 0)
+ update_old_pins_array_initial_pins(di);
+
+ for (i = 0; i < num_samples_to_process; i++, (di->abs_cur_samplenum)++) {
+
+ sample_pos = di->inbuf + ((di->abs_cur_samplenum - di->abs_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).
+ * This function immediately terminates when the decoder's wait() method
+ * invocation shall get terminated.
+ *
+ * @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;
+
+ *found_match = FALSE;
+ if (di->want_wait_terminate)
+ return SRD_OK;
+
+ /* 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->abs_cur_samplenum >= di->abs_end_samplenum) {
+ srd_dbg("Done, handled all samples (abs cur %" PRIu64
+ " / abs end %" PRIu64 ").",
+ di->abs_cur_samplenum, di->abs_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;
+ int wanted_term;
+ PyGILState_STATE gstate;
+
+ if (!data)
+ return NULL;
+
+ di = data;
+
+ srd_dbg("%s: Starting thread routine for decoder.", di->inst_id);
+
+ gstate = PyGILState_Ensure();
+
+ /*
+ * Call self.decode(). Only returns if the PD throws an exception.
+ * "Regular" termination of the decode() method is not expected.
+ */
+ Py_INCREF(di->py_inst);
+ srd_dbg("%s: Calling decode().", di->inst_id);
+ py_res = PyObject_CallMethod(di->py_inst, "decode", NULL);
+ srd_dbg("%s: decode() terminated.", di->inst_id);
+
+ if (!py_res)
+ di->decoder_state = SRD_ERR;
+
+ /*
+ * Make sure to unblock potentially pending srd_inst_decode()
+ * calls in application threads after the decode() method might
+ * have terminated, while it neither has processed sample data
+ * nor has terminated upon request. This happens e.g. when "need
+ * a samplerate to decode" exception is thrown.
+ */
+ g_mutex_lock(&di->data_mutex);
+ wanted_term = di->want_wait_terminate;
+ di->want_wait_terminate = TRUE;
+ di->handled_all_samples = TRUE;
+ g_cond_signal(&di->handled_all_samples_cond);
+ g_mutex_unlock(&di->data_mutex);
+
+ /*
+ * Check for the termination cause of the decode() method.
+ * Though this is mostly for information.
+ */
+ if (!py_res && wanted_term) {
+ /*
+ * Silently ignore errors upon return from decode() calls
+ * when termination was requested. Terminate the thread
+ * which executed this instance's decode() logic.
+ */
+ srd_dbg("%s: Thread done (!res, want_term).", di->inst_id);
+ PyErr_Clear();
+ PyGILState_Release(gstate);
+ return NULL;
+ }
+ if (!py_res) {
+ /*
+ * The decode() invocation terminated unexpectedly. Have
+ * the back trace printed, and terminate the thread which
+ * executed the decode() method.
+ */
+ srd_dbg("%s: decode() terminated unrequested.", di->inst_id);
+ srd_exception_catch("Protocol decoder instance %s: ", di->inst_id);
+ srd_dbg("%s: Thread done (!res, !want_term).", di->inst_id);
+ PyGILState_Release(gstate);
+ return NULL;
+ }
+
+ /*
+ * TODO: By design the decode() method is not supposed to terminate.
+ * Nevertheless we have the thread joined, and srd backend calls to
+ * decode() will re-start another thread transparently.
+ */
+ srd_dbg("%s: decode() terminated (req %d).", di->inst_id, wanted_term);
+ Py_DECREF(py_res);
+ PyErr_Clear();
+
+ PyGILState_Release(gstate);
+
+ srd_dbg("%s: Thread done (with res).", di->inst_id);
+
+ return NULL;
+}
+