]> sigrok.org Git - libsigrok.git/blobdiff - src/soft-trigger.c
scpi-pps: Support for the EEZ PSU series
[libsigrok.git] / src / soft-trigger.c
index 386f8cc662ac4481ca4188f555ac09db78c45168..1f81fae54e11575d226cb1fd0aaf9fc0ef9a9ddb 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <string.h>
-#include "libsigrok.h"
+#include <libsigrok/libsigrok.h>
 #include "libsigrok-internal.h"
 
-/* @cond PRIVATE */
+/** @cond PRIVATE */
 #define LOG_PREFIX "soft-trigger"
-/* @endcond */
+/** @endcond */
+
+SR_PRIV int logic_channel_unitsize(GSList *channels)
+{
+       int number = 0;
+       struct sr_channel *channel;
+       GSList *l;
+
+       for (l = channels; l; l = l->next) {
+               channel = l->data;
+               if (channel->type == SR_CHANNEL_LOGIC)
+                       number++;
+       }
+
+       return (number + 7) / 8;
+}
 
 SR_PRIV struct soft_trigger_logic *soft_trigger_logic_new(
-               const struct sr_dev_inst *sdi, struct sr_trigger *trigger)
+               const struct sr_dev_inst *sdi, struct sr_trigger *trigger,
+               int pre_trigger_samples)
 {
        struct soft_trigger_logic *stl;
 
        stl = g_malloc0(sizeof(struct soft_trigger_logic));
        stl->sdi = sdi;
        stl->trigger = trigger;
-       stl->unitsize = (g_slist_length(sdi->channels) + 7) / 8;
+       stl->unitsize = logic_channel_unitsize(sdi->channels);
        stl->prev_sample = g_malloc0(stl->unitsize);
+       stl->pre_trigger_size = stl->unitsize * pre_trigger_samples;
+       stl->pre_trigger_buffer = g_try_malloc(stl->pre_trigger_size);
+       if (pre_trigger_samples > 0 && !stl->pre_trigger_buffer) {
+               /*
+                * Error out if g_try_malloc() failed (or was invoked as
+                * g_try_malloc(0)) *and* more than 0 pretrigger samples
+                * were requested.
+                */
+               soft_trigger_logic_free(stl);
+               return NULL;
+       }
+       stl->pre_trigger_head = stl->pre_trigger_buffer;
+
+       if (stl->pre_trigger_size > 0 && !stl->pre_trigger_buffer) {
+               soft_trigger_logic_free(stl);
+               return NULL;
+       }
 
        return stl;
 }
 
 SR_PRIV void soft_trigger_logic_free(struct soft_trigger_logic *stl)
 {
+       g_free(stl->pre_trigger_buffer);
        g_free(stl->prev_sample);
        g_free(stl);
 }
 
+static void pre_trigger_append(struct soft_trigger_logic *stl,
+               uint8_t *buf, int len)
+{
+       /* Avoid uselessly copying more than the pre-trigger size. */
+       if (len > stl->pre_trigger_size) {
+               buf += len - stl->pre_trigger_size;
+               len = stl->pre_trigger_size;
+       }
+
+       /* Update the filling level of the pre-trigger circular buffer. */
+       stl->pre_trigger_fill = MIN(stl->pre_trigger_fill + len,
+                                   stl->pre_trigger_size);
+
+       /* Actually copy data to the pre-trigger circular buffer. */
+       while (len > 0) {
+               size_t size = MIN(stl->pre_trigger_buffer + stl->pre_trigger_size
+                                 - stl->pre_trigger_head, len);
+               memcpy(stl->pre_trigger_head, buf, size);
+               stl->pre_trigger_head += size;
+               if (stl->pre_trigger_head >= stl->pre_trigger_buffer
+                                            + stl->pre_trigger_size)
+                       stl->pre_trigger_head = stl->pre_trigger_buffer;
+               buf += size;
+               len -= size;
+       }
+}
+
+static void pre_trigger_send(struct soft_trigger_logic *stl,
+               int *pre_trigger_samples)
+{
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_logic logic;
+
+       packet.type = SR_DF_LOGIC;
+       packet.payload = &logic;
+       logic.unitsize = stl->unitsize;
+
+       if (pre_trigger_samples)
+               *pre_trigger_samples = 0;
+
+       /* If pre-trigger buffer not full, rewind head to the first valid sample. */
+       if (stl->pre_trigger_fill < stl->pre_trigger_size)
+               stl->pre_trigger_head = stl->pre_trigger_buffer;
+
+       /* Send logic packets for the pre-trigger circular buffer content. */
+       while (stl->pre_trigger_fill > 0) {
+               size_t size = MIN(stl->pre_trigger_buffer + stl->pre_trigger_size
+                                 - stl->pre_trigger_head, stl->pre_trigger_fill);
+               logic.length = size;
+               logic.data = stl->pre_trigger_head;
+               sr_session_send(stl->sdi, &packet);
+               stl->pre_trigger_head = stl->pre_trigger_buffer;
+               stl->pre_trigger_fill -= size;
+               if (pre_trigger_samples)
+                       *pre_trigger_samples += size / stl->unitsize;
+       }
+}
+
 static gboolean logic_check_match(struct soft_trigger_logic *stl,
                uint8_t *sample, struct sr_trigger_match *match)
 {
@@ -80,9 +173,8 @@ static gboolean logic_check_match(struct soft_trigger_logic *stl,
 /* Returns the offset (in samples) within buf of where the trigger
  * occurred, or -1 if not triggered. */
 SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *stl,
-               uint8_t *buf, int len)
+               uint8_t *buf, int len, int *pre_trigger_samples)
 {
-       struct sr_datafeed_packet packet;
        struct sr_trigger_stage *stage;
        struct sr_trigger_match *match;
        GSList *l, *l_stage;
@@ -116,12 +208,14 @@ SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *stl,
                                /* Advance to next stage. */
                                stl->cur_stage++;
                        } else {
-                               /* Matched on last stage, fire trigger. */
+                               /* Matched on last stage, send pre-trigger data. */
+                               pre_trigger_append(stl, buf, i);
+                               pre_trigger_send(stl, pre_trigger_samples);
+
+                               /* Fire trigger. */
                                offset = i / stl->unitsize;
 
-                               packet.type = SR_DF_TRIGGER;
-                               packet.payload = NULL;
-                               sr_session_send(stl->sdi, &packet);
+                               std_session_send_df_trigger(stl->sdi);
                                break;
                        }
                } else if (stl->cur_stage > 0) {
@@ -142,6 +236,8 @@ SR_PRIV int soft_trigger_logic_check(struct soft_trigger_logic *stl,
                }
        }
 
+       if (offset == -1)
+               pre_trigger_append(stl, buf, len);
+
        return offset;
 }
-