+ lock_guard<mutex> decode_lock(output_mutex_);
+ return samples_decoded_;
+}
+
+vector<Row> DecodeSignal::visible_rows() const
+{
+ lock_guard<mutex> lock(output_mutex_);
+
+ vector<Row> rows;
+
+ for (const shared_ptr<decode::Decoder> &dec : stack_) {
+ assert(dec);
+ if (!dec->shown())
+ continue;
+
+ const srd_decoder *const decc = dec->decoder();
+ assert(dec->decoder());
+
+ // Add a row for the decoder if it doesn't have a row list
+ if (!decc->annotation_rows)
+ rows.emplace_back(decc);
+
+ // Add the decoder rows
+ for (const GSList *l = decc->annotation_rows; l; l = l->next) {
+ const srd_decoder_annotation_row *const ann_row =
+ (srd_decoder_annotation_row *)l->data;
+ assert(ann_row);
+ rows.emplace_back(decc, ann_row);
+ }
+ }
+
+ return rows;
+}
+
+void DecodeSignal::get_annotation_subset(
+ vector<pv::data::decode::Annotation> &dest,
+ const decode::Row &row, uint64_t start_sample,
+ uint64_t end_sample) const
+{
+ lock_guard<mutex> lock(output_mutex_);
+
+ const auto iter = rows_.find(row);
+ if (iter != rows_.end())
+ (*iter).second.get_annotation_subset(dest,
+ start_sample, end_sample);
+}
+
+void DecodeSignal::save_settings(QSettings &settings) const
+{
+ SignalBase::save_settings(settings);
+
+ // TODO Save decoder stack, channel mapping and decoder options
+}
+
+void DecodeSignal::restore_settings(QSettings &settings)
+{
+ SignalBase::restore_settings(settings);
+
+ // TODO Restore decoder stack, channel mapping and decoder options
+}
+
+uint64_t DecodeSignal::inc_annotation_count()
+{
+ return (annotation_count_++);
+}
+
+void DecodeSignal::update_channel_list()
+{
+ vector<data::DecodeChannel> prev_channels = channels_;
+ channels_.clear();
+
+ uint16_t id = 0;
+
+ // Copy existing entries, create new as needed
+ for (shared_ptr<Decoder> decoder : stack_) {
+ const srd_decoder* srd_d = decoder->decoder();
+ const GSList *l;
+
+ // Mandatory channels
+ for (l = srd_d->channels; l; l = l->next) {
+ const struct srd_channel *const pdch = (struct srd_channel *)l->data;
+ bool ch_added = false;
+
+ // Copy but update ID if this channel was in the list before
+ for (data::DecodeChannel &ch : prev_channels)
+ if (ch.pdch_ == pdch) {
+ ch.id = id++;
+ channels_.push_back(ch);
+ ch_added = true;
+ break;
+ }
+
+ if (!ch_added) {
+ // Create new entry without a mapped signal
+ data::DecodeChannel ch = {id++, false, nullptr,
+ QString::fromUtf8(pdch->name), QString::fromUtf8(pdch->desc),
+ SRD_INITIAL_PIN_SAME_AS_SAMPLE0, decoder, pdch};
+ channels_.push_back(ch);
+ }
+ }
+
+ // Optional channels
+ for (l = srd_d->opt_channels; l; l = l->next) {
+ const struct srd_channel *const pdch = (struct srd_channel *)l->data;
+ bool ch_added = false;
+
+ // Copy but update ID if this channel was in the list before
+ for (data::DecodeChannel &ch : prev_channels)
+ if (ch.pdch_ == pdch) {
+ ch.id = id++;
+ channels_.push_back(ch);
+ ch_added = true;
+ break;
+ }
+
+ if (!ch_added) {
+ // Create new entry without a mapped signal
+ data::DecodeChannel ch = {id++, true, nullptr,
+ QString::fromUtf8(pdch->name), QString::fromUtf8(pdch->desc),
+ SRD_INITIAL_PIN_SAME_AS_SAMPLE0, decoder, pdch};
+ channels_.push_back(ch);
+ }
+ }
+ }
+
+ // Invalidate the logic output data if the channel assignment changed
+ if (prev_channels.size() != channels_.size()) {
+ // The number of channels changed, there's definitely a difference
+ logic_mux_data_invalid_ = true;
+ } else {
+ // Same number but assignment may still differ, so compare all channels
+ for (size_t i = 0; i < channels_.size(); i++) {
+ const data::DecodeChannel &p_ch = prev_channels[i];
+ const data::DecodeChannel &ch = channels_[i];
+
+ if ((p_ch.pdch_ != ch.pdch_) ||
+ (p_ch.assigned_signal != ch.assigned_signal)) {
+ logic_mux_data_invalid_ = true;
+ break;
+ }
+ }
+
+ }
+
+ channels_updated();
+}
+
+void DecodeSignal::commit_decoder_channels()
+{
+ // Submit channel list to every decoder, containing only the relevant channels
+ for (shared_ptr<decode::Decoder> dec : stack_) {
+ vector<data::DecodeChannel*> channel_list;
+
+ for (data::DecodeChannel &ch : channels_)
+ if (ch.decoder_ == dec)
+ channel_list.push_back(&ch);
+
+ dec->set_channels(channel_list);
+ }
+}
+
+void DecodeSignal::mux_logic_samples(const int64_t start, const int64_t end)
+{
+ // Enforce end to be greater than start
+ if (end <= start)
+ return;
+
+ // Fetch all segments and their data
+ // TODO Currently, we assume only a single segment exists
+ vector<shared_ptr<LogicSegment> > segments;
+ vector<const uint8_t*> signal_data;
+ vector<uint8_t> signal_in_bytepos;
+ vector<uint8_t> signal_in_bitpos;
+
+ for (data::DecodeChannel &ch : channels_)
+ if (ch.assigned_signal) {
+ const shared_ptr<Logic> logic_data = ch.assigned_signal->logic_data();
+ const shared_ptr<LogicSegment> segment = logic_data->logic_segments().front();
+ segments.push_back(segment);
+ signal_data.push_back(segment->get_samples(start, end));
+
+ const int bitpos = ch.assigned_signal->logic_bit_index();
+ signal_in_bytepos.push_back(bitpos / 8);
+ signal_in_bitpos.push_back(bitpos % 8);
+ }
+
+ // Perform the muxing of signal data into the output data
+ uint8_t* output = new uint8_t[(end - start) * segment_->unit_size()];
+ unsigned int signal_count = signal_data.size();
+
+ for (int64_t sample_cnt = 0; sample_cnt < (end - start); sample_cnt++) {
+ int bitpos = 0;
+ uint8_t bytepos = 0;
+
+ const int out_sample_pos = sample_cnt * segment_->unit_size();
+ for (unsigned int i = 0; i < segment_->unit_size(); i++)
+ output[out_sample_pos + i] = 0;
+
+ for (unsigned int i = 0; i < signal_count; i++) {
+ const int in_sample_pos = sample_cnt * segments[i]->unit_size();
+ const uint8_t in_sample = 1 &
+ ((signal_data[i][in_sample_pos + signal_in_bytepos[i]]) >> (signal_in_bitpos[i]));
+
+ const uint8_t out_sample = output[out_sample_pos + bytepos];
+
+ output[out_sample_pos + bytepos] = out_sample | (in_sample << bitpos);
+
+ bitpos++;
+ if (bitpos > 7) {
+ bitpos = 0;
+ bytepos++;
+ }
+ }
+ }
+
+ segment_->append_payload(output, (end - start) * segment_->unit_size());
+ delete[] output;
+
+ for (const uint8_t* data : signal_data)
+ delete[] data;
+}
+
+void DecodeSignal::logic_mux_proc()
+{
+ do {
+
+ const uint64_t input_sample_count = get_working_sample_count();
+ const uint64_t output_sample_count = segment_->get_sample_count();
+
+ const uint64_t samples_to_process =
+ (input_sample_count > output_sample_count) ?
+ (input_sample_count - output_sample_count) : 0;
+
+ // Process the samples if necessary...
+ if (samples_to_process > 0) {
+ const uint64_t unit_size = segment_->unit_size();
+ const uint64_t chunk_sample_count = DecodeChunkLength / unit_size;
+
+ uint64_t processed_samples = 0;
+ do {
+ const uint64_t start_sample = output_sample_count + processed_samples;
+ const uint64_t sample_count =
+ min(samples_to_process - processed_samples, chunk_sample_count);
+
+ mux_logic_samples(start_sample, start_sample + sample_count);
+ processed_samples += sample_count;
+
+ // ...and process the newly muxed logic data
+ decode_input_cond_.notify_one();
+ } while (processed_samples < samples_to_process);
+ }
+
+ if (session_.get_capture_state() != Session::Stopped) {
+ // Wait for more input
+ unique_lock<mutex> logic_mux_lock(logic_mux_mutex_);
+ logic_mux_cond_.wait(logic_mux_lock);
+ }
+ } while ((session_.get_capture_state() != Session::Stopped) && !logic_mux_interrupt_);
+
+ // No more input data and session is stopped, let the decode thread
+ // process any pending data, terminate and release the global SRD mutex
+ // in order to let other decoders run
+ decode_input_cond_.notify_one();
+}
+
+void DecodeSignal::decode_data(
+ const int64_t abs_start_samplenum, const int64_t sample_count,
+ srd_session *const session)
+{
+ const int64_t unit_size = segment_->unit_size();
+ const int64_t chunk_sample_count = DecodeChunkLength / unit_size;
+
+ for (int64_t i = abs_start_samplenum;
+ !decode_interrupt_ && (i < (abs_start_samplenum + sample_count));
+ i += chunk_sample_count) {
+
+ const int64_t chunk_end = min(i + chunk_sample_count,
+ abs_start_samplenum + sample_count);
+
+ const uint8_t* chunk = segment_->get_samples(i, chunk_end);
+
+ if (srd_session_send(session, i, chunk_end, chunk,
+ (chunk_end - i) * unit_size, unit_size) != SRD_OK) {
+ error_message_ = tr("Decoder reported an error");
+ delete[] chunk;
+ break;
+ }
+ delete[] chunk;
+
+ {
+ lock_guard<mutex> lock(output_mutex_);
+ samples_decoded_ = chunk_end;
+ }
+ }
+}
+
+void DecodeSignal::decode_proc()
+{
+ srd_session *session;
+ srd_decoder_inst *prev_di = nullptr;
+
+ // Prevent any other decode threads from accessing libsigrokdecode
+ lock_guard<mutex> srd_lock(global_srd_mutex_);
+
+ // Create the session
+ srd_session_new(&session);
+ assert(session);
+
+ // Create the decoders
+ for (const shared_ptr<decode::Decoder> &dec : stack_) {
+ srd_decoder_inst *const di = dec->create_decoder_inst(session);
+
+ if (!di) {
+ error_message_ = tr("Failed to create decoder instance");
+ srd_session_destroy(session);
+ return;
+ }
+
+ if (prev_di)
+ srd_inst_stack(session, prev_di, di);
+
+ prev_di = di;
+ }
+
+ // Start the session
+ srd_session_metadata_set(session, SRD_CONF_SAMPLERATE,
+ g_variant_new_uint64(samplerate_));
+
+ srd_pd_output_callback_add(session, SRD_OUTPUT_ANN,
+ DecodeSignal::annotation_callback, this);
+
+ srd_session_start(session);
+
+ uint64_t sample_count;
+ uint64_t abs_start_samplenum = 0;
+ do {
+ // Keep processing new samples until we exhaust the input data
+ do {
+ {
+ lock_guard<mutex> input_lock(input_mutex_);
+ sample_count = segment_->get_sample_count() - abs_start_samplenum;
+ }
+
+ if (sample_count > 0) {
+ decode_data(abs_start_samplenum, sample_count, session);
+ abs_start_samplenum += sample_count;
+ }
+ } while (error_message_.isEmpty() && (sample_count > 0));
+
+ // Terminate if we exhausted the input data
+ if ((int64_t)segment_->get_sample_count() == get_working_sample_count())
+ break;
+
+ if (error_message_.isEmpty()) {
+ // Wait for new input data or an interrupt request
+ unique_lock<mutex> input_wait_lock(input_mutex_);
+ decode_input_cond_.wait(input_wait_lock);
+ }
+ } while (error_message_.isEmpty() && !decode_interrupt_);
+
+ // Make sure all annotations are known to the frontend