From: Matthias Weber <[email protected]>

My first patch ever. It implements sigrok API and protocol for our logic
analyzer. More information about the hardware and firmware have been
posted to the mailinglist before.

Signed-off-by: Matthias Weber <[email protected]>
---
 configure.ac                 |   12 ++
 hardware/Makefile.am         |    5 +
 hardware/hsa-tple/api.c      |  481 ++++++++++++++++++++++++++++++++++++++++++
 hardware/hsa-tple/protocol.c |  381 +++++++++++++++++++++++++++++++++
 hardware/hsa-tple/protocol.h |  118 +++++++++++
 hwdriver.c                   |    6 +
 6 files changed, 1003 insertions(+)
 create mode 100644 hardware/hsa-tple/api.c
 create mode 100644 hardware/hsa-tple/protocol.c
 create mode 100644 hardware/hsa-tple/protocol.h

diff --git a/configure.ac b/configure.ac
index 42704dd..aa4e659 100644
--- a/configure.ac
+++ b/configure.ac
@@ -235,6 +235,11 @@ AC_ARG_ENABLE(zeroplus-logic-cube,
        [HW_ZEROPLUS_LOGIC_CUBE="$enableval"],
        [HW_ZEROPLUS_LOGIC_CUBE=$HW_ENABLED_DEFAULT])
 
+AC_ARG_ENABLE(hsa-tple, AC_HELP_STRING([--enable-hsa-tple],
+       [enable hsa-tple support [default=yes]]),
+       [HW_HSA_TPLE="$enableval"],
+       [HW_HSA_TPLE=$HW_ENABLED_DEFAULT])
+
 # Checks for libraries.
 
 # This variable collects the pkg-config names of all detected libs.
@@ -503,6 +508,11 @@ if test "x$HW_ZEROPLUS_LOGIC_CUBE" = "xyes"; then
        AC_DEFINE(HAVE_HW_ZEROPLUS_LOGIC_CUBE, 1, [ZEROPLUS Logic Cube support])
 fi
 
+AM_CONDITIONAL(HW_HSA_TPLE, test x$HW_HSA_TPLE = xyes)
+if test "x$HW_HSA_TPLE" = "xyes"; then
+       AC_DEFINE(HAVE_HW_HSA_TPLE, 1, [hsa-tple support])
+fi
+
 # Checks for header files.
 # These are already checked: inttypes.h stdint.h stdlib.h string.h unistd.h.
 AC_CHECK_HEADERS([fcntl.h sys/time.h termios.h])
@@ -570,6 +580,7 @@ AC_CONFIG_FILES([Makefile version.h hardware/Makefile
                 hardware/uni-t-dmm/Makefile
                 hardware/uni-t-ut32x/Makefile
                 hardware/zeroplus-logic-cube/Makefile
+                hardware/hsa-tple/Makefile
                 input/Makefile
                 output/Makefile
                 output/text/Makefile
@@ -617,6 +628,7 @@ echo "  - fluke-dmm....................... $HW_FLUKE_DMM"
 echo "  - fx2lafw......................... $HW_FX2LAFW"
 echo "  - gmc-mh-1x-2x.................... $HW_GMC_MH_1X_2X"
 echo "  - hantek-dso...................... $HW_HANTEK_DSO"
+echo "  - hsa-tple........................ $HW_HSA_TPLE"
 echo "  - ikalogic-scanalogic2............ $HW_IKALOGIC_SCANALOGIC2"
 echo "  - ikalogic-scanaplus.............. $HW_IKALOGIC_SCANAPLUS"
 echo "  - kecheng-kc-330b................. $HW_KECHENG_KC_330B"
diff --git a/hardware/Makefile.am b/hardware/Makefile.am
index fec4579..2703fb2 100644
--- a/hardware/Makefile.am
+++ b/hardware/Makefile.am
@@ -33,6 +33,7 @@ SUBDIRS = \
        fx2lafw \
        gmc-mh-1x-2x \
        hantek-dso \
+       hsa-tple \
        ikalogic-scanalogic2 \
        ikalogic-scanaplus \
        kecheng-kc-330b \
@@ -110,6 +111,10 @@ if HW_HANTEK_DSO
 libsigrokhardware_la_LIBADD += hantek-dso/libsigrok_hw_hantek_dso.la
 endif
 
+if HW_HSA_TPLE
+libsigrokhardware_la_LIBADD += hsa-tple/libsigrok_hw_hsa_tple.la
+endif
+
 if HW_IKALOGIC_SCANALOGIC2
 libsigrokhardware_la_LIBADD += 
ikalogic-scanalogic2/libsigrok_hw_ikalogic_scanalogic2.la
 endif
diff --git a/hardware/hsa-tple/api.c b/hardware/hsa-tple/api.c
new file mode 100644
index 0000000..b2a495f
--- /dev/null
+++ b/hardware/hsa-tple/api.c
@@ -0,0 +1,481 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013-2014 Matthias Weber <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+#include <glib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+*/
+#include <string.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h" /* needed for sr_serial_dev_inst_new */
+#include "protocol.h"
+
+/* The HSA TPLE uses this */
+#define SERIALCOMM "9600/8n1"
+
+// HSA TPLE currently only supports 8 probes (will be up to 24 in the future)
+#define NUM_PROBES 8
+
+/* Note: The hsa-tple always samples at 6.25 MHz. */
+static const uint64_t samplerate = SR_MHZ(6.25);
+
+/* We name the probes 0-7 on our driver. */
+static const char *probe_names[NUM_PROBES + 1] = {
+       "0", "1", "2", "3", "4", "5", "6", "7",
+       NULL,
+};
+
+static const int32_t hwopts[] = {
+       SR_CONF_CONN,        // Specification on how to connect to a device.
+       SR_CONF_SERIALCOMM,  // Serial communication specification
+       /*SR_CONF_SAMPLERATE // device supports setting its samplerate, in Hz */
+       /*SR_CONF_RLE        // device supports Run Length Encoding */
+};
+
+static const int32_t hwcaps[] = {
+       SR_CONF_LOGIC_ANALYZER, // device class (libsigrok.h)
+       SR_CONF_LIMIT_SAMPLES,
+       SR_CONF_LIMIT_MSEC,
+};
+
+SR_PRIV struct sr_dev_driver hsa_tple_driver_info;
+static struct sr_dev_driver *di = &hsa_tple_driver_info;
+
+static int init(struct sr_context *sr_ctx)
+{
+       return std_init(sr_ctx, di, LOG_PREFIX);
+}
+
+static GSList *scan(GSList *options)
+{
+       struct drv_context *drvc;
+       struct dev_context *devc;
+       struct sr_config *src;
+       struct sr_probe *probe; 
+       struct sr_dev_inst *sdi;
+       struct sr_serial_dev_inst *serial;
+       GSList *devices, *l;
+       GPollFD probefd;
+       const char *conn, *serialcomm;
+       int len, i;
+       char *buf;
+       (void)options;
+
+       drvc = di->priv;
+       drvc->instances = NULL;
+
+       devices = NULL;
+       conn = serialcomm = NULL;
+
+       sr_dbg("Scanning for hsa-tple devices...");
+
+       // check for options
+       for (l = options; l; l = l->next) {
+               src = l->data;
+               switch (src->key) {
+               case SR_CONF_CONN:
+                       conn = g_variant_get_string(src->data, NULL);
+                       sr_dbg("SR_CONF_CONN option given.");
+                       break;
+               case SR_CONF_SERIALCOMM:
+                       serialcomm = g_variant_get_string(src->data, NULL);
+                       sr_dbg("SR_CONF_SERIALCOMM option given.");
+                       break;
+               }
+       }
+       if (!conn)
+               return NULL;
+       if (!serialcomm)
+               serialcomm = SERIALCOMM; // if no comm specs provided try the 
default
+       
+       if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
+       {
+               sr_dbg("sr_serial_dev_inst_new failed.");
+               return NULL;
+       }
+
+       if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+       {
+               sr_dbg("serial_open failed.");
+               return NULL;
+       }
+
+       serial_flush(serial);
+
+       //  let's check if the device is really a hsa-tple device!
+       // 'i' is arranged by the protocol as identify command  
+       if (serial_write(serial, "i", 1) == -1) {
+               sr_err("Unable to send identification string.");
+               return NULL;
+       }
+       else
+               sr_dbg("Identification command was sent.");
+       
+       // response should be "HSA-TPLE"!
+       len = 128;
+       if (!(buf = g_try_malloc(len))) {
+               sr_err("Serial buffer malloc failed.");
+               return NULL;
+       }
+
+       /* Wait 100ms for a response. */
+       g_usleep(100000);
+
+       probefd.fd = serial->fd;
+       probefd.events = G_IO_IN;
+       g_poll(&probefd, 1, 1);
+
+       if (probefd.revents != G_IO_IN) {
+               sr_spew("No G_IO_IN event!");
+               return NULL;
+       }
+       if (serial_read(serial, buf, 8) != 8) {
+               sr_spew("could not read 8 bytes from serial");
+               return NULL;
+       }
+       
+       if (strncmp(buf, "HSA-TPLE", 8))        {
+               sr_spew("identification string does not match");
+               g_free(buf);
+               return NULL;
+       }
+       else {
+               g_free(buf);
+               sr_spew("identification string matches");
+       }
+       
+       sr_info("Found device on port %s.", conn);
+       
+       // register the device with libsigrok   
+       if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "HS Augsburg",
+                       "TPLE", NULL))) {
+               sr_warn("new device instance could not be created");
+               return NULL;
+       }
+       sr_dbg("new device instance successfully created");
+
+       // malloc memory for the device context
+       if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
+               sr_dbg("Device context malloc failed.");
+               return NULL;
+       }
+
+       // init device context
+       devc->num_samples = 0;
+       devc->limit_samples = 0;
+       devc->limit_msec = 0;
+       devc->last_record_received = FALSE;
+       devc->first_sample_sent = 0;
+       devc->state = ST_IDENTIFIED;
+       
+       // init driver instance
+       sdi->inst_type = SR_INST_SERIAL;
+       sdi->conn = serial;
+       sdi->priv = devc;
+       sdi->driver = di;
+       
+       // let's append our probes
+       for (i = 0; probe_names[i]; i++) {
+               if (!(probe = sr_probe_new(i, SR_PROBE_LOGIC, TRUE,
+                               probe_names[i])))
+                       return NULL;
+               sdi->probes = g_slist_append(sdi->probes, probe);
+       }
+       
+       drvc->instances = g_slist_append(drvc->instances, sdi);
+       devices = g_slist_append(devices, sdi);
+       
+       serial_close(serial);
+       if (!devices)
+               sr_serial_dev_inst_free(serial);
+
+       return devices;
+}
+
+static GSList *dev_list(void)
+{
+       return ((struct drv_context *)(di->priv))->instances;
+}
+
+static int dev_clear(void)
+{
+       return std_dev_clear(di, NULL);
+}
+
+static int dev_open(struct sr_dev_inst *sdi)
+{
+       struct drv_context *drvc;
+       struct sr_serial_dev_inst *serial;
+
+       sr_dbg("opening hsa-tple device...");
+       
+       if (!(drvc = di->priv)) {
+               sr_err("dev_open: driver was not initialized.");
+               return SR_ERR;
+       }
+       
+       serial = sdi->conn;
+       if (serial_open(serial, SERIAL_RDWR | SERIAL_NONBLOCK) != SR_OK)
+       {
+               sr_err("dev_open: serial device could not be opened");
+               return SR_ERR;
+       }
+
+       // FIXME/TODO: send reset command here? this only resets the CPLD!
+
+       // "The device instance is actively in use in a session."
+       sdi->status = SR_ST_ACTIVE;
+
+       return SR_OK;
+}
+
+static int dev_close(struct sr_dev_inst *sdi)
+{
+       struct sr_serial_dev_inst *serial;
+
+       if (!di->priv) {
+               sr_err("dev_close: driver was not initialized.");
+               return SR_ERR;
+       }
+               
+       sr_dbg("closing hsa-tple device...");
+
+       // get handle from sdi->conn and close it
+       serial = sdi->conn;
+       if (serial && serial->fd != -1) {
+               serial_close(serial);
+               sdi->status = SR_ST_INACTIVE;
+       }
+
+       return SR_OK;
+}
+
+static int cleanup(void)
+{
+       int ret;
+       struct drv_context *drvc;
+
+       if (!(drvc = di->priv))
+               /* Can get called on an unused driver, doesn't matter. */
+               return SR_OK;
+
+       ret = dev_clear();
+       g_free(drvc);
+       di->priv = NULL;
+
+       return ret;
+}
+
+static int config_set(int key, GVariant *data, const struct sr_dev_inst *sdi,
+               const struct sr_probe_group *probe_group)
+{
+       struct dev_context *devc;
+       int ret;
+
+       (void)data;
+       (void)probe_group;
+
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
+       
+       if (!(devc = sdi->priv)) {
+               sr_err("sdi->priv was NULL.");
+               return SR_ERR_BUG;
+       }
+       
+       ret = SR_OK;
+       switch (key) {
+       case SR_CONF_LIMIT_SAMPLES:
+               devc->limit_samples = g_variant_get_uint64(data);
+               devc->limit_msec = 
(uint64_t)((double)devc->limit_samples/(double)samplerate*1000);
+               if( devc->limit_msec == 0 )
+                       devc->limit_msec = 1;
+
+               sr_dbg("samplerate: %d", samplerate);
+               sr_dbg("calculated devc->limit_msec: %d", devc->limit_msec);
+               break;
+       case SR_CONF_LIMIT_MSEC:
+               devc->limit_msec = g_variant_get_uint64(data);
+               devc->limit_samples = 
(uint64_t)(samplerate*((double)devc->limit_msec/1000));
+               if( devc->limit_samples == 0 )
+                       devc->limit_samples = 1;
+               
+               sr_dbg("samplerate: %d", samplerate);
+               sr_dbg("calculated devc->limit_samples: %d", 
devc->limit_samples);
+               break;
+       default:
+               ret = SR_ERR_NA;
+       }
+
+       return ret;
+}
+
+// config_get fixes vcd problem
+static int config_get(int id, GVariant **data, const struct sr_dev_inst *sdi,
+               const struct sr_probe_group *probe_group)
+{
+       (void)sdi;
+       (void)probe_group;
+
+       switch (id) {
+       /*
+       case SR_CONF_LIMIT_SAMPLES:
+               *data = g_variant_new_uint64(devc->limit_samples);
+               break;
+       case SR_CONF_LIMIT_MSEC:
+               *data = g_variant_new_uint64(devc->limit_msec);
+               break;
+       case SR_CONF_NUM_TIMEBASE:
+               *data = g_variant_new_int32(devc->num_timebases);
+               break;*/
+       case SR_CONF_SAMPLERATE:
+               /* The tple samplerate is 62.5MHz and can't be changed. */
+               *data = g_variant_new_uint64(SR_MHZ(6.25));
+               break;
+       default:
+               return SR_ERR_NA;
+       }
+
+       return SR_OK;
+}
+
+static int config_list(int key, GVariant **data, const struct sr_dev_inst *sdi,
+               const struct sr_probe_group *probe_group)
+{
+       int ret;
+
+       (void)sdi;
+       (void)data;
+       (void)probe_group;
+
+       ret = SR_OK;
+       switch (key) {
+       case SR_CONF_DEVICE_OPTIONS:
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+                               hwcaps, ARRAY_SIZE(hwcaps), sizeof(int32_t));
+               break;
+       case SR_CONF_SCAN_OPTIONS:
+               *data = g_variant_new_fixed_array(G_VARIANT_TYPE_INT32,
+                               hwopts, ARRAY_SIZE(hwopts), sizeof(int32_t));
+               break;
+       // case SR_CONF_SAMPLERATE:
+       //      ...
+       //      break;
+       default:
+               return SR_ERR_NA;
+       }
+
+
+       return ret;
+}
+
+static int dev_acquisition_start(const struct sr_dev_inst *sdi,
+                                   void *cb_data)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+       
+       (void)sdi;
+       (void)cb_data;
+
+       if (!sdi || !cb_data || !(devc = sdi->priv))
+               return SR_ERR_BUG;
+       
+       if (sdi->status != SR_ST_ACTIVE)
+               return SR_ERR_DEV_CLOSED;
+
+       /*
+        * Reset the number of samples to take. If we've already collected our
+        * quota, but we start a new session, and don't reset this, we'll just
+        * quit without acquiring any new samples.
+        */
+       devc->num_samples = 0; // set number of samples to zero
+       devc->last_record_received = FALSE;
+       devc->cb_data = cb_data;
+       devc->buflen = 0;
+       devc->first_sample_sent = 0;
+
+       // start acquisition
+       sr_dbg("Acquisition has started now!");
+       devc->state = ST_START;
+
+       /* Send header packet to the session bus. */
+       std_session_send_df_header(cb_data, LOG_PREFIX);
+
+       /* Poll every 50ms, or whenever some data comes in. */
+       serial = sdi->conn;
+       sr_source_add(serial->fd, G_IO_IN, 50, hsa_tple_receive_data, (void 
*)sdi);
+       
+       return SR_OK;
+}
+
+
+static int dev_acquisition_stop(struct sr_dev_inst *sdi, void *cb_data)
+{
+       struct dev_context *devc;
+       struct sr_datafeed_packet packet;
+
+       sr_dbg("Stopping acquisition.");
+       
+       if (!di->priv) {
+               sr_err("Driver was not initialized.");
+               return SR_ERR;
+       }
+
+       if (sdi->status != SR_ST_ACTIVE) {
+               sr_spew("sdi->status: %d", sdi->status);                
+               sr_err("Device inactive, can't stop acquisition.");
+               return SR_ERR;
+       }
+       //sdi->status = SR_ST_STOPPING; // makes std_... function fail
+       
+       devc = sdi->priv;
+       devc->cb_data = cb_data;
+
+       // TODO/FIXME: sr_source_remove? channel remove?
+       
+       /* Send end packet to the session bus. */
+       sr_dbg("Sending SR_DF_END packet.");
+       packet.type = SR_DF_END;
+       sr_session_send(cb_data, &packet);
+
+       return std_dev_acquisition_stop_serial(sdi, cb_data, dev_close, 
sdi->conn, LOG_PREFIX);
+}
+
+SR_PRIV struct sr_dev_driver hsa_tple_driver_info = {
+       .name = "hsa-tple",
+       .longname = "HS Augsburg tple-logicanalyzer",
+       .api_version = 1,
+       .init = init,
+       .cleanup = cleanup,
+       .scan = scan,
+       .dev_list = dev_list,
+       .dev_clear = dev_clear,
+       .config_get = config_get,
+       .config_set = config_set,
+       .config_list = config_list,
+       .dev_open = dev_open,
+       .dev_close = dev_close,
+       .dev_acquisition_start = dev_acquisition_start,
+       .dev_acquisition_stop = dev_acquisition_stop,
+       .priv = NULL,
+};
diff --git a/hardware/hsa-tple/protocol.c b/hardware/hsa-tple/protocol.c
new file mode 100644
index 0000000..80850a8
--- /dev/null
+++ b/hardware/hsa-tple/protocol.c
@@ -0,0 +1,381 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013-2014 Matthias Weber <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "protocol.h"
+
+/* one packet consists of 32 bit of data */
+struct data_packet {
+       uint8_t  data;
+       uint16_t timestamp;
+       uint8_t  status;
+};
+
+/* send command to the device, this is one ASCII token */
+SR_PRIV int send_command(struct sr_serial_dev_inst *serial, uint8_t command)
+{
+       char buf[1];
+
+       sr_dbg("Sending cmd 0x%.2x / '%c'.", command, command);
+       buf[0] = command;
+       serial_flush(serial);
+       if (serial_write(serial, buf, 1) != 1) {
+               sr_spew("could not write serial");
+               return SR_ERR;
+       }
+
+       sr_spew("serial written, command sent");
+       return SR_OK;
+}
+
+// parse raw data on the serial interface and get data records out of it
+static int packet_parse(struct sr_dev_inst *sdi, struct data_packet *packet)
+{
+       struct dev_context *devc;
+       char strbuf[128];
+
+       devc = sdi->priv;
+
+       // we need to work with blocks of 4 byte
+       if(devc->buflen < 4) {
+               sr_spew("  packet_parse: PACKET_NEED_MORE_DATA");
+               return PACKET_NEED_MORE_DATA;
+       }
+       
+       // byte 4 represents the status byte
+       packet->status = devc->buf[3];
+       packet->data = devc->buf[0];
+       packet->timestamp = (devc->buf[1] << 8) | devc->buf[2];
+       
+       sprintf(strbuf, "  RAW: t: 0x%04X / %d, d: 0x%02X, s: 0x%02X", 
packet->timestamp, packet->timestamp, packet->data, packet->status);
+       sr_spew("%s", strbuf);
+
+       // FIXME: don't ignore invalid packets
+       if( (packet->status != DATA_RECORD_MORE) &&
+            (packet->status != DATA_RECORD_LAST) ) {
+               sr_warn("  packet_parse: PACKET_INVALID");
+               //while(1);
+               //return PACKET_INVALID;
+       }
+       
+
+       // TODO: correct implementation of timestamp overflow (if needed)
+       if( packet->timestamp == (uint16_t)0xFFFF ) {
+               sr_spew("  /!\\ Timestamp overflow detected");
+       }
+
+       // modify buffer! If we have any data left, move it to the beginning of 
our buffer.
+       // dest, source, length
+       memmove(devc->buf, devc->buf + 4, devc->buflen - 4);
+       devc->buflen -= 4;
+       
+       devc->num_samples++;
+       
+       sr_spew("  packet_parse: PACKET_OK");
+       return PACKET_OK;
+}
+
+// interprete and send data packets as packets to the session bus
+static int handle_packet(struct sr_dev_inst *sdi)
+{
+       struct sr_datafeed_packet packet;
+       struct sr_datafeed_logic logic;
+       struct dev_context *devc;
+       struct data_packet dpacket;
+
+       devc = sdi->priv;
+
+       while( packet_parse(sdi, &dpacket) == PACKET_OK ) {
+               memset(&logic, 0, sizeof(struct sr_datafeed_logic) );
+
+               packet.type = SR_DF_LOGIC;
+               packet.payload = &logic;
+       
+               logic.length = 1;
+               logic.unitsize = 1;
+               logic.data = &dpacket.data;
+               sr_session_send(devc->cb_data, &packet); // FIXME: how to set 
correct timestamp/ implement RLE?
+               
+               if( dpacket.status == DATA_RECORD_LAST ) {      
+                       devc->last_record_received = TRUE;
+                       break;
+               }
+       }
+
+       return SR_OK;
+}
+
+/** handle new data bytes on serial interface */
+static void handle_new_data(struct sr_dev_inst *sdi)
+{
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+       uint64_t time;
+       int len;
+       int bufferempty;
+       char c = 0; // single character buffer
+
+       devc = sdi->priv;
+       serial = sdi->conn;
+       
+       /* Try to get as much data as the buffer can hold. */
+       len = BUFSIZE - devc->buflen;
+       if (len < 0) {
+               sr_err("Buffer overflow.");
+               return;
+       }
+       
+       len = serial_read(serial, devc->buf + devc->buflen, len);
+       if (len < 0) {
+               sr_err("Serial port read error: %d.", len);
+               return;
+       }
+       else {
+               sr_spew("%d Bytes read from serial", len);
+       }
+       devc->buflen += len;
+       
+       if( devc->buflen > 0 ) {
+               bufferempty = FALSE;
+       }
+       else
+       {
+               bufferempty = TRUE;
+       }
+
+       //sr_spew("buffer length: %d", devc->buflen);   
+       //sr_spew("handle new data, state: %d", devc->state);
+       
+       
+       /* Now try to interprete this data, dependent from the state of 
communication. */
+       if (devc->state == ST_IDENTIFIED) {
+               sr_spew("ST_IDENTIFIED should be ST_START instead");
+       }
+       else if (devc->state == ST_START) {
+               sr_spew("ST_START");
+               devc->state = ST_SENDCMD_START_MEASUREMENT;
+       }
+       else if (devc->state == ST_SENDCMD_START_MEASUREMENT) {
+               sr_spew("ST_SENDCMD_START_MEASUREMENT");
+
+               // send command to start measurement
+               if( send_command(serial, CMD_GO) == SR_OK ) {
+                       // set start time of acquisition session; FIXME: wait 
for trigger signal!
+                       devc->starttime = g_get_monotonic_time();
+                       sr_spew("  start time: %d", devc->starttime);
+
+                       sr_spew("  next state: ST_GETRESP_START_MEASUREMENT");
+                       devc->state = ST_GETRESP_START_MEASUREMENT;
+               }
+               else {
+                       sr_spew("  unable to send command: ST_COM_ERROR");
+                       devc->state = ST_COM_ERROR;
+               }               
+       }
+       else if (devc->state == ST_GETRESP_START_MEASUREMENT) {
+               sr_spew("ST_GETRESP_START_MEASUREMENT");
+
+               // get response of start measurement command
+               if( !bufferempty ) {
+                       c = devc->buf[0];
+                       devc->buflen = 0; // buffer can be overwritten, so 
index will be 0 again
+                       if( c == STATUS_MEASUREMENT_STARTED ) {
+                               sr_spew("  received: 
STATUS_MEASUREMENT_STARTED");
+                               devc->state = ST_WAIT_ENDCONDITIONS;
+                       }
+                       else if( c == STATUS_MEASUREMENT_NOT_STARTED ) {
+                               sr_spew("  received: 
STATUS_MEASUREMENT_NOT_STARTED");
+                               // TODO: retry
+                               devc->state = ST_COM_ERROR;
+                       }
+                       else {
+                               sr_spew("  received: unknown STATUS");
+                               devc->state = ST_COM_ERROR;
+                       }
+               }
+       }
+       else if (devc->state == ST_WAIT_ENDCONDITIONS) {
+               sr_spew("ST_WAIT_ENDCONDITIONS");
+               // wait until our end conditions are met
+               
+               // check if we got a character
+               if( !bufferempty ) {
+                       c = devc->buf[0];
+                       devc->buflen = 0; // buffer can be overwritten, so 
index will be 0 again
+                       if( c == STATUS_MEMFULL ) {
+                               sr_spew("  received: STATUS_MEMFULL, next 
state: ST_SENDCMD_STOP_MEASUREMENT");
+                               devc->state = ST_SENDCMD_STOP_MEASUREMENT;
+                       }
+                       else {
+                               sr_spew("  next state: ST_COM_ERROR");
+                               devc->state = ST_COM_ERROR;
+                       }
+               }
+               // or if time has elapsed
+               else {
+                       time = g_get_monotonic_time();
+                       sr_spew("  start time: %d:", devc->starttime);
+                       sr_spew("  current time: %d", devc->starttime);
+                       time = time - devc->starttime;
+                       sr_spew("  difference: %d", time);
+                       sr_spew("  time limit msec: %d", devc->limit_msec);
+                       
+                       // both are times in milliseconds (hopefully)
+                       if( time > devc->limit_msec ) {
+                               sr_spew("  time limit exceeded; next state: 
ST_SENDCMD_STOP_MEASUREMENT");
+                               devc->state = ST_SENDCMD_STOP_MEASUREMENT;
+                       }
+               }
+       }
+       else if (devc->state == ST_SENDCMD_STOP_MEASUREMENT) {
+               sr_spew("ST_SENDCMD_STOP_MEASUREMENT");
+               
+               // send command to stop measurement
+               if( send_command(serial, CMD_STOP) == SR_OK ) {
+                       devc->state = ST_GETRESP_STOP_MEASUREMENT;
+                       sr_spew("  sent: CMD_STOP command");
+               }
+               else {
+                       devc->state = ST_COM_ERROR;
+                       sr_spew("  command CMD_STOP could not be sent");
+               }               
+       }
+       else if (devc->state == ST_GETRESP_STOP_MEASUREMENT) {
+               sr_spew("ST_GETRESP_STOP_MEASUREMENT");
+               
+               // get response of stop measurement command             
+               if( !bufferempty ) {
+                       c = devc->buf[0];
+                       devc->buflen = 0; // buffer can be overwritten, so 
index will be 0 again
+                       if( c == STATUS_MEASUREMENT_NOT_STARTED ) {
+                               sr_spew("  received: 
STATUS_MEASUREMENT_NOT_STARTED = measurement stopped");
+                               sr_spew("  next state: ST_SENDCMD_DUMP_DATA");
+                               devc->state = ST_SENDCMD_DUMP_DATA;
+                       }
+                       else if( c == STATUS_MEASUREMENT_STARTED ) {
+                               // this is: still running; TODO: retry stopping
+                               sr_spew("  received: 
STATUS_MEASUREMENT_STARTED");
+                               devc->state = ST_COM_ERROR;
+                       }
+                       else {
+                               sr_spew("  received: unknown STATUS");
+                               devc->state = ST_COM_ERROR;
+                       }
+               }
+       }
+       else if (devc->state == ST_SENDCMD_DUMP_DATA) {
+               sr_spew("ST_SENDCMD_DUMP_DATA");
+               
+               // send command to dump data
+               if( send_command(serial, CMD_DUMP) == SR_OK ) {
+                       sr_spew("  sent: CMD_DUMP command, next state: 
ST_GETRESP_DUMP_DATA");
+                       devc->state = ST_GETRESP_DUMP_DATA;
+               }
+               else {
+                       sr_spew("  command CMD_DUMP could not be sent");
+                       devc->state = ST_COM_ERROR;
+               }               
+       }
+       else if (devc->state == ST_GETRESP_DUMP_DATA) {
+               sr_spew("ST_GETRESP_DUMP_DATA");
+               
+               // get response of dump data command
+               if( !bufferempty ) {
+                       c = devc->buf[0];
+                       // devc->buflen = 0; // there might be useful data!
+                       memmove(devc->buf, devc->buf + 1, devc->buflen - 1);
+                       devc->buflen -= 1;
+                                       
+                       if( c == STATUS_MEASUREMENT_NOT_STARTED ) {
+                               sr_spew("  received: 
STATUS_MEASUREMENT_NOT_STARTED");
+                               devc->state = ST_GET_DUMP;
+                       }
+                       else if( c == STATUS_MEASUREMENT_STARTED ) {
+                               // this is: still running; TODO: retry 
stopping/ dumping
+                               sr_spew("  received: 
STATUS_MEASUREMENT_STARTED");
+                               devc->state = ST_COM_ERROR;
+                       }
+                       else {
+                               sr_spew("  received: unknown STATUS");
+                               devc->state = ST_COM_ERROR;
+                       }
+               }
+       }
+       else if (devc->state == ST_GET_DUMP) {
+               sr_spew("ST_GET_DUMP");
+
+               // get raw data which gets dumped
+               handle_packet(sdi);
+               if( devc->last_record_received )
+               {
+                       // FIXME/HELPME: this number seems to be a bit low?!
+                       sr_spew("  devc->num_samples: %d", devc->num_samples);
+
+                       sr_spew("  received last record, next state: 
ST_FINISHED");
+                       devc->state = ST_FINISHED;
+               }
+       }
+       else if (devc->state == ST_FINISHED) {
+               sr_spew("ST_FINISHED");
+               // data dump has finished, now send all data? currently already 
sent before!
+       }
+       else if (devc->state == ST_COM_ERROR) {
+               sr_spew("ST_COM_ERROR");
+
+               // communication error: unexpected response from device
+               sr_dbg("  Communication error: unexpected response from 
device");
+       }
+}
+
+// hsa_tple_receive_data from serial interface, this function gets polled
+SR_PRIV int hsa_tple_receive_data(int fd, int revents, void *cb_data)
+{
+       struct sr_dev_inst *sdi;
+       struct dev_context *devc;
+       struct sr_serial_dev_inst *serial;
+
+       (void)serial;
+       (void)fd;
+       (void)revents;
+
+       if (!(sdi = cb_data))
+               return TRUE;
+
+       if (!(devc = sdi->priv))
+               return TRUE;
+
+       serial = sdi->conn;
+
+       // check if finished -- before we handle new data
+       if (devc->state == ST_FINISHED) {
+               sdi->driver->dev_acquisition_stop(sdi, cb_data);
+               sr_spew("hsa_tple_receive_data returns successfully");
+               return TRUE;
+       }
+       else if (devc->state == ST_COM_ERROR) {
+               sr_spew("hsa_tple_receive_data returns with an error");
+               return FALSE;
+       }
+
+       // don't care if serial data has arrived or not
+       // (some states require also change after time -- at least to calculate 
end of measurement)
+       handle_new_data(sdi);
+
+       return TRUE;
+}
+
diff --git a/hardware/hsa-tple/protocol.h b/hardware/hsa-tple/protocol.h
new file mode 100644
index 0000000..5c88ebd
--- /dev/null
+++ b/hardware/hsa-tple/protocol.h
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the libsigrok project.
+ *
+ * Copyright (C) 2013-2014 Matthias Weber <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBSIGROK_HARDWARE_HSA_TPLE_PROTOCOL_H
+#define LIBSIGROK_HARDWARE_HSA_TPLE_PROTOCOL_H
+
+#include <stdint.h>
+#include <string.h>
+#include <glib.h>
+#include "libsigrok.h"
+#include "libsigrok-internal.h"
+
+/* Message logging helpers with subsystem-specific prefix string. */
+#define LOG_PREFIX "hsa-tple: "
+#define sr_log(l, s, args...) sr_log(l, LOG_PREFIX s, ## args)
+#define sr_spew(s, args...) sr_spew(LOG_PREFIX s, ## args)
+#define sr_dbg(s, args...) sr_dbg(LOG_PREFIX s, ## args)
+#define sr_info(s, args...) sr_info(LOG_PREFIX s, ## args)
+#define sr_warn(s, args...) sr_warn(LOG_PREFIX s, ## args)
+#define sr_err(s, args...) sr_err(LOG_PREFIX s, ## args)
+
+/* Command opcodes (ASCII tokens) */
+#define CMD_IDENTIFY   'i'  // identify
+#define CMD_RESET      'r'  // reset device
+#define CMD_GO         'g'  // start measurement
+#define CMD_STOP       's'  // stop measurement
+#define CMD_DUMP       'd'  // dump data
+#define CMD_TXTDUMP    'D'  // dump data as VCD in ASCII format (debug dump)
+#define CMD_IDENTIFY   'i'  // prompt the device to identify itself as 
'HSA-TPLE'
+#define CMD_GETSTATUS  'S'  // request status information
+
+/* Status codes (ASCII tokens) */
+#define STATUS_MEMFULL                 'f' // RAM memory full
+#define STATUS_MEASUREMENT_STARTED     'r' // aka running
+#define STATUS_MEASUREMENT_NOT_STARTED 's' // aka stopped
+
+/* Record status byte codes */
+#define DATA_RECORD_MORE (uint8_t)0x00
+#define DATA_RECORD_LAST (uint8_t)0xFF
+
+/* Size of internal (host-side) serial buffer */
+#define BUFSIZE 1024
+
+/** Private, per-device-instance driver context. */
+struct dev_context {
+       /* --- Acquisition settings ------------------------------ */
+
+       /** The current sampling limit (in number of samples). */
+       uint64_t limit_samples;
+
+       /** The current sampling limit (in ms). */
+       uint64_t limit_msec;
+       
+       /* --- Operational state --------------------------------- */
+       int state;
+
+       /** The current number of already received (this will be "sent" for us) 
samples. */
+       uint64_t num_samples; // aka sample_counter
+
+       /** Start time of acquisition session */
+       int64_t starttime;
+
+       /** Opaque pointer passed in by the frontend. */
+       void *cb_data; // "callback data"
+       
+       /** host side sample buffer */
+       uint8_t buf[BUFSIZE];
+       int buflen; // current buffer length
+
+       /** flag if last record/ data packet was received, so we have finished 
*/
+       int last_record_received;
+       int first_sample_sent;
+};
+
+/* Parser state machine. */
+enum {
+       ST_UNKNOWN,
+       ST_IDENTIFIED,
+       ST_START,
+       ST_SENDCMD_RESET,
+       ST_SENDCMD_START_MEASUREMENT,
+       ST_GETRESP_START_MEASUREMENT,
+       ST_WAIT_ENDCONDITIONS,
+       ST_SENDCMD_STOP_MEASUREMENT,
+       ST_GETRESP_STOP_MEASUREMENT,
+       ST_SENDCMD_DUMP_DATA,
+       ST_GETRESP_DUMP_DATA,
+       ST_GET_DUMP,
+       ST_FINISHED,
+       ST_COM_ERROR,
+};
+
+/* Packet parser return code. */
+enum {
+       PACKET_OK,
+       PACKET_NEED_MORE_DATA,
+       PACKET_INVALID,
+};
+
+SR_PRIV int hsa_tple_receive_data(int fd, int revents, void *cb_data);
+
+#endif
diff --git a/hwdriver.c b/hwdriver.c
index 804838c..d40a376 100644
--- a/hwdriver.c
+++ b/hwdriver.c
@@ -126,6 +126,9 @@ extern SR_PRIV struct sr_dev_driver demo_driver_info;
 #ifdef HAVE_HW_GMC_MH_1X_2X
 extern SR_PRIV struct sr_dev_driver gmc_mh_1x_2x_rs232_driver_info;
 #endif
+#ifdef HAVE_HW_HSA_TPLE
+extern SR_PRIV struct sr_dev_driver hsa_tple_driver_info;
+#endif
 #ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
 extern SR_PRIV struct sr_dev_driver ikalogic_scanalogic2_driver_info;
 #endif
@@ -252,6 +255,9 @@ static struct sr_dev_driver *drivers_list[] = {
 #ifdef HAVE_HW_GMC_MH_1X_2X
        &gmc_mh_1x_2x_rs232_driver_info,
 #endif
+#ifdef HAVE_HW_HSA_TPLE
+       &hsa_tple_driver_info,
+#endif
 #ifdef HAVE_HW_IKALOGIC_SCANALOGIC2
        &ikalogic_scanalogic2_driver_info,
 #endif
-- 
1.7.9.5



------------------------------------------------------------------------------
CenturyLink Cloud: The Leader in Enterprise Cloud Services.
Learn Why More Businesses Are Choosing CenturyLink Cloud For
Critical Workloads, Development Environments & Everything In Between.
Get a Quote or Start a Free Trial Today. 
http://pubads.g.doubleclick.net/gampad/clk?id=119420431&iu=/4140/ostg.clktrk
_______________________________________________
sigrok-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sigrok-devel

Reply via email to