Pārlūkot izejas kodu

First sw commit

Per Mårtensson 2 gadi atpakaļ
vecāks
revīzija
c354662f11
52 mainītis faili ar 7066 papildinājumiem un 0 dzēšanām
  1. 39 0
      sw/esp32s2/boards/max80.json
  2. 102 0
      sw/esp32s2/boards/max80/pins_arduino.h
  3. BIN
      sw/esp32s2/bootloader_qio_80m.bin
  4. 23 0
      sw/esp32s2/get-platformio.py
  5. 308 0
      sw/esp32s2/include/ESPAsyncWiFiManager.h
  6. 5 0
      sw/esp32s2/include/events.h
  7. 12 0
      sw/esp32s2/include/max80_callback.h
  8. 65 0
      sw/esp32s2/include/max80_config.h
  9. 34 0
      sw/esp32s2/include/max80_func.h
  10. 36 0
      sw/esp32s2/include/max80_gpio.h
  11. 14 0
      sw/esp32s2/include/max80_littlefs.h
  12. 0 0
      sw/esp32s2/include/max80_ntp.h
  13. 0 0
      sw/esp32s2/include/max80_spislave.h
  14. 0 0
      sw/esp32s2/include/max80_usbserial.h
  15. 28 0
      sw/esp32s2/include/max80_web.h
  16. 154 0
      sw/esp32s2/include/max80_webcontent.h
  17. 18 0
      sw/esp32s2/include/max80_wifi.h
  18. 18 0
      sw/esp32s2/include/netlog.h
  19. 19 0
      sw/esp32s2/include/program.h
  20. 22 0
      sw/esp32s2/include/programmer.h
  21. 63 0
      sw/esp32s2/max80_esp32s2.code-workspace
  22. 40 0
      sw/esp32s2/platformio.ini
  23. 1389 0
      sw/esp32s2/src/ESPAsyncWiFiManager.cpp
  24. 3 0
      sw/esp32s2/src/events.cpp
  25. 18 0
      sw/esp32s2/src/libxsvf/COPYING
  26. 83 0
      sw/esp32s2/src/libxsvf/Makefile
  27. 365 0
      sw/esp32s2/src/libxsvf/README
  28. 141 0
      sw/esp32s2/src/libxsvf/libxsvf.h
  29. 65 0
      sw/esp32s2/src/libxsvf/memname.c
  30. 72 0
      sw/esp32s2/src/libxsvf/play.c
  31. 58 0
      sw/esp32s2/src/libxsvf/scan.c
  32. 46 0
      sw/esp32s2/src/libxsvf/statename.c
  33. 1236 0
      sw/esp32s2/src/libxsvf/svf.c
  34. 173 0
      sw/esp32s2/src/libxsvf/tap.c
  35. 486 0
      sw/esp32s2/src/libxsvf/xsvf.c
  36. 454 0
      sw/esp32s2/src/libxsvf/xsvftool-esp.c
  37. 544 0
      sw/esp32s2/src/libxsvf/xsvftool-gpio.c
  38. 78 0
      sw/esp32s2/src/main.cpp
  39. 13 0
      sw/esp32s2/src/max80_callback.cpp
  40. 159 0
      sw/esp32s2/src/max80_func.cpp
  41. 99 0
      sw/esp32s2/src/max80_gpio.cpp
  42. 0 0
      sw/esp32s2/src/max80_jtag.cpp
  43. 131 0
      sw/esp32s2/src/max80_littlefs.cpp
  44. 0 0
      sw/esp32s2/src/max80_ntp.cpp
  45. 0 0
      sw/esp32s2/src/max80_spislave.cpp
  46. 0 0
      sw/esp32s2/src/max80_usbserial.cpp
  47. 62 0
      sw/esp32s2/src/max80_web.cpp
  48. 100 0
      sw/esp32s2/src/max80_wifi.cpp
  49. 97 0
      sw/esp32s2/src/program.cpp
  50. 115 0
      sw/esp32s2/src/programmer.cpp
  51. 12 0
      sw/esp32s2/variants/max80/partitions.csv
  52. 67 0
      sw/esp32s2/variants/max80/pins_arduino.h

+ 39 - 0
sw/esp32s2/boards/max80.json

@@ -0,0 +1,39 @@
+{
+      "build": {
+      "arduino":{
+        "ldscript": "esp32s2_out.ld"
+      },
+      "core": "esp32",
+      "f_cpu": "240000000L",
+      "f_flash": "80000000L",
+      "flash_mode": "qio",
+      "boot": "qio",
+      "mcu": "esp32s2",
+      "variant": "max80"
+    },
+    "connectivity": [
+      "wifi"
+    ],
+    "debug": {
+      "openocd_target": "esp32s2.cfg"
+    },
+    "frameworks": [
+      "espidf",
+      "arduino"
+    ],
+    "bootloader": {
+      "file": "/home/pm/project/abc80/max80/sw/esp32_s2/bootloader_qio_80m.bin"
+    },
+    "name": "max80",
+    "upload": {
+      "flash_size": "4MB",
+      "maximum_ram_size": 327680,
+      "maximum_size": 4194304,
+      "require_upload_port": true,
+      "speed": 460800
+    },
+    "url": "https://git.sweproj.com/ABC80/max80",
+    "vendor": "SweProj"
+  
+}
+  

+ 102 - 0
sw/esp32s2/boards/max80/pins_arduino.h

@@ -0,0 +1,102 @@
+#ifndef Pins_Arduino_h
+#define Pins_Arduino_h
+
+#include <stdint.h>
+
+
+#define USB_VID 0x239A
+#define USB_PID 0x80DF
+#define USB_MANUFACTURER "SweProj"
+#define USB_PRODUCT "MAX80 ESP32-S2"
+#define USB_SERIAL ""
+
+
+#define EXTERNAL_NUM_INTERRUPTS 46
+#define NUM_DIGITAL_PINS        48
+#define NUM_ANALOG_INPUTS       20
+
+#define analogInputToDigitalPin(p)  (((p)<20)?(esp32_adc2gpio[(p)]):-1)
+#define digitalPinToInterrupt(p)    (((p)<48)?(p):-1)
+#define digitalPinHasPWM(p)         (p < 46)
+/*
+static const uint8_t PIN_NEOPIXEL = 1;  // D1
+static const uint8_t NEOPIXEL_POWER = 21;
+
+static const uint8_t LED_BUILTIN = 13;
+
+static const uint8_t EPD_BUSY = 5;
+static const uint8_t EPD_RESET = 6;
+static const uint8_t EPD_DC = 7;
+static const uint8_t EPD_CS = 8;
+
+static const uint8_t ACCEL_IRQ = 9;
+
+static const uint8_t BUTTON_A = 15;
+static const uint8_t BUTTON_B = 14;
+static const uint8_t BUTTON_C = 12;
+static const uint8_t BUTTON_D = 11;
+
+static const uint8_t LIGHT_SENSOR = 3;
+static const uint8_t BATT_MONITOR = 4;
+static const uint8_t SPEAKER_SHUTDOWN = 16;
+
+static const uint8_t SDA = 33;
+static const uint8_t SCL = 34;
+
+static const uint8_t SS    = 8;
+static const uint8_t MOSI  = 35;
+static const uint8_t SCK   = 36;
+static const uint8_t MISO  = 37;
+
+*/
+
+
+
+static const uint8_t TX = 37;
+static const uint8_t RX = 38;
+
+
+static const uint8_t A0 = 17;
+static const uint8_t A1 = 18;
+static const uint8_t A2 = 1;
+static const uint8_t A3 = 2;
+static const uint8_t A4 = 3;
+static const uint8_t A5 = 4;
+static const uint8_t A6 = 5;
+static const uint8_t A7 = 6;
+static const uint8_t A8 = 7;
+static const uint8_t A9 = 8;
+static const uint8_t A10 = 9;
+static const uint8_t A11 = 10;
+static const uint8_t A12 = 11;
+static const uint8_t A13 = 12;
+static const uint8_t A14 = 13;
+static const uint8_t A15 = 14;
+static const uint8_t A16 = 15;
+static const uint8_t A17 = 16;
+static const uint8_t A18 = 19;
+static const uint8_t A19 = 20;
+
+
+static const uint8_t T1 = 1;
+static const uint8_t T2 = 2;
+static const uint8_t T3 = 3;
+static const uint8_t T4 = 4;
+static const uint8_t T5 = 5;
+static const uint8_t T6 = 6;
+static const uint8_t T7 = 7;
+static const uint8_t T8 = 8;
+static const uint8_t T9 = 9;
+static const uint8_t T10 = 10;
+static const uint8_t T11 = 11;
+static const uint8_t T12 = 12;
+static const uint8_t T13 = 13;
+static const uint8_t T14 = 14;
+
+static const uint8_t DAC1 = 17;
+static const uint8_t DAC2 = 18;
+static const uint8_t MOSI = 12;
+static const uint8_t MISO = 13;
+static const uint8_t SS = 10;
+static const uint8_t SCK = 11;
+#endif /* Pins_Arduino_h */

BIN
sw/esp32s2/bootloader_qio_80m.bin


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 23 - 0
sw/esp32s2/get-platformio.py


+ 308 - 0
sw/esp32s2/include/ESPAsyncWiFiManager.h

@@ -0,0 +1,308 @@
+/**************************************************************
+   WiFiManager is a library for the ESP8266/Arduino platform
+   (https://github.com/esp8266/Arduino) to enable easy
+   configuration and reconfiguration of WiFi credentials using a Captive Portal
+   inspired by:
+   http://www.esp8266.com/viewtopic.php?f=29&t=2520
+   https://github.com/chriscook8/esp-arduino-apboot
+   https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced
+   Built by AlexT https://github.com/tzapu
+   Ported to Async Web Server by https://github.com/alanswx
+   Licensed under MIT license
+ **************************************************************/
+
+#ifndef ESPAsyncWiFiManager_h
+#define ESPAsyncWiFiManager_h
+
+#if defined(ESP8266)
+#include <ESP8266WiFi.h> // https://github.com/esp8266/Arduino
+#else
+#include <WiFi.h>
+#include "esp_wps.h"
+#define ESP_WPS_MODE WPS_TYPE_PBC
+#endif
+#include <ESPAsyncWebServer.h>
+
+//#define USE_EADNS               // uncomment to use ESPAsyncDNSServer
+#ifdef USE_EADNS
+#include <ESPAsyncDNSServer.h> // https://github.com/devyte/ESPAsyncDNSServer
+                               // https://github.com/me-no-dev/ESPAsyncUDP
+#else
+#include <DNSServer.h>
+#endif
+#include <memory>
+
+// fix crash on ESP32 (see https://github.com/alanswx/ESPAsyncWiFiManager/issues/44)
+#if defined(ESP8266)
+typedef int wifi_ssid_count_t;
+#else
+typedef int16_t wifi_ssid_count_t;
+#endif
+
+#if defined(ESP8266)
+extern "C"
+{
+#include "user_interface.h"
+}
+#else
+#include <rom/rtc.h>
+#endif
+
+const char WFM_HTTP_HEAD[] PROGMEM = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/><title>{v}</title>";
+const char HTTP_STYLE[] PROGMEM = "<style>.c{text-align: center;} div,input{padding:5px;font-size:1em;} input{width:95%;} body{text-align: center;font-family:verdana;} button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;} .q{float: right;width: 64px;text-align: right;} .l{background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==\") no-repeat left center;background-size: 1em;}</style>";
+const char HTTP_SCRIPT[] PROGMEM = "<script>function c(l){document.getElementById('s').value=l.innerText||l.textContent;document.getElementById('p').focus();}</script>";
+const char HTTP_HEAD_END[] PROGMEM = "</head><body><div style='text-align:left;display:inline-block;min-width:260px;'>";
+const char HTTP_PORTAL_OPTIONS[] PROGMEM = "<form action=\"/wifi\" method=\"get\"><button>Configure WiFi</button></form><br/><form action=\"/0wifi\" method=\"get\"><button>Configure WiFi (No Scan)</button></form><br/><form action=\"/i\" method=\"get\"><button>Info</button></form><br/><form action=\"/r\" method=\"post\"><button>Reset</button></form>";
+const char HTTP_ITEM[] PROGMEM = "<div><a href='#p' onclick='c(this)'>{v}</a>&nbsp;<span class='q {i}'>{r}%</span></div>";
+const char HTTP_FORM_START[] PROGMEM = "<form method='get' action='wifisave'><input id='s' name='s' length=32 placeholder='SSID'><br/><input id='p' name='p' length=64 type='password' placeholder='password'><br/>";
+const char HTTP_FORM_PARAM[] PROGMEM = "<br/><input id='{i}' name='{n}' length={l} placeholder='{p}' value='{v}' {c}>";
+const char HTTP_FORM_END[] PROGMEM = "<br/><button type='submit'>save</button></form>";
+const char HTTP_SCAN_LINK[] PROGMEM = "<br/><div class=\"c\"><a href=\"/wifi\">Scan</a></div>";
+const char HTTP_SAVED[] PROGMEM = "<div>Credentials Saved<br />Trying to connect ESP to network.<br />If it fails reconnect to AP to try again</div>";
+const char HTTP_END[] PROGMEM = "</div></body></html>";
+
+#define WIFI_MANAGER_MAX_PARAMS 10
+
+class AsyncWiFiManagerParameter
+{
+public:
+  AsyncWiFiManagerParameter(const char *custom);
+  AsyncWiFiManagerParameter(const char *id,
+                            const char *placeholder,
+                            const char *defaultValue,
+                            unsigned int length);
+  AsyncWiFiManagerParameter(const char *id,
+                            const char *placeholder,
+                            const char *defaultValue,
+                            unsigned int length,
+                            const char *custom);
+
+  const char *getID();
+  const char *getValue();
+  const char *getPlaceholder();
+  unsigned int getValueLength();
+  const char *getCustomHTML();
+
+private:
+  const char *_id;
+  const char *_placeholder;
+  char *_value;
+  unsigned int _length;
+  const char *_customHTML;
+
+  void init(const char *id,
+            const char *placeholder,
+            const char *defaultValue,
+            unsigned int length,
+            const char *custom);
+
+  friend class AsyncWiFiManager;
+};
+
+class WiFiResult
+{
+public:
+  bool duplicate;
+  String SSID;
+  uint8_t encryptionType;
+  int32_t RSSI;
+  uint8_t *BSSID;
+  int32_t channel;
+  bool isHidden;
+
+  WiFiResult()
+  {
+  }
+};
+
+class AsyncWiFiManager
+{
+public:
+#ifdef USE_EADNS
+  AsyncWiFiManager(AsyncWebServer *server, AsyncDNSServer *dns);
+#else
+  AsyncWiFiManager(AsyncWebServer *server, DNSServer *dns);
+#endif
+
+  void scan(boolean async = false);
+  String scanModal();
+  void loop();
+  void safeLoop();
+  void criticalLoop();
+  String infoAsString();
+
+  boolean autoConnect(unsigned long maxConnectRetries = 1,
+                      unsigned long retryDelayMs = 1000);
+  boolean autoConnect(char const *apName,
+                      char const *apPassword = NULL,
+                      unsigned long maxConnectRetries = 1,
+                      unsigned long retryDelayMs = 1000);
+
+  // if you want to always start the config portal, without trying to connect first
+  boolean startConfigPortal(char const *apName, char const *apPassword = NULL);
+  void startConfigPortalModeless(char const *apName, char const *apPassword);
+
+  // get the AP name of the config portal, so it can be used in the callback
+  String getConfigPortalSSID();
+
+  void resetSettings();
+
+  // sets timeout before webserver loop ends and exits even if there has been no setup.
+  // usefully for devices that failed to connect at some point and got stuck in a webserver loop.
+  // in seconds, setConfigPortalTimeout is a new name for setTimeout
+  void setConfigPortalTimeout(unsigned long seconds);
+  void setTimeout(unsigned long seconds);
+
+  // sets timeout for which to attempt connecting, usefull if you get a lot of failed connects
+  void setConnectTimeout(unsigned long seconds);
+
+  // wether or not the wifi manager tries to connect to configured access point even when
+  // configuration portal (ESP as access point) is running [default true/on]
+  void setTryConnectDuringConfigPortal(boolean v);
+
+  void setDebugOutput(boolean debug);
+  // defaults to not showing anything under 8% signal quality if called
+  void setMinimumSignalQuality(unsigned int quality = 8);
+  // sets a custom ip /gateway /subnet configuration
+  void setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn);
+  // sets config for a static IP
+  void setSTAStaticIPConfig(IPAddress ip,
+                            IPAddress gw,
+                            IPAddress sn,
+                            IPAddress dns1 = (uint32_t)0x00000000,
+                            IPAddress dns2 = (uint32_t)0x00000000);
+  // called when AP mode and config portal is started
+  void setAPCallback(std::function<void(AsyncWiFiManager *)>);
+  // called when settings have been changed and connection was successful
+  void setSaveConfigCallback(std::function<void()> func);
+  void setConnectCallback(std::function<void()> func);
+  void setDisconnectCallback(std::function<void()> func);
+  //adds a custom parameter
+  void addParameter(AsyncWiFiManagerParameter *p);
+  // if this is set, it will exit after config, even if connection is unsucessful
+  void setBreakAfterConfig(boolean shouldBreak);
+  // if this is set, try WPS setup when starting (this will delay config portal for up to 2 mins)
+  // TODO
+  // if this is set, customise style
+  void setCustomHeadElement(const char *element);
+  // if this is true, remove duplicated Access Points - defaut true
+  void setRemoveDuplicateAPs(boolean removeDuplicates);
+  // sets a custom element to add to options page
+  void setCustomOptionsElement(const char *element);
+
+  String getConfiguredSTASSID(){
+      return _ssid;
+  }
+  String getConfiguredSTAPassword(){
+      return _pass;
+  }
+  void handleWifi(AsyncWebServerRequest *, boolean scan);
+  void handleWifiSave(AsyncWebServerRequest *);
+  void setupConfig();
+private:
+  AsyncWebServer *server;
+#ifdef USE_EADNS
+  AsyncDNSServer *dnsServer;
+#else
+  DNSServer *dnsServer;
+#endif
+
+  boolean _modeless;
+  unsigned long scannow;
+  boolean shouldscan = true;
+  boolean needInfo = true;
+
+  //const int     WM_DONE                 = 0;
+  //const int     WM_WAIT                 = 10;
+  //const String  HTTP_HEAD = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><title>{v}</title>";
+
+  void setupConfigPortal();
+  
+#ifdef NO_EXTRA_4K_HEAP
+  void startWPS();
+#endif
+  String pager;
+  wl_status_t wifiStatus;
+  const char *_apName = "no-net";
+  const char *_apPassword = NULL;
+  String _ssid = "";
+  String _pass = "";
+  unsigned long _configPortalTimeout = 0;
+  unsigned long _connectTimeout = 0;
+  unsigned long _configPortalStart = 0;
+
+  IPAddress _ap_static_ip;
+  IPAddress _ap_static_gw;
+  IPAddress _ap_static_sn;
+  IPAddress _sta_static_ip;
+  IPAddress _sta_static_gw;
+  IPAddress _sta_static_sn;
+  IPAddress _sta_static_dns1 = (uint32_t)0x00000000;
+  IPAddress _sta_static_dns2 = (uint32_t)0x00000000;
+
+  unsigned int _paramsCount = 0;
+  unsigned int _minimumQuality = 0;
+  boolean _removeDuplicateAPs = true;
+  boolean _shouldBreakAfterConfig = false;
+#ifdef NO_EXTRA_4K_HEAP
+  boolean _tryWPS = false;
+#endif
+  const char *_customHeadElement = "";
+  const char *_customOptionsElement = "";
+
+  //String        getEEPROMString(int start, int len);
+  //void          setEEPROMString(int start, int len, String string);
+
+  uint8_t status = WL_IDLE_STATUS;
+  uint8_t connectWifi(String ssid, String pass);
+  uint8_t waitForConnectResult();
+  void setInfo();
+  void copySSIDInfo(wifi_ssid_count_t n);
+  String networkListAsString();
+
+  void handleRoot(AsyncWebServerRequest *);
+
+  void handleInfo(AsyncWebServerRequest *);
+  void handleReset(AsyncWebServerRequest *);
+  void handleNotFound(AsyncWebServerRequest *);
+  boolean captivePortal(AsyncWebServerRequest *);
+
+  // DNS server
+  const byte DNS_PORT = 53;
+
+  // helpers
+  unsigned int getRSSIasQuality(int RSSI);
+  boolean isIp(String str);
+  String toStringIp(IPAddress ip);
+
+  boolean connect;
+  boolean _debug = false;
+
+  WiFiResult *wifiSSIDs;
+  wifi_ssid_count_t wifiSSIDCount;
+  boolean wifiSSIDscan;
+
+  boolean _tryConnectDuringConfigPortal = true;
+
+  std::function<void(AsyncWiFiManager *)> _apcallback;
+  std::function<void()> _savecallback;
+  std::function<void()> _connectcallback;
+  std::function<void()> _disconnectcallback;
+  AsyncWiFiManagerParameter *_params[WIFI_MANAGER_MAX_PARAMS];
+
+  template <typename Generic>
+  void DEBUG_WM(Generic text);
+
+  template <class T>
+  auto optionalIPFromString(T *obj, const char *s) -> decltype(obj->fromString(s))
+  {
+    return obj->fromString(s);
+  }
+  auto optionalIPFromString(...) -> bool
+  {
+    DEBUG_WM(F("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work."));
+    return false;
+  }
+};
+
+#endif

+ 5 - 0
sw/esp32s2/include/events.h

@@ -0,0 +1,5 @@
+#pragma once
+
+#include <ESPAsyncWebServer.h>
+
+extern AsyncWebSocket websocket;

+ 12 - 0
sw/esp32s2/include/max80_callback.h

@@ -0,0 +1,12 @@
+#ifndef __max80_CALLBACK_H__
+#define __Ymax80_CALLBACK_H__
+#include "config.h"
+
+namespace max80 {
+	class callback {
+	public:
+
+		void wificonnect();
+	};
+}
+#endif // __max80_CALLBACK_H__

+ 65 - 0
sw/esp32s2/include/max80_config.h

@@ -0,0 +1,65 @@
+#ifndef __MAX80_DEFS_H__
+#define __MAX80_DEFS_H__
+
+#include <Arduino.h>
+
+#define TRUE 1
+#define FALSE 0
+// Software config
+#define MAX80_FWVERSION    1
+#define MAX80_HWVERSION 	101
+
+// Serial port settings
+#define MAX80_SERIAL_BAUD_RATE 921600
+
+
+//Logging
+#define MAX80_LOGGING_ENABLE_SERIAL
+#ifndef MAX80_LOGGING_SERIAL_PORT
+#define MAX80_LOGGING_SERIAL_PORT 		Serial
+#endif
+#define MAX80_LOGGING_VERBOSE_LEVEL 	5
+
+//WIFI
+#ifndef MAX80_WIFI_RETRY_TIMEOUT
+#define MAX80_WIFI_RETRY_TIMEOUT 		00
+#endif
+
+#ifndef MAX80_WIFI_TIMEOUT_MS 
+#define MAX80_WIFI_TIMEOUT_MS			10000 
+#endif 
+
+#ifndef MAX80_WIFI_RECOVER_TIME_MS 
+#define MAX80_WIFI_RECOVER_TIME_MS 		30000 
+#endif 
+
+
+#define MAX80_WIFI_SSID				"MAX80"
+#define MAX80_WIFI_PSK				"max80!!!"
+#define MAX80_WIFI_HOSTNAME			"ABC80-MAX80"
+#define MAX80_SPIFFS_CONFIG_PART  	"data"
+#define MAX80_CONFIG_FILNAME	  	"/config"
+	
+
+#define MAX80_SPIFFS_PATH			"/"
+#define MAX80_SPIFFS_PARTITION		"data"
+
+#define SSID_MAX_LEN            32
+#define PASS_MAX_LEN            64
+
+typedef struct
+{
+  char wifi_ssid[SSID_MAX_LEN];
+  char wifi_pw  [PASS_MAX_LEN];
+}  max80config;
+
+
+
+#define NUM_WIFI_CREDENTIALS      1
+
+typedef struct
+{
+  max80config  WiFi_Creds [NUM_WIFI_CREDENTIALS];
+  uint16_t checksum;
+} max80Config;
+#endif

+ 34 - 0
sw/esp32s2/include/max80_func.h

@@ -0,0 +1,34 @@
+#ifndef __MAX80_FUNC_H__
+#define __MAX80_FUNC_H__
+#include <Arduino.h>
+struct utf8decodedata
+{
+	uint8_t		state;
+	uint8_t		left;
+	uint8_t		acc;
+	uint8_t		data;
+	uint8_t		error;
+
+};
+
+namespace max80 {
+	class std_max80
+	{
+	public:
+		void reverse8(uint8_t *buf, int size, uint8_t *rev);
+		String hex_to_string(uint8_t *buf, int size, String separator = "");
+		void serial_empty(HardwareSerial *port);
+		int hex_to_int(char c);
+		int hex_to_ascii(char c, char d);
+		void hex2bin(uint8_t *out, const char *in, size_t *size);
+		void getmac_b(String mac, uint8_t *mac_b);
+		String hexaddr(uint8_t *buf);
+		uint8_t processutf8String(const uint8_t* s,uint8_t len,uint8_t* d);
+	private:
+		void processutf8(uint8_t b,utf8decodedata *data);
+
+	};
+
+}
+
+#endif // __MAX80__STD_H__

+ 36 - 0
sw/esp32s2/include/max80_gpio.h

@@ -0,0 +1,36 @@
+#ifndef __max80_GPIO_H__
+#define __max80_GPIO_H__
+#include <Arduino.h>
+
+#define MAX80_LED1          2
+#define MAX80_LED2          4
+#define MAX80_LED3          3
+#define LED_BUILTIN         3
+#define MAX80_FPGA_I2C_SDA  5      
+#define MAX80_FPGA_I2C_SCL  6
+
+#define MAX80_FPGA_MISO     13
+#define MAX80_FPGA_MOSI     12
+#define MAX80_FPGA_CS       10
+#define MAX80_FPGA_CLK      11
+
+
+#define MAX80_RTC_32KHZ     15
+
+#define MAX80_FPGA_TDI      16
+#define MAX80_FPGA_TDO      17
+#define MAX80_FPGA_TCK      18
+#define MAX80_FPGA_TMS      14
+
+void blink_led(void *pvParameter);
+namespace max80 {
+	class gpio
+	{
+        public:
+            void init();
+            
+
+    };
+}
+
+#endif 

+ 14 - 0
sw/esp32s2/include/max80_littlefs.h

@@ -0,0 +1,14 @@
+#ifndef __max80_LITTLEFS_H__
+#define __max80_LITTLEFS__H__
+#include <Arduino.h>
+#include "max80_config.h"
+namespace max80 {
+	class littlefs {
+        public:
+            void setup();
+            bool loadconfig();
+            bool saveconfig();
+        private:
+    };
+}
+#endif

+ 0 - 0
sw/esp32s2/include/max80_ntp.h


+ 0 - 0
sw/esp32s2/include/max80_spislave.h


+ 0 - 0
sw/esp32s2/include/max80_usbserial.h


+ 28 - 0
sw/esp32s2/include/max80_web.h

@@ -0,0 +1,28 @@
+#ifndef __max80_OTA_H
+#define __max80_OTA_H
+#include <Arduino.h>
+#include <ESPAsyncWebServer.h>
+#include "max80_wifi.h"
+namespace max80
+{
+    void OTAtask(void * parameter);
+    class web
+    {
+        public:
+            void init();
+            bool connected = false;
+            void initWebSocket();
+            void handleWebSocketMessage(void *arg, uint8_t *data, size_t len);
+        private:
+            void notifyClients();
+            
+            void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,void *arg, uint8_t *data, size_t len);
+            
+            String processor(const String& var);
+            
+            
+            
+    };
+}
+
+#endif

+ 154 - 0
sw/esp32s2/include/max80_webcontent.h

@@ -0,0 +1,154 @@
+#ifndef __ABC80_WEBCONTENT_H
+#define __ABC80_WEBCONTENT_H
+#include <ArduinoOTA.h>
+const char rgbtohdmi[] PROGMEM = R"rawliteral(
+<!DOCTYPE HTML><html>
+<head>
+  <title>MAX80 keyboard controller</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link rel="icon" href="data:,">
+  <style>
+  html {
+    font-family: Arial, Helvetica, sans-serif;
+    text-align: center;
+  }
+  h1 {
+    font-size: 1.8rem;
+    color: white;
+  }
+  h2{
+    font-size: 1.5rem;
+    font-weight: bold;
+    color: #143642;
+  }
+  .topnav {
+    overflow: hidden;
+    background-color: #143642;
+  }
+  body {
+    margin: 0;
+  }
+  .content {
+    padding: 30px;
+    max-width: 600px;
+    margin: 0 auto;
+  }
+  .card {
+    background-color: #F8F7F9;;
+    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
+    padding-top:10px;
+    padding-bottom:20px;
+  }
+  .button {
+    padding: 15px 50px;
+    width: 400px;
+    font-size: 24px;
+    text-align: center;
+    outline: none;
+    color: #fff;
+    background-color: #0f8b8d;
+    border: none;
+    border-radius: 5px;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+    -khtml-user-select: none;
+    -moz-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+    -webkit-tap-highlight-color: rgba(0,0,0,0);
+   }
+   /*.button:hover {background-color: #0f8b8d}*/
+   .button:active {
+     background-color: #0f8b8d;
+     box-shadow: 2 2px #CDCDCD;
+     transform: translateY(2px);
+   }
+   .state {
+     font-size: 1.5rem;
+     color:#8c8c8c;
+     font-weight: bold;
+   }
+  </style>
+<title>ABC800 Keyboard RGBtoHDMI</title>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<link rel="icon" href="data:,">
+</head>
+<body>
+  <div class="topnav">
+    <h1>ABC800 Keyboard RGBtoHDMI</h1>
+  </div>
+  <div class="content">
+    <div class="card">
+      <h2>RGBtoHDMI</h2>
+      <p><button id="button_menu" class="button">Menu</button></p>
+      <p><button id="button_up" class="button">Up / Genlock</button></p>
+      <p><button id="button_down" class="button">Down / Screen Capture</button></p>
+    </div>
+  </div>
+<script>
+  var gateway = `ws://${window.location.hostname}/ws`;
+  var websocket;
+  window.addEventListener('load', onLoad);
+  function initWebSocket() {
+    console.log('Trying to open a WebSocket connection...');
+    websocket = new WebSocket(gateway);
+    websocket.onopen    = onOpen;
+    websocket.onclose   = onClose;
+    websocket.onmessage = onMessage; // <-- add this line
+  }
+  function onOpen(event) {
+    console.log('Connection opened');
+  }
+  function onClose(event) {
+    console.log('Connection closed');
+    setTimeout(initWebSocket, 2000);
+  }
+  function onMessage(event) {
+    var state;
+    if (event.data == "1"){
+      state = "ON";
+    }
+    else{
+      state = "OFF";
+    }
+    document.getElementById('state').innerHTML = state;
+  }
+  function onLoad(event) {
+    initWebSocket();
+    initButton();
+  }
+  function initButton() {
+    document.getElementById('button_menu').addEventListener('click', button_menu);
+    document.getElementById('button_up').addEventListener('click', button_up);
+    document.getElementById('button_down').addEventListener('click', button_down);
+  }
+  function button_menu(){
+    websocket.send('button_menu');
+  }
+  function button_up(){
+    websocket.send('button_up');
+  }
+  function button_down(){
+    websocket.send('button_down');
+  }
+</script>
+</body>
+</html>)rawliteral";
+const char fbuttons_html[] PROGMEM = R"rawliteral(<!DOCTYPE HTML><html><head>
+  <title>KEY80 Input Form</title>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  </head><body>
+  <form action="/fbuttonsget">
+    F1:  <textarea name="input1" rows="15" cols="60"></textarea>
+    <input type="submit" value="Submit">
+  </form><br>
+  <form action="/fbuttonsget">
+    F2:  <textarea name="input2" rows="15" cols="60"></textarea>
+    <input type="submit" value="Submit">
+  </form><br>
+    <form action="/fbuttonsget">
+    F3:  <textarea name="input3" rows="15" cols="60"></textarea>
+    <input type="submit" value="Submit">
+  </form><br>
+</body></html>)rawliteral";
+#endif

+ 18 - 0
sw/esp32s2/include/max80_wifi.h

@@ -0,0 +1,18 @@
+#ifndef __max80_WIFI_H__
+#define __max80_WIFI_H__
+#include <Arduino.h>
+#include "max80_config.h"
+#include <WiFi.h>
+namespace max80 {
+	class wifi {
+		public:
+			void setup();
+			void reconnect();
+		private:
+
+	};
+
+}
+
+
+#endif // __max80_WIFI_H__

+ 18 - 0
sw/esp32s2/include/netlog.h

@@ -0,0 +1,18 @@
+#pragma once
+#include "events.h"
+
+template<typename... Args> void Info(const char * fmt, Args... args) {
+  websocket.printfAll(fmt, args...);
+  Serial.printf(fmt, args...);
+}
+
+template<typename... Args> void Debug(const char * fmt, Args... args) {
+  // websocket.printfAll(fmt, args...);
+  // Serial.printf(fmt, args...);
+}
+
+template<typename... Args> void Error(const char * fmt, Args... args) {
+  websocket.printfAll(fmt, args...);
+  Serial.printf(fmt, args...);
+}
+

+ 19 - 0
sw/esp32s2/include/program.h

@@ -0,0 +1,19 @@
+#pragma once
+
+#include <stdint.h>
+#include <ESPAsyncWebServer.h>
+#define BUFFER_SIZE  128
+
+#define DATA_TYPE_SVF  0
+#define DATA_TYPE_XSVF 1
+
+#define MODE_SERIAL 1
+#define MODE_WIFI 0
+
+// External function to fetch next block to play
+extern int fetch_next_block(uint8_t *buffer, int length);
+extern int fetch_next_block_wifi(uint8_t *buffer, int length);
+
+int jtag_program(int dataType, uint8_t mode);
+void set_pins(uint8_t tdi, uint8_t tdo, uint8_t tck, uint8_t tms, uint8_t led);
+uint32_t jtag_chip_id();

+ 22 - 0
sw/esp32s2/include/programmer.h

@@ -0,0 +1,22 @@
+#pragma once
+
+#define PIN_TDI 33
+#define PIN_TDO 32
+#define PIN_TCK 27
+#define PIN_TMS 26
+
+#define CMD_DATA          'd'
+#define CMD_SET_HOSTNAME  'h'
+#define CMD_START_SVF     'i'
+#define CMD_SET_WIFI_PASS 'l'
+#define CMD_SET_OTA_PASS  'o'
+#define CMD_PASSTHROUGH   'p'
+#define CMD_QUERY         'q'
+#define CMD_REBOOT        'r'
+#define CMD_STOP          's'
+#define CMD_SET_WIFI_SSID 'u'
+#define CMD_START_XSVF    'x'
+#define CMD_GET_STATE     'g'
+
+void ProgrammerLoop();
+void ProgrammerInit();

+ 63 - 0
sw/esp32s2/max80_esp32s2.code-workspace

@@ -0,0 +1,63 @@
+{
+	"folders": [
+		{
+			"path": "."
+		}
+	],
+	"settings": {
+		"files.associations": {
+			"array": "cpp",
+			"deque": "cpp",
+			"string": "cpp",
+			"unordered_map": "cpp",
+			"unordered_set": "cpp",
+			"vector": "cpp",
+			"string_view": "cpp",
+			"initializer_list": "cpp",
+			"*.tcc": "cpp",
+			"memory": "cpp",
+			"random": "cpp",
+			"type_traits": "cpp",
+			"fstream": "cpp",
+			"istream": "cpp",
+			"ostream": "cpp",
+			"sstream": "cpp",
+			"cstddef": "cpp",
+			"functional": "cpp",
+			"optional": "cpp",
+			"system_error": "cpp",
+			"regex": "cpp",
+			"tuple": "cpp",
+			"utility": "cpp",
+			"*.cppxxx": "cpp",
+			"atomic": "cpp",
+			"bitset": "cpp",
+			"cctype": "cpp",
+			"clocale": "cpp",
+			"cmath": "cpp",
+			"cstdarg": "cpp",
+			"cstdint": "cpp",
+			"cstdio": "cpp",
+			"cstdlib": "cpp",
+			"cstring": "cpp",
+			"ctime": "cpp",
+			"cwchar": "cpp",
+			"cwctype": "cpp",
+			"exception": "cpp",
+			"algorithm": "cpp",
+			"iterator": "cpp",
+			"map": "cpp",
+			"memory_resource": "cpp",
+			"numeric": "cpp",
+			"iomanip": "cpp",
+			"iosfwd": "cpp",
+			"iostream": "cpp",
+			"limits": "cpp",
+			"new": "cpp",
+			"stdexcept": "cpp",
+			"streambuf": "cpp",
+			"cinttypes": "cpp",
+			"typeinfo": "cpp"
+		}
+	}
+}

+ 40 - 0
sw/esp32s2/platformio.ini

@@ -0,0 +1,40 @@
+[platformio]
+
+boards_dir = boards
+
+[env:max80]
+platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream
+
+
+board_bootloader.file = bootloader_qio_80m.bin
+board = max80
+framework = arduino
+platform_packages = 
+    platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.2
+    #2.0.0-rc1
+board_build.mcu = esp32s2
+description = MAX80
+lib_deps =
+    https://git.sweproj.com/pm/esp32S2-usb-host-library.git
+    me-no-dev/AsyncTCP
+    https://github.com/me-no-dev/ESPAsyncWebServer.git#master
+    https://git.sweproj.com/pm/AsyncElegantOTAKEY80.git#master
+    https://git.sweproj.com/pm/WebSerialKEY80.git
+    https://github.com/bblanchon/ArduinoJson.git#5.x
+    https://github.com/lorol/LITTLEFS.git#master
+    https://git.sweproj.com/pm/ElegantOTAKEY80.git#master
+    hideakitai/ESP32DMASPI@^0.2.0
+    # https://github.com/khoih-prog/ESPAsync_WiFiManager#master
+    #emard/LibXSVF@^1.0.0
+
+upload_port = /dev/ttyACM0
+monitor_port = /dev/ttyACM0
+monitor_speed = 115200
+
+build_type = debug
+board_build.partitions = variants/max80/partitions.csv
+BUILD_FLAGS =  -I$PROJECT_DIR/boards/max80 -w 
+    '-DCORE_HAS_LIBB64'
+    '-DCORE_DEBUG_LEVEL=5'
+    '-DLOG_LOCAL_LEVEL=5'
+    '-DHWVER=102'

+ 1389 - 0
sw/esp32s2/src/ESPAsyncWiFiManager.cpp

@@ -0,0 +1,1389 @@
+/**************************************************************
+   AsyncWiFiManager is a library for the ESP8266/Arduino platform
+   (https://github.com/esp8266/Arduino) to enable easy
+   configuration and reconfiguration of WiFi credentials using a Captive Portal
+   inspired by:
+   http://www.esp8266.com/viewtopic.php?f=29&t=2520
+   https://github.com/chriscook8/esp-arduino-apboot
+   https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced
+   Built by AlexT https://github.com/tzapu
+   Ported to Async Web Server by https://github.com/alanswx
+   Licensed under MIT license
+ **************************************************************/
+#include <Arduino.h>
+#include "ESPAsyncWiFiManager.h"
+#include <ESPAsyncWebServer.h>
+#include <AsyncElegantOTA.h>
+#include <WebSerial.h>
+
+extern AsyncElegantOtaClass AsyncElegantOTA;
+AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *custom)
+{
+  _id = NULL;
+  _placeholder = NULL;
+  _length = 0;
+  _value = NULL;
+
+  _customHTML = custom;
+}
+
+AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id,
+                                                     const char *placeholder,
+                                                     const char *defaultValue,
+                                                     unsigned int length)
+{
+  init(id, placeholder, defaultValue, length, "");
+}
+
+AsyncWiFiManagerParameter::AsyncWiFiManagerParameter(const char *id,
+                                                     const char *placeholder,
+                                                     const char *defaultValue,
+                                                     unsigned int length,
+                                                     const char *custom)
+{
+  init(id, placeholder, defaultValue, length, custom);
+}
+
+void AsyncWiFiManagerParameter::init(const char *id,
+                                     const char *placeholder,
+                                     const char *defaultValue,
+                                     unsigned int length,
+                                     const char *custom)
+{
+  _id = id;
+  _placeholder = placeholder;
+  _length = length;
+  _value = new char[length + 1];
+
+  for (unsigned int i = 0; i < length; i++)
+  {
+    _value[i] = 0;
+  }
+  if (defaultValue != NULL)
+  {
+    strncpy(_value, defaultValue, length);
+  }
+
+  _customHTML = custom;
+}
+
+const char *AsyncWiFiManagerParameter::getValue()
+{
+  return _value;
+}
+const char *AsyncWiFiManagerParameter::getID()
+{
+  return _id;
+}
+const char *AsyncWiFiManagerParameter::getPlaceholder()
+{
+  return _placeholder;
+}
+unsigned int AsyncWiFiManagerParameter::getValueLength()
+{
+  return _length;
+}
+const char *AsyncWiFiManagerParameter::getCustomHTML()
+{
+  return _customHTML;
+}
+
+#ifdef USE_EADNS
+AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server,
+                                   AsyncDNSServer *dns) : server(server), dnsServer(dns)
+{
+#else
+AsyncWiFiManager::AsyncWiFiManager(AsyncWebServer *server,
+                                   DNSServer *dns) : server(server), dnsServer(dns)
+{
+#endif
+  wifiSSIDs = NULL;
+  wifiSSIDscan = true;
+  _modeless = false;
+  shouldscan = true;
+}
+
+void AsyncWiFiManager::addParameter(AsyncWiFiManagerParameter *p)
+{
+  _params[_paramsCount] = p;
+  _paramsCount++;
+  DEBUG_WM(F("Adding parameter"));
+  DEBUG_WM(p->getID());
+}
+void AsyncWiFiManager::setupConfig()
+{
+  server->on("/wifi",
+             std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1, true));
+  server->on("/0wifi",
+             std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1, false));
+  server->on("/wifisave",
+             std::bind(&AsyncWiFiManager::handleWifiSave, this, std::placeholders::_1));
+}
+void AsyncWiFiManager::setupConfigPortal()
+{
+  // dnsServer.reset(new DNSServer());
+  // server.reset(new ESP8266WebServer(80));
+  server->reset();
+
+  DEBUG_WM(F(""));
+  _configPortalStart = millis();
+
+  DEBUG_WM(F("Configuring access point... "));
+  DEBUG_WM(_apName);
+  if (_apPassword != NULL)
+  {
+    if (strlen(_apPassword) < 8 || strlen(_apPassword) > 63)
+    {
+      // fail passphrase to short or long!
+      DEBUG_WM(F("Invalid AccessPoint password. Ignoring"));
+      _apPassword = NULL;
+    }
+    DEBUG_WM(_apPassword);
+  }
+
+  // optional soft ip config
+  if (_ap_static_ip)
+  {
+    DEBUG_WM(F("Custom AP IP/GW/Subnet"));
+    WiFi.softAPConfig(_ap_static_ip, _ap_static_gw, _ap_static_sn);
+  }
+
+  if (_apPassword != NULL)
+  {
+    WiFi.softAP(_apName, _apPassword); // password option
+  }
+  else
+  {
+    WiFi.softAP(_apName);
+  }
+
+  delay(500); // without delay I've seen the IP address blank
+  DEBUG_WM(F("AP IP address: "));
+  DEBUG_WM(WiFi.softAPIP());
+
+// setup the DNS server redirecting all the domains to the apIP
+#ifdef USE_EADNS
+  dnsServer->setErrorReplyCode(AsyncDNSReplyCode::NoError);
+#else
+  dnsServer->setErrorReplyCode(DNSReplyCode::NoError);
+#endif
+  if (!dnsServer->start(DNS_PORT, "*", WiFi.softAPIP()))
+  {
+    DEBUG_WM(F("Could not start Captive DNS Server!"));
+  }
+
+  setInfo();
+
+  // setup web pages: root, wifi config pages, SO captive portal detectors and not found
+  server->on("/",
+             std::bind(&AsyncWiFiManager::handleRoot, this, std::placeholders::_1))
+      .setFilter(ON_AP_FILTER);
+  server->on("/wifi",
+             std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1, true))
+      .setFilter(ON_AP_FILTER);
+  server->on("/0wifi",
+             std::bind(&AsyncWiFiManager::handleWifi, this, std::placeholders::_1, false))
+      .setFilter(ON_AP_FILTER);
+  server->on("/wifisave",
+             std::bind(&AsyncWiFiManager::handleWifiSave, this, std::placeholders::_1))
+      .setFilter(ON_AP_FILTER);
+  server->on("/i",
+             std::bind(&AsyncWiFiManager::handleInfo, this, std::placeholders::_1))
+      .setFilter(ON_AP_FILTER);
+  server->on("/r",
+             std::bind(&AsyncWiFiManager::handleReset, this, std::placeholders::_1))
+      .setFilter(ON_AP_FILTER);
+  server->on("/fwlink",
+             std::bind(&AsyncWiFiManager::handleRoot, this, std::placeholders::_1))
+      .setFilter(ON_AP_FILTER); // Microsoft captive portal. Maybe not needed. Might be handled by notFound handler.
+  server->onNotFound(std::bind(&AsyncWiFiManager::handleNotFound, this, std::placeholders::_1));
+  server->begin(); // web server start
+  AsyncElegantOTA.begin(server);
+  WebSerial.begin(server);
+  //WebSerial.msgCallback(WSrecvMsg);
+        /*
+  DEBUG_WM(F("HTTP server started"));
+  */
+}
+
+static const char HEX_CHAR_ARRAY[17] = "0123456789ABCDEF";
+
+#if !defined(ESP8266)
+/**
+* convert char array (hex values) to readable string by seperator
+* buf:           buffer to convert
+* length:        data length
+* strSeperator   seperator between each hex value
+* return:        formated value as String
+*/
+static String byteToHexString(uint8_t *buf, uint8_t length, String strSeperator = "-")
+{
+  String dataString = "";
+  for (uint8_t i = 0; i < length; i++)
+  {
+    byte v = buf[i] / 16;
+    byte w = buf[i] % 16;
+    if (i > 0)
+    {
+      dataString += strSeperator;
+    }
+    dataString += String(HEX_CHAR_ARRAY[v]);
+    dataString += String(HEX_CHAR_ARRAY[w]);
+  }
+  dataString.toUpperCase();
+  return dataString;
+} // byteToHexString
+
+String getESP32ChipID()
+{
+  uint64_t chipid;
+  chipid = ESP.getEfuseMac(); // the chip ID is essentially its MAC address (length: 6 bytes)
+  uint8_t chipid_size = 6;
+  uint8_t chipid_arr[chipid_size];
+  for (uint8_t i = 0; i < chipid_size; i++)
+  {
+    chipid_arr[i] = (chipid >> (8 * i)) & 0xff;
+  }
+  return byteToHexString(chipid_arr, chipid_size, "");
+}
+#endif
+
+boolean AsyncWiFiManager::autoConnect(unsigned long maxConnectRetries,
+                                      unsigned long retryDelayMs)
+{
+  String ssid = "ESP";
+#if defined(ESP8266)
+  ssid += String(ESP.getChipId());
+#else
+  ssid += getESP32ChipID();
+#endif
+  return autoConnect(ssid.c_str(), NULL);
+}
+
+boolean AsyncWiFiManager::autoConnect(char const *apName,
+                                      char const *apPassword,
+                                      unsigned long maxConnectRetries,
+                                      unsigned long retryDelayMs)
+{
+  DEBUG_WM(F(""));
+
+  // attempt to connect; should it fail, fall back to AP
+  WiFi.mode(WIFI_STA);
+
+  for (unsigned long tryNumber = 0; tryNumber < maxConnectRetries; tryNumber++)
+  {
+    DEBUG_WM(F("AutoConnect Try No.:"));
+    DEBUG_WM(tryNumber);
+
+    if (connectWifi("", "") == WL_CONNECTED)
+    {
+      DEBUG_WM(F("IP Address:"));
+      DEBUG_WM(WiFi.localIP());
+      if (_connectcallback!=NULL){
+        _connectcallback();
+      }
+      // connected
+      return true;
+    }
+
+    if (tryNumber + 1 < maxConnectRetries)
+    {
+      // we might connect during the delay
+      unsigned long restDelayMs = retryDelayMs;
+      while (restDelayMs != 0)
+      {
+        if (WiFi.status() == WL_CONNECTED)
+        {
+          DEBUG_WM(F("IP Address (connected during delay):"));
+          DEBUG_WM(WiFi.localIP());
+          if (_connectcallback!=NULL){
+            _connectcallback();
+          }
+          return true;
+        }
+        unsigned long thisDelay = std::min(restDelayMs, 100ul);
+        delay(thisDelay);
+        restDelayMs -= thisDelay;
+      }
+    }
+  }
+
+  return startConfigPortal(apName, apPassword);
+}
+
+String AsyncWiFiManager::networkListAsString()
+{
+  String pager;
+  // display networks in page
+  for (int i = 0; i < wifiSSIDCount; i++)
+  {
+    if (wifiSSIDs[i].duplicate == true)
+    {
+      continue; // skip dups
+    }
+    unsigned int quality = getRSSIasQuality(wifiSSIDs[i].RSSI);
+
+    if (_minimumQuality == 0 || _minimumQuality < quality)
+    {
+      String item = FPSTR(HTTP_ITEM);
+      String rssiQ;
+      rssiQ += quality;
+      item.replace("{v}", wifiSSIDs[i].SSID);
+      item.replace("{r}", rssiQ);
+#if defined(ESP8266)
+      if (wifiSSIDs[i].encryptionType != ENC_TYPE_NONE)
+      {
+#else
+      if (wifiSSIDs[i].encryptionType != WIFI_AUTH_OPEN)
+      {
+#endif
+        item.replace("{i}", "l");
+      }
+      else
+      {
+        item.replace("{i}", "");
+      }
+      pager += item;
+    }
+    else
+    {
+      DEBUG_WM(F("Skipping due to quality"));
+    }
+  }
+  return pager;
+}
+
+String AsyncWiFiManager::scanModal()
+{
+  shouldscan = true;
+  scan();
+  String pager = networkListAsString();
+  return pager;
+}
+
+void AsyncWiFiManager::scan(boolean async)
+{
+  if (!shouldscan)
+  {
+    return;
+  }
+  DEBUG_WM(F("About to scan()"));
+  if (wifiSSIDscan)
+  {
+    wifi_ssid_count_t n = WiFi.scanNetworks(async);
+    copySSIDInfo(n);
+  }
+}
+
+void AsyncWiFiManager::copySSIDInfo(wifi_ssid_count_t n)
+{
+  if (n == WIFI_SCAN_FAILED)
+  {
+    DEBUG_WM(F("scanNetworks returned: WIFI_SCAN_FAILED!"));
+  }
+  else if (n == WIFI_SCAN_RUNNING)
+  {
+    DEBUG_WM(F("scanNetworks returned: WIFI_SCAN_RUNNING!"));
+  }
+  else if (n < 0)
+  {
+    DEBUG_WM(F("scanNetworks failed with unknown error code!"));
+  }
+  else if (n == 0)
+  {
+    DEBUG_WM(F("No networks found"));
+    // page += F("No networks found. Refresh to scan again.");
+  }
+  else
+  {
+    DEBUG_WM(F("Scan done"));
+  }
+
+  if (n > 0)
+  {
+    // WE SHOULD MOVE THIS IN PLACE ATOMICALLY
+    if (wifiSSIDs)
+    {
+      delete[] wifiSSIDs;
+    }
+    wifiSSIDs = new WiFiResult[n];
+    wifiSSIDCount = n;
+
+    if (n > 0)
+    {
+      shouldscan = false;
+    }
+    for (wifi_ssid_count_t i = 0; i < n; i++)
+    {
+      wifiSSIDs[i].duplicate = false;
+
+#if defined(ESP8266)
+      WiFi.getNetworkInfo(i,
+                          wifiSSIDs[i].SSID,
+                          wifiSSIDs[i].encryptionType,
+                          wifiSSIDs[i].RSSI,
+                          wifiSSIDs[i].BSSID,
+                          wifiSSIDs[i].channel,
+                          wifiSSIDs[i].isHidden);
+#else
+      WiFi.getNetworkInfo(i,
+                          wifiSSIDs[i].SSID,
+                          wifiSSIDs[i].encryptionType,
+                          wifiSSIDs[i].RSSI,
+                          wifiSSIDs[i].BSSID,
+                          wifiSSIDs[i].channel);
+#endif
+    }
+
+    // RSSI SORT
+
+    // old sort
+    for (int i = 0; i < n; i++)
+    {
+      for (int j = i + 1; j < n; j++)
+      {
+        if (wifiSSIDs[j].RSSI > wifiSSIDs[i].RSSI)
+        {
+          std::swap(wifiSSIDs[i], wifiSSIDs[j]);
+        }
+      }
+    }
+
+    // remove duplicates ( must be RSSI sorted )
+    if (_removeDuplicateAPs)
+    {
+      String cssid;
+      for (int i = 0; i < n; i++)
+      {
+        if (wifiSSIDs[i].duplicate == true)
+        {
+          continue;
+        }
+        cssid = wifiSSIDs[i].SSID;
+        for (int j = i + 1; j < n; j++)
+        {
+          if (cssid == wifiSSIDs[j].SSID)
+          {
+            DEBUG_WM("DUP AP: " + wifiSSIDs[j].SSID);
+            wifiSSIDs[j].duplicate = true; // set dup aps to NULL
+          }
+        }
+      }
+    }
+  }
+}
+
+void AsyncWiFiManager::startConfigPortalModeless(char const *apName, char const *apPassword)
+{
+  _modeless = true;
+  _apName = apName;
+  _apPassword = apPassword;
+
+  /*
+  AJS - do we want this?
+  */
+
+  // setup AP
+  WiFi.mode(WIFI_AP_STA);
+  DEBUG_WM(F("SET AP STA"));
+
+  // try to connect
+  if (connectWifi("", "") == WL_CONNECTED)
+  {
+    DEBUG_WM(F("IP Address:"));
+    DEBUG_WM(WiFi.localIP());
+    if (_connectcallback!=NULL){
+      _connectcallback();
+    }
+    // connected
+    // call the callback!
+    if (_savecallback != NULL)
+    {
+      // TODO: check if any custom parameters actually exist, and check if they really changed maybe
+      _savecallback();
+    }
+  }
+
+  // notify we entered AP mode
+  if (_apcallback != NULL)
+  {
+    _apcallback(this);
+  }
+
+  connect = false;
+  setupConfigPortal();
+  scannow = 0;
+}
+
+void AsyncWiFiManager::loop()
+{
+  safeLoop();
+  criticalLoop();
+}
+
+void AsyncWiFiManager::setInfo()
+{
+  if (needInfo)
+  {
+    pager = infoAsString();
+    wifiStatus = WiFi.status();
+    needInfo = false;
+  }
+}
+
+// anything that accesses WiFi, ESP or EEPROM goes here
+void AsyncWiFiManager::criticalLoop()
+{
+  if (_modeless)
+  {
+    if (scannow == 0 || millis() - scannow >= 60000)
+    {
+      scannow = millis();
+      scan(true);
+    }
+
+    wifi_ssid_count_t n = WiFi.scanComplete();
+    if (n >= 0)
+    {
+      copySSIDInfo(n);
+      WiFi.scanDelete();
+    }
+
+    if (connect)
+    {
+      connect = false;
+      //delay(2000);
+      DEBUG_WM(F("Connecting to new AP"));
+
+      // using user-provided _ssid, _pass in place of system-stored ssid and pass
+      if (connectWifi(_ssid, _pass) != WL_CONNECTED)
+      {
+        DEBUG_WM(F("Failed to connect"));
+      }
+      else
+      {
+        // connected
+        // alanswx - should we have a config to decide if we should shut down AP?
+        // WiFi.mode(WIFI_STA);
+        // notify that configuration has changed and any optional parameters should be saved
+        if (_savecallback != NULL)
+        {
+          // TODO: check if any custom parameters actually exist, and check if they really changed maybe
+          _savecallback();
+        }
+
+        return;
+      }
+
+      if (_shouldBreakAfterConfig)
+      {
+        // flag set to exit after config after trying to connect
+        // notify that configuration has changed and any optional parameters should be saved
+        if (_savecallback != NULL)
+        {
+          // TODO: check if any custom parameters actually exist, and check if they really changed maybe
+          _savecallback();
+        }
+      }
+    }
+  }
+}
+
+// anything that doesn't access WiFi, ESP or EEPROM can go here
+void AsyncWiFiManager::safeLoop()
+{
+#ifndef USE_EADNS
+  dnsServer->processNextRequest();
+#endif
+}
+
+boolean AsyncWiFiManager::startConfigPortal(char const *apName, char const *apPassword)
+{
+  // setup AP
+  WiFi.mode(WIFI_AP_STA);
+  DEBUG_WM(F("SET AP STA"));
+
+  _apName = apName;
+  _apPassword = apPassword;
+  bool connectedDuringConfigPortal = false;
+
+  // notify we entered AP mode
+  if (_apcallback != NULL)
+  {
+    _apcallback(this);
+  }
+
+  connect = false;
+  setupConfigPortal();
+  scannow = 0;
+  while (_configPortalTimeout == 0 || millis() - _configPortalStart < _configPortalTimeout)
+  {
+// DNS
+#ifndef USE_EADNS
+    dnsServer->processNextRequest();
+#endif
+    //
+    //  we should do a scan every so often here and
+    //  try to reconnect to AP while we are at it
+    //
+    if (scannow == 0 || millis() - scannow >= 10000)
+    {
+      DEBUG_WM(F("About to scan()"));
+      shouldscan = true; // since we are modal, we can scan every time
+#if defined(ESP8266)
+      // we might still be connecting, so that has to stop for scanning
+      ETS_UART_INTR_DISABLE();
+      wifi_station_disconnect();
+      ETS_UART_INTR_ENABLE();
+#else
+      WiFi.disconnect(false);
+#endif
+      scanModal();
+      if (_tryConnectDuringConfigPortal)
+      {
+        WiFi.begin(); // try to reconnect to AP
+        connectedDuringConfigPortal = true;
+      }
+      scannow = millis();
+    }
+
+    // attempts to reconnect were successful
+    if (WiFi.status() == WL_CONNECTED)
+    {
+      // connected
+      WiFi.mode(WIFI_STA);
+      // notify that configuration has changed and any optional parameters should be saved
+      // configuraton should not be saved when just connected using stored ssid and password during config portal
+      if (!connectedDuringConfigPortal && _savecallback != NULL)
+      {
+        // TODO: check if any custom parameters actually exist, and check if they really changed maybe
+        _savecallback();
+      }
+      break;
+    }
+
+    if (connect)
+    {
+      connect = false;
+      delay(2000);
+      DEBUG_WM(F("Connecting to new AP"));
+
+      // using user-provided _ssid, _pass in place of system-stored ssid and pass
+      if (_tryConnectDuringConfigPortal and connectWifi(_ssid, _pass) == WL_CONNECTED)
+      {
+        // connected
+        WiFi.mode(WIFI_STA);
+        // notify that configuration has changed and any optional parameters should be saved
+        if (_savecallback != NULL)
+        {
+          // TODO: check if any custom parameters actually exist, and check if they really changed maybe
+          _savecallback();
+        }
+        break;
+      }
+      else
+      {
+          if(_tryConnectDuringConfigPortal)
+            DEBUG_WM(F("Failed to connect"));
+      }
+
+      if (_shouldBreakAfterConfig)
+      {
+        // flag set to exit after config after trying to connect
+        // notify that configuration has changed and any optional parameters should be saved
+        if (_savecallback != NULL)
+        {
+          // TODO: check if any custom parameters actually exist, and check if they really changed maybe
+          _savecallback();
+        }
+        break;
+      }
+    }
+    yield();
+  }
+
+  server->reset();
+  dnsServer->stop();
+
+  return WiFi.status() == WL_CONNECTED;
+}
+
+uint8_t AsyncWiFiManager::connectWifi(String ssid, String pass)
+{
+  DEBUG_WM(F("Connecting as wifi client..."));
+
+  // check if we've got static_ip settings, if we do, use those
+  if (_sta_static_ip)
+  {
+    DEBUG_WM(F("Custom STA IP/GW/Subnet/DNS"));
+    WiFi.config(_sta_static_ip, _sta_static_gw, _sta_static_sn, _sta_static_dns1, _sta_static_dns2);
+    DEBUG_WM(WiFi.localIP());
+  }
+  // fix for auto connect racing issue
+  //  if (WiFi.status() == WL_CONNECTED) {
+  //    DEBUG_WM("Already connected. Bailing out.");
+  //    return WL_CONNECTED;
+  //  }
+  // check if we have ssid and pass and force those, if not, try with last saved values
+  if (ssid != "")
+  {
+#if defined(ESP8266)
+    // trying to fix connection in progress hanging
+    ETS_UART_INTR_DISABLE();
+    wifi_station_disconnect();
+    ETS_UART_INTR_ENABLE();
+#else
+    WiFi.disconnect(false);
+#endif
+    WiFi.begin(ssid.c_str(), pass.c_str());
+  }
+  else
+  {
+    if (WiFi.SSID().length() > 0)
+    {
+      DEBUG_WM(F("Using last saved values, should be faster"));
+#if defined(ESP8266)
+      // trying to fix connection in progress hanging
+      ETS_UART_INTR_DISABLE();
+      wifi_station_disconnect();
+      ETS_UART_INTR_ENABLE();
+#else
+      WiFi.disconnect(false);
+#endif
+      WiFi.begin();
+    }
+    else
+    {
+      DEBUG_WM(F("Try to connect with saved credentials"));
+      WiFi.begin();
+    }
+  }
+
+  uint8_t connRes = waitForConnectResult();
+  DEBUG_WM(F("Connection result: "));
+  DEBUG_WM(connRes);
+  if (WiFi.status() == WL_CONNECTED)
+  {
+
+      if (_connectcallback!=NULL){
+         _connectcallback();
+      }
+  }
+  // not connected, WPS enabled, no pass - first attempt
+#ifdef NO_EXTRA_4K_HEAP
+  if (_tryWPS && connRes != WL_CONNECTED && pass == "")
+  {
+    startWPS();
+    // should be connected at the end of WPS
+    connRes = waitForConnectResult();
+  }
+#endif
+  needInfo = true;
+  setInfo();
+  return connRes;
+}
+
+uint8_t AsyncWiFiManager::waitForConnectResult()
+{
+  if (_connectTimeout == 0)
+  {
+    return WiFi.waitForConnectResult();
+  }
+  else
+  {
+    DEBUG_WM(F("Waiting for connection result with time out"));
+    unsigned long start = millis();
+    boolean keepConnecting = true;
+    uint8_t status;
+    while (keepConnecting)
+    {
+      status = WiFi.status();
+      if (millis() > start + _connectTimeout)
+      {
+        keepConnecting = false;
+        DEBUG_WM(F("Connection timed out"));
+      }
+      if (status == WL_CONNECTED || status == WL_CONNECT_FAILED)
+      {
+        keepConnecting = false;
+      }
+      delay(100);
+    }
+    return status;
+  }
+}
+#ifdef NO_EXTRA_4K_HEAP
+void AsyncWiFiManager::startWPS()
+{
+  DEBUG_WM(F("START WPS"));
+#if defined(ESP8266)
+  WiFi.beginWPSConfig();
+#else
+  //esp_wps_config_t config = WPS_CONFIG_INIT_DEFAULT(ESP_WPS_MODE);
+  esp_wps_config_t config = {};
+  config.wps_type = ESP_WPS_MODE;
+  config.crypto_funcs = &g_wifi_default_wps_crypto_funcs;
+  strcpy(config.factory_info.manufacturer, "ESPRESSIF");
+  strcpy(config.factory_info.model_number, "ESP32");
+  strcpy(config.factory_info.model_name, "ESPRESSIF IOT");
+  strcpy(config.factory_info.device_name, "ESP STATION");
+
+  esp_wifi_wps_enable(&config);
+  esp_wifi_wps_start(0);
+#endif
+  DEBUG_WM(F("END WPS"));
+}
+#endif
+String AsyncWiFiManager::getConfigPortalSSID()
+{
+  return _apName;
+}
+
+void AsyncWiFiManager::resetSettings()
+{
+  DEBUG_WM(F("settings invalidated"));
+  DEBUG_WM(F("THIS MAY CAUSE AP NOT TO START UP PROPERLY. YOU NEED TO COMMENT IT OUT AFTER ERASING THE DATA."));
+
+  WiFi.mode(WIFI_AP_STA); // cannot erase if not in STA mode !
+  WiFi.persistent(true);
+#if defined(ESP8266)
+  WiFi.disconnect(true);
+#else
+  WiFi.disconnect(true, true);
+#endif
+  WiFi.persistent(false);
+
+  //delay(200);
+}
+void AsyncWiFiManager::setTimeout(unsigned long seconds)
+{
+  setConfigPortalTimeout(seconds);
+}
+
+void AsyncWiFiManager::setConfigPortalTimeout(unsigned long seconds)
+{
+  _configPortalTimeout = seconds * 1000;
+}
+
+void AsyncWiFiManager::setConnectTimeout(unsigned long seconds)
+{
+  _connectTimeout = seconds * 1000;
+}
+
+void AsyncWiFiManager::setTryConnectDuringConfigPortal(boolean v)
+{
+  _tryConnectDuringConfigPortal = v;
+}
+
+void AsyncWiFiManager::setDebugOutput(boolean debug)
+{
+  _debug = debug;
+}
+
+void AsyncWiFiManager::setAPStaticIPConfig(IPAddress ip,
+                                           IPAddress gw,
+                                           IPAddress sn)
+{
+  _ap_static_ip = ip;
+  _ap_static_gw = gw;
+  _ap_static_sn = sn;
+}
+
+void AsyncWiFiManager::setSTAStaticIPConfig(IPAddress ip,
+                                            IPAddress gw,
+                                            IPAddress sn,
+                                            IPAddress dns1,
+                                            IPAddress dns2)
+{
+  _sta_static_ip = ip;
+  _sta_static_gw = gw;
+  _sta_static_sn = sn;
+  _sta_static_dns1 = dns1;
+  _sta_static_dns2 = dns2;
+}
+
+void AsyncWiFiManager::setMinimumSignalQuality(unsigned int quality)
+{
+  _minimumQuality = quality;
+}
+
+void AsyncWiFiManager::setBreakAfterConfig(boolean shouldBreak)
+{
+  _shouldBreakAfterConfig = shouldBreak;
+}
+
+// handle root or redirect to captive portal
+void AsyncWiFiManager::handleRoot(AsyncWebServerRequest *request)
+{
+  // AJS - maybe we should set a scan when we get to the root???
+  // and only scan on demand? timer + on demand? plus a link to make it happen?
+
+  shouldscan = true;
+  scannow = 0;
+  DEBUG_WM(F("Handle root"));
+
+  if (captivePortal(request))
+  {
+    // if captive portal redirect instead of displaying the page
+    return;
+  }
+
+  DEBUG_WM(F("Sending Captive Portal"));
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Options");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  page += FPSTR(HTTP_HEAD_END);
+  page += "<h1>";
+  page += _apName;
+  page += "</h1>";
+  page += F("<h3>AsyncWiFiManager</h3>");
+  page += FPSTR(HTTP_PORTAL_OPTIONS);
+  page += _customOptionsElement;
+  page += FPSTR(HTTP_END);
+
+  request->send(200, "text/html", page);
+  DEBUG_WM(F("Sent..."));
+}
+
+// wifi config page handler
+void AsyncWiFiManager::handleWifi(AsyncWebServerRequest *request, boolean scan)
+{
+  shouldscan = true;
+  scannow = 0;
+
+  DEBUG_WM(F("Handle wifi"));
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Config ESP");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  page += FPSTR(HTTP_HEAD_END);
+
+  if (scan)
+  {
+    wifiSSIDscan = false;
+
+    DEBUG_WM(F("Scan done"));
+    if (wifiSSIDCount == 0)
+    {
+      DEBUG_WM(F("No networks found"));
+      page += F("No networks found. Refresh to scan again");
+    }
+    else
+    {
+      // display networks in page
+      String pager = networkListAsString();
+      page += pager;
+      page += "<br/>";
+    }
+  }
+  wifiSSIDscan = true;
+
+  page += FPSTR(HTTP_FORM_START);
+  char parLength[2];
+
+  // add the extra parameters to the form
+  for (unsigned int i = 0; i < _paramsCount; i++)
+  {
+    if (_params[i] == NULL)
+    {
+      break;
+    }
+
+    String pitem = FPSTR(HTTP_FORM_PARAM);
+    if (_params[i]->getID() != NULL)
+    {
+      pitem.replace("{i}", _params[i]->getID());
+      pitem.replace("{n}", _params[i]->getID());
+      pitem.replace("{p}", _params[i]->getPlaceholder());
+      snprintf(parLength, 2, "%d", _params[i]->getValueLength());
+      pitem.replace("{l}", parLength);
+      pitem.replace("{v}", _params[i]->getValue());
+      pitem.replace("{c}", _params[i]->getCustomHTML());
+    }
+    else
+    {
+      pitem = _params[i]->getCustomHTML();
+    }
+
+    page += pitem;
+  }
+  if (_params[0] != NULL)
+  {
+    page += "<br/>";
+  }
+  if (_sta_static_ip)
+  {
+    String item = FPSTR(HTTP_FORM_PARAM);
+    item.replace("{i}", "ip");
+    item.replace("{n}", "ip");
+    item.replace("{p}", "Static IP");
+    item.replace("{l}", "15");
+    item.replace("{v}", _sta_static_ip.toString());
+
+    page += item;
+
+    item = FPSTR(HTTP_FORM_PARAM);
+    item.replace("{i}", "gw");
+    item.replace("{n}", "gw");
+    item.replace("{p}", "Static Gateway");
+    item.replace("{l}", "15");
+    item.replace("{v}", _sta_static_gw.toString());
+
+    page += item;
+
+    item = FPSTR(HTTP_FORM_PARAM);
+    item.replace("{i}", "sn");
+    item.replace("{n}", "sn");
+    item.replace("{p}", "Subnet");
+    item.replace("{l}", "15");
+    item.replace("{v}", _sta_static_sn.toString());
+
+    page += item;
+
+    item = FPSTR(HTTP_FORM_PARAM);
+    item.replace("{i}", "dns1");
+    item.replace("{n}", "dns1");
+    item.replace("{p}", "DNS1");
+    item.replace("{l}", "15");
+    item.replace("{v}", _sta_static_dns1.toString());
+
+    page += item;
+
+    item = FPSTR(HTTP_FORM_PARAM);
+    item.replace("{i}", "dns2");
+    item.replace("{n}", "dns2");
+    item.replace("{p}", "DNS2");
+    item.replace("{l}", "15");
+    item.replace("{v}", _sta_static_dns2.toString());
+
+    page += item;
+    page += "<br/>";
+  }
+  page += FPSTR(HTTP_FORM_END);
+  page += FPSTR(HTTP_SCAN_LINK);
+  page += FPSTR(HTTP_END);
+
+  request->send(200, "text/html", page);
+
+  DEBUG_WM(F("Sent config page"));
+}
+
+// handle the WLAN save form and redirect to WLAN config page again
+void AsyncWiFiManager::handleWifiSave(AsyncWebServerRequest *request)
+{
+  DEBUG_WM(F("WiFi save"));
+
+  // SAVE/connect here
+  needInfo = true;
+  _ssid = request->arg("s").c_str();
+  _pass = request->arg("p").c_str();
+
+  // parameters
+  for (unsigned int i = 0; i < _paramsCount; i++)
+  {
+    if (_params[i] == NULL)
+    {
+      break;
+    }
+    // read parameter
+    String value = request->arg(_params[i]->getID()).c_str();
+    // store it in array
+    value.toCharArray(_params[i]->_value, _params[i]->_length);
+
+    DEBUG_WM(F("Parameter"));
+    DEBUG_WM(_params[i]->getID());
+    DEBUG_WM(value);
+  }
+
+  if (request->hasArg("ip"))
+  {
+    DEBUG_WM(F("static ip"));
+    DEBUG_WM(request->arg("ip"));
+    //_sta_static_ip.fromString(request->arg("ip"));
+    String ip = request->arg("ip");
+    optionalIPFromString(&_sta_static_ip, ip.c_str());
+  }
+  if (request->hasArg("gw"))
+  {
+    DEBUG_WM(F("static gateway"));
+    DEBUG_WM(request->arg("gw"));
+    String gw = request->arg("gw");
+    optionalIPFromString(&_sta_static_gw, gw.c_str());
+  }
+  if (request->hasArg("sn"))
+  {
+    DEBUG_WM(F("static netmask"));
+    DEBUG_WM(request->arg("sn"));
+    String sn = request->arg("sn");
+    optionalIPFromString(&_sta_static_sn, sn.c_str());
+  }
+  if (request->hasArg("dns1"))
+  {
+    DEBUG_WM(F("static DNS 1"));
+    DEBUG_WM(request->arg("dns1"));
+    String dns1 = request->arg("dns1");
+    optionalIPFromString(&_sta_static_dns1, dns1.c_str());
+  }
+  if (request->hasArg("dns2"))
+  {
+    DEBUG_WM(F("static DNS 2"));
+    DEBUG_WM(request->arg("dns2"));
+    String dns2 = request->arg("dns2");
+    optionalIPFromString(&_sta_static_dns2, dns2.c_str());
+  }
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Credentials Saved");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  page += F("<meta http-equiv=\"refresh\" content=\"5; url=/i\">");
+  page += FPSTR(HTTP_HEAD_END);
+  page += FPSTR(HTTP_SAVED);
+  page += FPSTR(HTTP_END);
+
+  request->send(200, "text/html", page);
+
+  DEBUG_WM(F("Sent wifi save page"));
+
+  connect = true; // signal ready to connect/reset
+}
+
+// handle the info page
+String AsyncWiFiManager::infoAsString()
+{
+  String page;
+  page += F("<dt>Chip ID</dt><dd>");
+#if defined(ESP8266)
+  page += ESP.getChipId();
+#else
+  page += getESP32ChipID();
+#endif
+  page += F("</dd>");
+  page += F("<dt>Flash Chip ID</dt><dd>");
+#if defined(ESP8266)
+  page += ESP.getFlashChipId();
+#else
+  page += F("N/A for ESP32");
+#endif
+  page += F("</dd>");
+  page += F("<dt>IDE Flash Size</dt><dd>");
+  page += ESP.getFlashChipSize();
+  page += F(" bytes</dd>");
+  page += F("<dt>Real Flash Size</dt><dd>");
+#if defined(ESP8266)
+  page += ESP.getFlashChipRealSize();
+#else
+  page += F("N/A for ESP32");
+#endif
+  page += F(" bytes</dd>");
+  page += F("<dt>Soft AP IP</dt><dd>");
+  page += WiFi.softAPIP().toString();
+  page += F("</dd>");
+  page += F("<dt>Soft AP MAC</dt><dd>");
+  page += WiFi.softAPmacAddress();
+  page += F("</dd>");
+  page += F("<dt>Station SSID</dt><dd>");
+  page += WiFi.SSID();
+  page += F("</dd>");
+  page += F("<dt>Station IP</dt><dd>");
+  page += WiFi.localIP().toString();
+  page += F("</dd>");
+  page += F("<dt>Station MAC</dt><dd>");
+  page += WiFi.macAddress();
+  page += F("</dd>");
+  page += F("</dl>");
+  return page;
+}
+
+void AsyncWiFiManager::handleInfo(AsyncWebServerRequest *request)
+{
+  DEBUG_WM(F("Info"));
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Info");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  if (connect == true)
+  {
+    page += F("<meta http-equiv=\"refresh\" content=\"5; url=/i\">");
+  }
+  page += FPSTR(HTTP_HEAD_END);
+  page += F("<dl>");
+  if (connect == true)
+  {
+    page += F("<dt>Trying to connect</dt><dd>");
+    page += wifiStatus;
+    page += F("</dd>");
+  }
+  page += pager;
+  page += FPSTR(HTTP_END);
+
+  request->send(200, "text/html", page);
+
+  DEBUG_WM(F("Sent info page"));
+}
+
+// handle the reset page
+void AsyncWiFiManager::handleReset(AsyncWebServerRequest *request)
+{
+  DEBUG_WM(F("Reset"));
+
+  String page = FPSTR(WFM_HTTP_HEAD);
+  page.replace("{v}", "Info");
+  page += FPSTR(HTTP_SCRIPT);
+  page += FPSTR(HTTP_STYLE);
+  page += _customHeadElement;
+  page += FPSTR(HTTP_HEAD_END);
+  page += F("Module will reset in a few seconds");
+  page += FPSTR(HTTP_END);
+  request->send(200, "text/html", page);
+
+  DEBUG_WM(F("Sent reset page"));
+  delay(5000);
+#if defined(ESP8266)
+  ESP.reset();
+#else
+  ESP.restart();
+#endif
+  delay(2000);
+}
+
+void AsyncWiFiManager::handleNotFound(AsyncWebServerRequest *request)
+{
+  DEBUG_WM(F("Handle not found"));
+  if (captivePortal(request))
+  {
+    // if captive portal redirect instead of displaying the error page
+    return;
+  }
+
+  String message = "File Not Found\n\n";
+  message += "URI: ";
+  message += request->url();
+  message += "\nMethod: ";
+  message += (request->method() == HTTP_GET) ? "GET" : "POST";
+  message += "\nArguments: ";
+  message += request->args();
+  message += "\n";
+
+  for (unsigned int i = 0; i < request->args(); i++)
+  {
+    message += " " + request->argName(i) + ": " + request->arg(i) + "\n";
+  }
+  AsyncWebServerResponse *response = request->beginResponse(404, "text/plain", message);
+  response->addHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+  response->addHeader("Pragma", "no-cache");
+  response->addHeader("Expires", "-1");
+  request->send(response);
+}
+
+/** Redirect to captive portal if we got a request for another domain.
+ * Return true in that case so the page handler do not try to handle the request again. */
+boolean AsyncWiFiManager::captivePortal(AsyncWebServerRequest *request)
+{
+  if (!isIp(request->host()))
+  {
+    DEBUG_WM(F("Request redirected to captive portal"));
+    AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "");
+    response->addHeader("Location", String("http://") + toStringIp(request->client()->localIP()));
+    request->send(response);
+    return true;
+  }
+  return false;
+}
+
+// start up config portal callback
+void AsyncWiFiManager::setAPCallback(std::function<void(AsyncWiFiManager *)> func)
+{
+  _apcallback = func;
+}
+
+// start up save config callback
+void AsyncWiFiManager::setSaveConfigCallback(std::function<void()> func)
+{
+  _savecallback = func;
+}
+void AsyncWiFiManager::setConnectCallback(std::function<void()> func)
+{
+  _connectcallback = func;
+}
+
+// sets a custom element to add to head, like a new style tag
+void AsyncWiFiManager::setCustomHeadElement(const char *element)
+{
+  _customHeadElement = element;
+}
+
+// sets a custom element to add to options page
+void AsyncWiFiManager::setCustomOptionsElement(const char *element)
+{
+  _customOptionsElement = element;
+}
+
+// if this is true, remove duplicated Access Points - defaut true
+void AsyncWiFiManager::setRemoveDuplicateAPs(boolean removeDuplicates)
+{
+  _removeDuplicateAPs = removeDuplicates;
+}
+
+template <typename Generic>
+void AsyncWiFiManager::DEBUG_WM(Generic text)
+{
+  if (_debug)
+  {
+    Serial.print(F("*WM: "));
+    Serial.println(text);
+  }
+}
+
+unsigned int AsyncWiFiManager::getRSSIasQuality(int RSSI)
+{
+  unsigned int quality = 0;
+
+  if (RSSI <= -100)
+  {
+    quality = 0;
+  }
+  else if (RSSI >= -50)
+  {
+    quality = 100;
+  }
+  else
+  {
+    quality = 2 * (RSSI + 100);
+  }
+  return quality;
+}
+
+// is this an IP?
+boolean AsyncWiFiManager::isIp(String str)
+{
+  for (unsigned int i = 0; i < str.length(); i++)
+  {
+    int c = str.charAt(i);
+    if (c != '.' && (c < '0' || c > '9'))
+    {
+      return false;
+    }
+  }
+  return true;
+}
+
+// IP to String?
+String AsyncWiFiManager::toStringIp(IPAddress ip)
+{
+  String res = "";
+  for (int i = 0; i < 3; i++)
+  {
+    res += String((ip >> (8 * i)) & 0xFF) + ".";
+  }
+  res += String(((ip >> 8 * 3)) & 0xFF);
+  return res;
+}

+ 3 - 0
sw/esp32s2/src/events.cpp

@@ -0,0 +1,3 @@
+#include "events.h"
+
+AsyncWebSocket websocket("/ws");

+ 18 - 0
sw/esp32s2/src/libxsvf/COPYING

@@ -0,0 +1,18 @@
+
+Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+
+Copyright (C) 2009  RIEGL Research ForschungsGmbH
+Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+

+ 83 - 0
sw/esp32s2/src/libxsvf/Makefile

@@ -0,0 +1,83 @@
+# Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+#
+# Copyright (C) 2009  RIEGL Research ForschungsGmbH
+# Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+# 
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#AR = ppc_6xx-ar
+#RANLIB = ppc_6xx-ranlib
+#CC = ppc_6xx-gcc
+#CFLAGS += -DXSVFTOOL_RLMS_VLINE
+
+AR = ar
+RANLIB = ranlib
+CC = gcc
+
+CFLAGS += -Wall -Os -ggdb -MD
+#CFLAGS += -Wextra -Wno-unused-parameter -Werror
+
+help:
+	@echo ""
+	@echo "Usage:"
+	@echo ""
+	@echo "  $(MAKE) libxsvf.a"
+	@echo "                .... build only the library"
+	@echo ""
+	@echo "  $(MAKE) xsvftool-gpio"
+	@echo "                .... build the library and xsvftool-gpio"
+	@echo ""
+	@echo "  $(MAKE) xsvftool-ft232h"
+	@echo "                .... build the library and xsvftool-ft232h"
+	@echo ""
+	@echo "  $(MAKE) xsvftool-xpcu"
+	@echo "                .... build the library and xsvftool-xpcu"
+	@echo ""
+	@echo "  $(MAKE) all"
+	@echo "                .... build the library and all examples"
+	@echo ""
+	@echo "  $(MAKE) install"
+	@echo "                .... install everything in /usr/local/"
+	@echo ""
+
+all: libxsvf.a xsvftool-gpio xsvftool-ft232h xsvftool-xpcu
+
+install: all
+	install -Dt /usr/local/bin/ xsvftool-gpio xsvftool-ft232h xsvftool-xpcu
+	install -Dt /usr/local/include/ -m 644 libxsvf.h
+	install -Dt /usr/local/lib/ -m 644 libxsvf.a
+
+libxsvf.a: tap.o statename.o memname.o svf.o xsvf.o scan.o play.o
+	rm -f libxsvf.a
+	$(AR) qc $@ $^
+	$(RANLIB) $@
+
+xsvftool-gpio: libxsvf.a xsvftool-gpio.o
+
+xsvftool-ft232h: LDLIBS+=-lftdi -lm
+xsvftool-ft232h: LDFLAGS+=-pthread
+xsvftool-ft232h.o: CFLAGS+=-pthread
+xsvftool-ft232h: libxsvf.a xsvftool-ft232h.o
+
+xsvftool-xpcu: libxsvf.a xsvftool-xpcu.src/*.c xsvftool-xpcu.src/*.h \
+		xsvftool-xpcu.src/*.v xsvftool-xpcu.src/*.ucf
+	$(MAKE) -C xsvftool-xpcu.src
+	cp xsvftool-xpcu.src/xsvftool-xpcu xsvftool-xpcu
+
+clean:
+	$(MAKE) -C xsvftool-xpcu.src clean
+	rm -f xsvftool-gpio xsvftool-ft232h xsvftool-xpcu
+	rm -f libxsvf.a *.o *.d
+
+-include *.d
+

+ 365 - 0
sw/esp32s2/src/libxsvf/README

@@ -0,0 +1,365 @@
+
+Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+******************************************************************
+
+Please check the subversion repository for updates:
+http://svn.clifford.at/libxsvf/trunk/
+
+You also might want to have a look at the project homepage:
+http://www.clifford.at/libxsvf/
+
+In papers and reports, please refer to Lib(X)SVF as follows: "Clifford Wolf,
+Lib(X)SVF: A library for implementing SVF and XSVF JTAG players.
+http://www.clifford.at/libxsvf/", e.g. using the following BibTeX code:
+
+@MISC{LibXSVF,
+  author = {Clifford Wolf},
+  title = {Lib(X)SVF: A library for implementing SVF and XSVF JTAG players},
+  howpublished = "\url{http://www.clifford.at/libxsvf/}"
+}
+
+Please send bug reports and fixes to <clifford@clifford.at>.
+
+
+Copyright and Disclaimer
+------------------------
+
+Copyright (C) 2009  RIEGL Research ForschungsGmbH
+Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+Introduction
+------------
+
+JTAG (IEEE 1149.1, aka "Boundary Scan", [1], [2]) is a standard IC
+testing, debugging and programming port.
+
+SVF (Serial Vector Format, [3], [4]) is a file format for storing the
+patterns that should be sent to the JTAG interface, as well as the
+expected response. It is used as an exchange format between programms
+that generate the JTAG input/output patterns and devices that can
+physically talk to a JTAG interface.
+
+XSVF (Xilinx Serial Vector Format, [5]) is a binary variant of the SVF
+file format, optimized but not limited to programming Xilinx FPGA and
+CPLD devices.
+
+Often one wants to use an embedded host processor or microcontroller
+to access the JTAG interface on an embedded device instead of using
+an external JTAG probe. This library can be used to implement such a
+solution. In addition to playing SVF and XSVF files this library is
+also capable of scanning the devices in the JTAG chain.
+
+Lib(X)SVF is free software licensed under the ISC license (a licence
+that is functionally equivalent to the 2-clause BSD license).
+
+[1] http://en.wikipedia.org/wiki/JTAG
+[2] http://www.fpga4fun.com/JTAG.html
+[3] http://en.wikipedia.org/wiki/Serial_Vector_Format
+[4] http://www.asset-intertech.com/support/svf.pdf
+[5] http://www.xilinx.com/bvdocs/appnotes/xapp503.pdf
+
+
+Limitations and non-standard extensions
+---------------------------------------
+
+The SVF commands 'PIO' and 'PIOMAP' are unsupported. Any use of this
+commands in an SVF input file is reported as error.
+
+The SVF 'RUNTEST' command is implemented in a limited manner: The
+'MAXIMUM' time parameter is ignored. Combining 'SCK' with a time
+parameter results in first executing the specified number of clock
+pulses followed by a delay of the specified timespan, instead of
+performing the clock cycles and the delay in parallel.
+
+The SVF commands 'HDR', 'HIR', 'SDR', 'SIR', 'TDR' and 'TIR' support
+an additional non-standard 'RMASK' parameter. This is a mask for the
+TDO bits, simmilar to the standard 'MASK' parameter. All TDO bits
+marked using a '1' in 'RMASK' are reported back to the host application
+using the the ret_tdo() callback function. This can be used to read
+data (such as device serial numbers) using JTAG by providing SVF
+templates.
+
+
+Using and Porting
+-----------------
+
+The library itself is written in plain C and does not depend on any
+library calls or headers, not even from the standard C library. So it
+can be ported easily even to restricted environment, such as embedded
+systems.
+
+So far the libary has only been tested on 32 bit hosts. Using it on
+16 bit processors might not be possible without performing some minor
+fixes in the code.
+
+The program 'xsvftool-gpio' (see xsvftool-gpio.c) is a good example
+of how to use the library. Please have a look at this program first.
+
+Note: See 'Host accessor macros' below for an alternative way of
+integrating libxsvf into your host environment.
+
+The host application must provide an libxsvf_host struct as defined
+in libxsvf.h. This struct contains some function pointers that must
+be set to implementations of the following functions:
+
+  int setup(struct libxsvf_host *h);
+
+	This function is called by libxsvf to setup/initialize the
+	jtag interface. It is executed before any other libxsvf_host
+	callback function is called.
+
+	This function should return 0 when setting up the jtag
+	interface went fine and -1 on an error.
+
+  int shutdown(struct libxsvf_host *h);
+
+	This function is called by libxsvf to shutdown the jtag
+	interface. No other libxsvf_host callback function will
+	be called after this function has been executed.
+
+	This function should return 0 when shutting down the jtag
+	interface went fine and -1 on an error.
+
+  void udelay(struct libxsvf_host *h, long usecs, int tms, long num_tck)
+
+	A function that delays execution for at least the specified
+	number of microseconds.
+
+	When the 'num_tck' argument is not 0 also the specified number tck
+	clock cycles (i.e. 1-0-1 transitions) must be generated with TMS set
+	to the value specified in the 'tms' argument.
+
+	A simple implementation may perform the delay and the clock cycles
+	after another instead of parallel. This only has an impact on the
+	runtime of the player, not on its functionality.
+
+  int getbyte(struct libxsvf_host *h);
+
+	A function that returns the next byte from the input file
+	or -1 on end of file.
+
+  int sync(struct libxsvf_host *h);
+
+	This function is only needed when writing bindings for an asynchronous
+	hardware interface (see 'Using libxsvf with asynchronous interfaces' below).
+
+	This function is optional and the function pointer may be set to a NULL
+	pointer on implementations using the simple synchronous interface.
+
+  int pulse_tck(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync)
+
+	This is the main JTAG I/O callback function.
+	It must perform the following tasks:
+
+	* Set the tms line to the value specified in the 'tms' argument.
+
+	* Set the tdi line to the value specified in the 'tdi' argument.
+	  This argument may be '-1' to indicate that the value of the tdi
+	  line is not important. In this case the tdi line may be set to
+	  any value or may be left unchanged.
+
+	* Create a negative pulse (1-0-1) on the JTAG TCK line.
+
+	* Check the tdo line against the should-be value specified in
+	  the 'tdo' argument. This argument may be '-1' to indicate that
+	  the value of the tdo line doesn't need to be checked.
+
+	* Store the current tdo value if the 'rmask' value is set to '1'.
+	  This step may be ignored if the RMASK feature (see "Limitations
+	  and non-standard extensions" above) is not used.
+
+	The function must return the current value of the tdo line, or -1 on
+	a TDO-mismatch-error.
+
+	A simple implementation of this function would look like this:
+
+	--snip--
+	int my_pulse_tck(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync) {
+		int line_tdo;
+		setPort(TMS, tms);
+		if (tdi >= 0)
+			setPort(TDI, tdi);
+		setPort(TCK, 0);
+		setPort(TCK, 1);
+		line_tdo = getPort(TDO);
+		return tdo < 0 || line_tdo == tdo ? line_tdo : -1;
+	}
+	--snap--
+
+	The 'sync' argument can safely be ignored unless you are writing
+	bindings for an asynchronous hardware interface (see 'Using libxsvf
+	with asynchronous interfaces' below).
+
+  void pulse_sck(struct libxsvf_host *h);
+
+	A function to create a pulse on the JTAG SCK line.
+
+	This function is optional and the function pointer may
+	be set to a NULL pointer on implementations without an
+	SCK line. In this cases an SVF 'RUNTEST n SCK' command
+	has no effect.
+
+  void set_trst(struct libxsvf_host *h, int v);
+
+	A function to set the JTAG TRST line to the specified
+        value:
+		 1 ... drive the line high
+		 0 ... drive the line low
+		-1 ... do not drive the line (high impedance)
+		-2 ... got SVF 'TRST ABSENT' command
+
+	This function is optional and the function pointer may
+	be set to a NULL pointer on implementations without an
+	TRST line. In this cases an SVF 'TRST' command has no
+	effect.
+
+  int set_frequency(struct libxsvf_host *h, int v);
+
+	A function to set the JTAG CLK frequency to the specified
+	value in Hz. This function should return 0 when setting
+	the frequency was successful and -1 on error. 
+
+	This function pointer is optional (may be set to NULL).
+	In this case an SVF FREQUENCY command always results in
+	an error.
+
+  void report_tapstate(struct libxsvf_host *h);
+
+	This function is called whenever the state of the TAP
+	state machine has changed. The TAP state can be read
+	using the h->tap_state enum.
+
+	This function pointer is optional (may be set to NULL)
+	and is for debugging purposes only.
+
+  void report_device(struct libxsvf_host *h, unsigned long idcode);
+
+	This function is called in scan mode for each device found
+	in the JTAG chain.
+
+	This function pointer is optional (may be set to NULL)
+	and is only called in the SCAN mode.
+
+  void report_status(struct libxsvf_host *h, const char *message);
+
+	This function is called each time before an SVF or XSVF
+	command is executed.
+
+	This function pointer is optional (may be set to NULL)
+	and is for debugging purposes only.
+
+  void report_error(struct libxsvf_host *h, const char *file, int line, const char *message);
+
+	This function is called whenever an error is detected
+	in libxsvf. It is not optional and should provide a
+	way to notify a user about the error.
+
+  void *realloc(struct libxsvf_host *h, void *ptr, int size, enum libxsvf_mem which);
+
+	This function must provide a way to allocate dynamic
+	memory. In cases where there is a standard c library
+	it may be a simple wrapper for the realloc() function.
+
+	The xsvftool-gpio command line option '-r' can be used to
+	auto-generate a realloc function for static buffers, based
+	on the requirements from an example svf or xsvf file.
+	Example given:
+
+		./xsvftool-gpio -r my_host_realloc -s demo.svf
+
+	(Re-)allocation may fail. In this cases a NULL pointer
+	must be returned. The library then generates an error,
+	frees all resources and returns.
+
+After such a struct is prepared, the function libxsvf_play()
+can be called, passing the libxsvf_host struct as first and the
+mode (LIBXSVF_MODE_SVF, LIBXSVF_MODE_XSVF or LIBXSVF_MODE_SCAN)
+as second argument.
+
+Example given:
+
+	if (libxsvf_play(&h, LIBXSVF_MODE_XSVF) < 0) {
+		/* Error handling */
+	}
+
+The libxsvf_host struct is passed back to all callback functions
+and the 'user_data' member (a void pointer) can be used to pass
+additional data (such as a file handle) to the callbacks.
+
+
+Host accessor macros
+--------------------
+
+Despite its great flexibility, APIs based on callback functions
+as the one used by libxsvf are unusual in the embedded community.
+Basically because they introduce a slight overhead in the memory
+footprint.
+
+For those who prefer a more direct integration with their host
+environments libxsvf also provides 'host accessor macros' in the
+libxsvf.h header file. Simply remove the callback functions from
+the libxsvf_host struct and modify the LIBXSVF_HOST_ macros
+to fit your needs.
+
+
+Using libxsvf with asynchronous interfaces
+------------------------------------------
+
+This library has been designed at first for a register mapped bit banging
+interface as it can be found on many microcontrollers or host CPUs. But
+some interfaces might require the JTAG data to be send and recivied in
+blocks and/or asynchronously. This is not trivial with this library because the
+pulse_tck() callback function is expected to transfer one single bit at a time.
+The solution to this is to buffer all data sent to pulse_tck() and always
+return a valid status (i.e. 'tdo < 0 ? 1 : tdo') and do the transfers when the
+buffer is full or when an interface shutdown is requested.
+
+However, some JTAG transaction must be performed synchronously (e.g. the last
+transaction in an XSVF XREPEAT data shift. In this cases pulse_tck() is called
+with the 'sync' argument set to 1. The pulse_tck() function must then perform
+all buffered JTAG transactions and return the actual tdo value for this last
+JTAG transaction or -1 if an error was detected before.
+
+An asynchronous interface binding also must implement the sync() function. It
+must perform all buffered JTAG transactions and return -1 if a TDO error was
+detected an 0 otherwise.
+
+Note: The returncode of pulse_tck is not checked in all conditions! So whenever
+an error is detected all further calls to pulse_tck() up to and including
+the next call of pulse_tck() with the 'sync' argument set or the next call to
+sync() or shutdown() must return -1.
+
+Have a look at the example program 'xsvftool-ft232h.c' for a reference
+implementation.
+
+
+Stripping down libxsvf
+----------------------
+
+It is possible to disable SVF, XSVF and/or SCAN support by setting the
+LIBXSVF_WITHOUT_SVF, LIBXSVF_WITHOUT_XSVF or LIBXSVF_WITHOUT_SCAN
+defines. In this cases one would not want to link against svf.o, xsvf.o
+or scan.o.
+
+One does not need to link agains statename.o and memname.o if the
+libxsvf_state2str() and libxsvf_mem2str() functions are not needed.
+Usually this functions are used for debugging purposes only.
+
+It is possible to modify the LIBXSVF_HOST_REPORT_STATUS() and
+LIBXSVF_HOST_REPORT_ERROR() macros in libxsvf.h to be empty
+instructions. This drastically reduces the number of string
+constants in the code.
+

+ 141 - 0
sw/esp32s2/src/libxsvf/libxsvf.h

@@ -0,0 +1,141 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef LIBXSVF_H
+#define LIBXSVF_H
+
+
+enum libxsvf_mode {
+	LIBXSVF_MODE_SVF = 1,
+	LIBXSVF_MODE_XSVF = 2,
+	LIBXSVF_MODE_SCAN = 3
+};
+
+enum libxsvf_tap_state {
+	/* Special States */
+	LIBXSVF_TAP_INIT = 0,
+	LIBXSVF_TAP_RESET = 1,
+	LIBXSVF_TAP_IDLE = 2,
+	/* DR States */
+	LIBXSVF_TAP_DRSELECT = 3,
+	LIBXSVF_TAP_DRCAPTURE = 4,
+	LIBXSVF_TAP_DRSHIFT = 5,
+	LIBXSVF_TAP_DREXIT1 = 6,
+	LIBXSVF_TAP_DRPAUSE = 7,
+	LIBXSVF_TAP_DREXIT2 = 8,
+	LIBXSVF_TAP_DRUPDATE = 9,
+	/* IR States */
+	LIBXSVF_TAP_IRSELECT = 10,
+	LIBXSVF_TAP_IRCAPTURE = 11,
+	LIBXSVF_TAP_IRSHIFT = 12,
+	LIBXSVF_TAP_IREXIT1 = 13,
+	LIBXSVF_TAP_IRPAUSE = 14,
+	LIBXSVF_TAP_IREXIT2 = 15,
+	LIBXSVF_TAP_IRUPDATE = 16
+};
+
+enum libxsvf_mem {
+	LIBXSVF_MEM_XSVF_TDI_DATA = 0,
+	LIBXSVF_MEM_XSVF_TDO_DATA = 1,
+	LIBXSVF_MEM_XSVF_TDO_MASK = 2,
+	LIBXSVF_MEM_XSVF_ADDR_MASK = 3,
+	LIBXSVF_MEM_XSVF_DATA_MASK = 4,
+	LIBXSVF_MEM_SVF_COMMANDBUF = 5,
+	LIBXSVF_MEM_SVF_SDR_TDI_DATA = 6,
+	LIBXSVF_MEM_SVF_SDR_TDI_MASK = 7,
+	LIBXSVF_MEM_SVF_SDR_TDO_DATA = 8,
+	LIBXSVF_MEM_SVF_SDR_TDO_MASK = 9,
+	LIBXSVF_MEM_SVF_SDR_RET_MASK = 10,
+	LIBXSVF_MEM_SVF_SIR_TDI_DATA = 11,
+	LIBXSVF_MEM_SVF_SIR_TDI_MASK = 12,
+	LIBXSVF_MEM_SVF_SIR_TDO_DATA = 13,
+	LIBXSVF_MEM_SVF_SIR_TDO_MASK = 14,
+	LIBXSVF_MEM_SVF_SIR_RET_MASK = 15,
+	LIBXSVF_MEM_SVF_HDR_TDI_DATA = 16,
+	LIBXSVF_MEM_SVF_HDR_TDI_MASK = 17,
+	LIBXSVF_MEM_SVF_HDR_TDO_DATA = 18,
+	LIBXSVF_MEM_SVF_HDR_TDO_MASK = 19,
+	LIBXSVF_MEM_SVF_HDR_RET_MASK = 20,
+	LIBXSVF_MEM_SVF_HIR_TDI_DATA = 21,
+	LIBXSVF_MEM_SVF_HIR_TDI_MASK = 22,
+	LIBXSVF_MEM_SVF_HIR_TDO_DATA = 23,
+	LIBXSVF_MEM_SVF_HIR_TDO_MASK = 24,
+	LIBXSVF_MEM_SVF_HIR_RET_MASK = 25,
+	LIBXSVF_MEM_SVF_TDR_TDI_DATA = 26,
+	LIBXSVF_MEM_SVF_TDR_TDI_MASK = 27,
+	LIBXSVF_MEM_SVF_TDR_TDO_DATA = 28,
+	LIBXSVF_MEM_SVF_TDR_TDO_MASK = 29,
+	LIBXSVF_MEM_SVF_TDR_RET_MASK = 30,
+	LIBXSVF_MEM_SVF_TIR_TDI_DATA = 31,
+	LIBXSVF_MEM_SVF_TIR_TDI_MASK = 32,
+	LIBXSVF_MEM_SVF_TIR_TDO_DATA = 33,
+	LIBXSVF_MEM_SVF_TIR_TDO_MASK = 34,
+	LIBXSVF_MEM_SVF_TIR_RET_MASK = 35,
+	LIBXSVF_MEM_NUM = 36
+};
+
+struct libxsvf_host {
+	int (*setup)(struct libxsvf_host *h);
+	int (*shutdown)(struct libxsvf_host *h);
+	void (*udelay)(struct libxsvf_host *h, long usecs, int tms, long num_tck);
+	int (*getbyte)(struct libxsvf_host *h);
+	int (*sync)(struct libxsvf_host *h);
+	int (*pulse_tck)(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync);
+	void (*pulse_sck)(struct libxsvf_host *h);
+	void (*set_trst)(struct libxsvf_host *h, int v);
+	int (*set_frequency)(struct libxsvf_host *h, int v);
+	void (*report_tapstate)(struct libxsvf_host *h);
+	void (*report_device)(struct libxsvf_host *h, unsigned long idcode);
+	void (*report_status)(struct libxsvf_host *h, const char *message);
+	void (*report_error)(struct libxsvf_host *h, const char *file, int line, const char *message);
+	void *(*realloc)(struct libxsvf_host *h, void *ptr, int size, enum libxsvf_mem which);
+	enum libxsvf_tap_state tap_state;
+	void *user_data;
+};
+
+int libxsvf_play(struct libxsvf_host *, enum libxsvf_mode mode);
+const char *libxsvf_state2str(enum libxsvf_tap_state tap_state);
+const char *libxsvf_mem2str(enum libxsvf_mem which);
+
+/* Internal API */ 
+int libxsvf_svf(struct libxsvf_host *h);
+int libxsvf_svf_packet(struct libxsvf_host *h, int index, int final);
+int libxsvf_xsvf(struct libxsvf_host *h);
+int libxsvf_scan(struct libxsvf_host *h);
+int libxsvf_tap_walk(struct libxsvf_host *, enum libxsvf_tap_state);
+
+/* Host accessor macros (see README) */
+#define LIBXSVF_HOST_SETUP() h->setup(h)
+#define LIBXSVF_HOST_SHUTDOWN() h->shutdown(h)
+#define LIBXSVF_HOST_UDELAY(_usecs, _tms, _num_tck) h->udelay(h, _usecs, _tms, _num_tck)
+#define LIBXSVF_HOST_GETBYTE() h->getbyte(h)
+#define LIBXSVF_HOST_SYNC() (h->sync ? h->sync(h) : 0)
+#define LIBXSVF_HOST_PULSE_TCK(_tms, _tdi, _tdo, _rmask, _sync) h->pulse_tck(h, _tms, _tdi, _tdo, _rmask, _sync)
+#define LIBXSVF_HOST_PULSE_SCK() do { if (h->pulse_sck) h->pulse_sck(h); } while (0)
+#define LIBXSVF_HOST_SET_TRST(_v) do { if (h->set_trst) h->set_trst(h, _v); } while (0)
+#define LIBXSVF_HOST_SET_FREQUENCY(_v) (h->set_frequency ? h->set_frequency(h, _v) : -1)
+#define LIBXSVF_HOST_REPORT_TAPSTATE() do { if (h->report_tapstate) h->report_tapstate(h); } while (0)
+#define LIBXSVF_HOST_REPORT_DEVICE(_v) do { if (h->report_device) h->report_device(h, _v); } while (0)
+#define LIBXSVF_HOST_REPORT_STATUS(_msg) do { if (h->report_status) h->report_status(h, _msg); } while (0)
+#define LIBXSVF_HOST_REPORT_ERROR(_msg) h->report_error(h, __FILE__, __LINE__, _msg)
+#define LIBXSVF_HOST_REALLOC(_ptr, _size, _which) h->realloc(h, _ptr, _size, _which)
+
+#endif
+

+ 65 - 0
sw/esp32s2/src/libxsvf/memname.c

@@ -0,0 +1,65 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+const char *libxsvf_mem2str(enum libxsvf_mem which)
+{
+#define X(_w, _t) if (which == LIBXSVF_MEM_ ## _w) return #_t;
+	X(XSVF_TDI_DATA, xsvf_tdi_data)
+	X(XSVF_TDO_DATA, xsvf_tdo_data)
+	X(XSVF_TDO_MASK, xsvf_tdo_mask)
+	X(XSVF_ADDR_MASK, xsvf_addr_mask)
+	X(XSVF_DATA_MASK, xsvf_data_mask)
+	X(SVF_COMMANDBUF, svf_commandbuf)
+	X(SVF_HDR_TDI_DATA, svf_hdr_tdi_data)
+	X(SVF_HDR_TDI_MASK, svf_hdr_tdi_mask)
+	X(SVF_HDR_TDO_DATA, svf_hdr_tdo_data)
+	X(SVF_HDR_TDO_MASK, svf_hdr_tdo_mask)
+	X(SVF_HDR_RET_MASK, svf_hdr_ret_mask)
+	X(SVF_HIR_TDI_DATA, svf_hir_tdi_data)
+	X(SVF_HIR_TDI_MASK, svf_hir_tdi_mask)
+	X(SVF_HIR_TDO_DATA, svf_hir_tdo_data)
+	X(SVF_HIR_TDO_MASK, svf_hir_tdo_mask)
+	X(SVF_HIR_RET_MASK, svf_hir_ret_mask)
+	X(SVF_TDR_TDI_DATA, svf_tdr_tdi_data)
+	X(SVF_TDR_TDI_MASK, svf_tdr_tdi_mask)
+	X(SVF_TDR_TDO_DATA, svf_tdr_tdo_data)
+	X(SVF_TDR_TDO_MASK, svf_tdr_tdo_mask)
+	X(SVF_TDR_RET_MASK, svf_tdr_ret_mask)
+	X(SVF_TIR_TDI_DATA, svf_tir_tdi_data)
+	X(SVF_TIR_TDI_MASK, svf_tir_tdi_mask)
+	X(SVF_TIR_TDO_DATA, svf_tir_tdo_data)
+	X(SVF_TIR_TDO_MASK, svf_tir_tdo_mask)
+	X(SVF_TIR_RET_MASK, svf_tir_ret_mask)
+	X(SVF_SDR_TDI_DATA, svf_sdr_tdi_data)
+	X(SVF_SDR_TDI_MASK, svf_sdr_tdi_mask)
+	X(SVF_SDR_TDO_DATA, svf_sdr_tdo_data)
+	X(SVF_SDR_TDO_MASK, svf_sdr_tdo_mask)
+	X(SVF_SDR_RET_MASK, svf_sdr_ret_mask)
+	X(SVF_SIR_TDI_DATA, svf_sir_tdi_data)
+	X(SVF_SIR_TDI_MASK, svf_sir_tdi_mask)
+	X(SVF_SIR_TDO_DATA, svf_sir_tdo_data)
+	X(SVF_SIR_TDO_MASK, svf_sir_tdo_mask)
+	X(SVF_SIR_RET_MASK, svf_sir_ret_mask)
+#undef X
+	return (void*)0;
+}
+

+ 72 - 0
sw/esp32s2/src/libxsvf/play.c

@@ -0,0 +1,72 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+int libxsvf_play(struct libxsvf_host *h, enum libxsvf_mode mode)
+{
+	int rc = -1;
+
+	h->tap_state = LIBXSVF_TAP_INIT;
+	if (LIBXSVF_HOST_SETUP() < 0) {
+		LIBXSVF_HOST_REPORT_ERROR("Setup of JTAG interface failed.");
+		return -1;
+	}
+
+	if (mode == LIBXSVF_MODE_SVF) {
+#ifdef LIBXSVF_WITHOUT_SVF
+		LIBXSVF_HOST_REPORT_ERROR("SVF support in libxsvf is disabled.");
+#else
+		rc = libxsvf_svf(h);
+#endif
+	}
+
+	if (mode == LIBXSVF_MODE_XSVF) {
+#ifdef LIBXSVF_WITHOUT_XSVF
+		LIBXSVF_HOST_REPORT_ERROR("XSVF support in libxsvf is disabled.");
+#else
+		rc = libxsvf_xsvf(h);
+#endif
+	}
+
+	if (mode == LIBXSVF_MODE_SCAN) {
+#ifdef LIBXSVF_WITHOUT_SCAN
+		LIBXSVF_HOST_REPORT_ERROR("SCAN support in libxsvf is disabled.");
+#else
+		rc = libxsvf_scan(h);
+#endif
+	}
+
+	libxsvf_tap_walk(h, LIBXSVF_TAP_RESET);
+	if (LIBXSVF_HOST_SYNC() != 0 && rc >= 0 ) {
+		LIBXSVF_HOST_REPORT_ERROR("TDO mismatch in TAP reset. (this is not possible!)");
+		rc = -1;
+	}
+
+	int shutdown_rc = LIBXSVF_HOST_SHUTDOWN();
+
+	if (shutdown_rc < 0) {
+		LIBXSVF_HOST_REPORT_ERROR("Shutdown of JTAG interface failed.");
+		rc = rc < 0 ? rc : shutdown_rc;
+	}
+
+	return rc;
+}
+

+ 58 - 0
sw/esp32s2/src/libxsvf/scan.c

@@ -0,0 +1,58 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+int libxsvf_scan(struct libxsvf_host *h)
+{
+	int i, j;
+
+	if (libxsvf_tap_walk(h, LIBXSVF_TAP_RESET) < 0)
+		return -1;
+
+	if (libxsvf_tap_walk(h, LIBXSVF_TAP_DRSHIFT) < 0)
+		return -1;
+
+	for (i=0; i<256; i++)
+	{
+		int bit = LIBXSVF_HOST_PULSE_TCK(0, 1, -1, 0, 1);
+
+		if (bit < 0)
+			return -1;
+
+		if (bit == 0) {
+			// LIBXSVF_HOST_REPORT_DEVICE(0);
+		} else {
+			unsigned long idcode = 1;
+			for (j=1; j<32; j++) {
+				int bit = LIBXSVF_HOST_PULSE_TCK(0, 1, -1, 0, 1);
+				if (bit < 0)
+					return -1;
+				idcode |= ((unsigned long)bit) << j;
+			}
+			if (idcode == 0xffffffff)
+				break;
+			LIBXSVF_HOST_REPORT_DEVICE(idcode);
+		}
+	}
+
+	return 0;
+}
+

+ 46 - 0
sw/esp32s2/src/libxsvf/statename.c

@@ -0,0 +1,46 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+const char *libxsvf_state2str(enum libxsvf_tap_state tap_state)
+{
+#define X(_s) if (tap_state == _s) return #_s;
+	X(LIBXSVF_TAP_INIT)
+	X(LIBXSVF_TAP_RESET)
+	X(LIBXSVF_TAP_IDLE)
+	X(LIBXSVF_TAP_DRSELECT)
+	X(LIBXSVF_TAP_DRCAPTURE)
+	X(LIBXSVF_TAP_DRSHIFT)
+	X(LIBXSVF_TAP_DREXIT1)
+	X(LIBXSVF_TAP_DRPAUSE)
+	X(LIBXSVF_TAP_DREXIT2)
+	X(LIBXSVF_TAP_DRUPDATE)
+	X(LIBXSVF_TAP_IRSELECT)
+	X(LIBXSVF_TAP_IRCAPTURE)
+	X(LIBXSVF_TAP_IRSHIFT)
+	X(LIBXSVF_TAP_IREXIT1)
+	X(LIBXSVF_TAP_IRPAUSE)
+	X(LIBXSVF_TAP_IREXIT2)
+	X(LIBXSVF_TAP_IRUPDATE)
+#undef X
+	return "UNKOWN_STATE";
+}
+

+ 1236 - 0
sw/esp32s2/src/libxsvf/svf.c

@@ -0,0 +1,1236 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+#include <stdio.h>
+
+/*
+this function will filter out comments, whitespaces
+nad line terminations and return one by one command as
+contiguous string in the buffer.
+return value:
+-2 buffer empty. try again later
+-1 error or unexpected EOF
+ 0 expected EOF, normal end of data
+ 1 normal data, proceed with processing
+*/
+static int read_command(struct libxsvf_host *h, char **buffer_p, int *len_p)
+{
+	char *buffer = *buffer_p;
+	int len;
+	static int braket_mode = 0;
+	static int comment_mode = 0;
+	static int p = 0;
+	if(len_p == NULL)
+	{
+	  p = 0;
+	  braket_mode = 0;
+	  comment_mode = 0;
+	  return 0;
+	}
+	
+	len = *len_p;
+
+	while (1)
+	{
+		if (len < p+10) {
+			len = len < 64 ? 96 : len*2;
+			buffer = LIBXSVF_HOST_REALLOC(buffer, len, LIBXSVF_MEM_SVF_COMMANDBUF);
+			*buffer_p = buffer;
+			*len_p = len;
+			if (!buffer) {
+				LIBXSVF_HOST_REPORT_ERROR("Allocating memory failed.");
+				p = 0;
+				return -1;
+			}
+		}
+		buffer[p] = 0;
+
+		int ch = LIBXSVF_HOST_GETBYTE();
+		if (ch < 0) {
+handle_eof:
+			if (ch == -2) /* temporary buffer empty */
+				return -2; /* keep value of 'p', exit now and try again later */
+			if (p == 0)
+				return 0;
+			p = 0;
+			comment_mode = 0;
+			braket_mode = 0;
+			LIBXSVF_HOST_REPORT_ERROR("Unexpected EOF.");
+			return -1;
+		}
+		if (comment_mode != 0)
+		{
+			if (ch < 0)
+				goto handle_eof;
+			if (ch < ' ' && ch != '\t')
+				goto insert_eol;
+			continue; // keep running while() loop
+		}
+		if (ch <= ' ') {
+insert_eol:
+			if (comment_mode == 0 && braket_mode == 0 && p > 0 && buffer[p-1] != ' ')
+				buffer[p++] = ' ';
+			comment_mode = 0;
+			continue; // keep running while() loop
+		}
+		if (ch == '!') {
+			comment_mode = 1;
+			continue; // keep running while() loop
+		}
+		if (ch == '/' && p > 0 && buffer[p-1] == '/') {
+			p--;
+			comment_mode = 1;
+			continue; // keep running while() loop
+		}
+		if (ch == ';')
+			break; // exit while loop immediately
+		if (ch == '(') {
+			if (!braket_mode && p > 0 && buffer[p-1] != ' ')
+				buffer[p++] = ' ';
+			braket_mode++;
+		}
+		if (ch >= 'a' && ch <= 'z')
+			buffer[p++] = ch - ('a' - 'A');
+		else
+			buffer[p++] = ch;
+		if (ch == ')') {
+			braket_mode--;
+			if (!braket_mode)
+				buffer[p++] = ' ';
+		}
+	}
+	p = 0;
+	return 1;
+}
+
+static int strtokencmp(const char *str1, const char *str2)
+{
+	int i = 0;
+	while (1) {
+		if ((str1[i] == ' ' || str1[i] == 0) && (str2[i] == ' ' || str2[i] == 0))
+			return 0;
+		if (str1[i] < str2[i])
+			return -1;
+		if (str1[i] > str2[i])
+			return +1;
+		i++;
+	}
+}
+
+static int strtokenskip(const char *str1)
+{
+	int i = 0;
+	while (str1[i] != 0 && str1[i] != ' ') i++;
+	while (str1[i] == ' ') i++;
+	return i;
+}
+
+static int token2tapstate(const char *str1)
+{
+#define X(_t) if (!strtokencmp(str1, #_t)) return LIBXSVF_TAP_ ## _t;
+	X(RESET)
+	X(IDLE)
+	X(DRSELECT)
+	X(DRCAPTURE)
+	X(DRSHIFT)
+	X(DREXIT1)
+	X(DRPAUSE)
+	X(DREXIT2)
+	X(DRUPDATE)
+	X(IRSELECT)
+	X(IRCAPTURE)
+	X(IRSHIFT)
+	X(IREXIT1)
+	X(IRPAUSE)
+	X(IREXIT2)
+	X(IRUPDATE)
+#undef X
+	return -1;
+}
+
+struct bitdata_s {
+	int len, alloced_len;
+	int alloced_bytes;
+	unsigned char *tdi_data;
+	unsigned char *tdi_mask;
+	unsigned char *tdo_data;
+	unsigned char *tdo_mask;
+	unsigned char *ret_mask;
+	int has_tdo_data;
+};
+
+static void bitdata_zero(struct libxsvf_host *h, struct bitdata_s *bd)
+{
+	bd->len = 0;
+	#if 0
+	// we don't initialize this at start of each
+	// file upload to avoid memory leaks from
+	// lost memory allocation information in case
+	// connection is lost.
+	// Realloc will always be called on
+	// bitdata pointers.
+	// static initializer will set them
+	// at zero (NULL) so first realloc will effectively
+	// do malloc for the first time and they are supposed
+	// to be realloced many times. Only when tey are free()'d
+	// (realloced to 0-size) then they can be overwritten with zero
+	bd->alloced_len = 0;
+	bd->alloced_bytes = 0;
+	if(bd->tdi_data)
+		LIBXSVF_HOST_REPORT_ERROR("Leak warning: Zeroing allocated bd->tdi_data");
+	if(bd->tdi_mask)
+		LIBXSVF_HOST_REPORT_ERROR("Leak warning: Zeroing allocated bd->tdi_mask");
+	if(bd->tdo_data)
+		LIBXSVF_HOST_REPORT_ERROR("Leak warning: Zeroing allocated bd->tdo_data");
+	if(bd->tdo_mask)
+		LIBXSVF_HOST_REPORT_ERROR("Leak warning: Zeroing allocated bd->tdo_mask");
+	if(bd->ret_mask)
+		LIBXSVF_HOST_REPORT_ERROR("Leak warning: Zeroing allocated bd->ret_mask");
+	bd->tdi_data = (void*)0;
+	bd->tdi_mask = (void*)0;
+	bd->tdo_data = (void*)0;
+	bd->tdo_mask = (void*)0;
+	bd->ret_mask = (void*)0;
+	#endif
+	bd->has_tdo_data = 0;
+}
+
+static void bitdata_free(struct libxsvf_host *h, struct bitdata_s *bd, int offset)
+{
+	LIBXSVF_HOST_REALLOC(bd->tdi_data, 0, offset+0);
+	LIBXSVF_HOST_REALLOC(bd->tdi_mask, 0, offset+1);
+	LIBXSVF_HOST_REALLOC(bd->tdo_data, 0, offset+2);
+	LIBXSVF_HOST_REALLOC(bd->tdo_mask, 0, offset+3);
+	LIBXSVF_HOST_REALLOC(bd->ret_mask, 0, offset+4);
+
+	bd->tdi_data = (void*)0;
+	bd->tdi_mask = (void*)0;
+	bd->tdo_data = (void*)0;
+	bd->tdo_mask = (void*)0;
+	bd->ret_mask = (void*)0;
+}
+
+static int hex(char ch)
+{
+	if (ch >= 'A' && ch <= 'Z')
+		return (ch - 'A') + 10;
+	if (ch >= '0' && ch <= '9')
+		return ch - '0';
+	return 0;
+}
+
+static const char *bitdata_parse(struct libxsvf_host *h, const char *p, struct bitdata_s *bd, int offset)
+{
+	int i, j;
+	bd->len = 0;
+	bd->has_tdo_data = 0;
+	while (*p >= '0' && *p <= '9') {
+		bd->len = bd->len * 10 + (*p - '0');
+		p++;
+	}
+	while (*p == ' ') {
+		p++;
+	}
+	if (bd->len != bd->alloced_len) {
+		bd->alloced_len = bd->len;
+		bd->alloced_bytes = (bd->len+7) / 8;
+	}
+	while (*p)
+	{
+		int memnum = 0;
+		unsigned char **dp = (void*)0;
+		if (!strtokencmp(p, "TDI")) {
+			p += strtokenskip(p);
+			dp = &bd->tdi_data;
+			memnum = 0;
+		}
+		if (!strtokencmp(p, "TDO")) {
+			p += strtokenskip(p);
+			dp = &bd->tdo_data;
+			bd->has_tdo_data = 1;
+			memnum = 1;
+		}
+		if (!strtokencmp(p, "SMASK")) {
+			p += strtokenskip(p);
+			dp = &bd->tdi_mask;
+			memnum = 2;
+		}
+		if (!strtokencmp(p, "MASK")) {
+			p += strtokenskip(p);
+			dp = &bd->tdo_mask;
+			memnum = 3;
+		}
+		if (!strtokencmp(p, "RMASK")) {
+			p += strtokenskip(p);
+			dp = &bd->ret_mask;
+			memnum = 4;
+		}
+		if (!dp)
+			return (void*)0;
+		*dp = LIBXSVF_HOST_REALLOC(*dp, bd->alloced_bytes, offset+memnum);
+		if (*dp == (void*)0) {
+			LIBXSVF_HOST_REPORT_ERROR("Allocating memory failed.");
+			return (void*)0;
+		}
+
+		unsigned char *d = *dp;
+		for (i=0; i<bd->alloced_bytes; i++)
+			d[i] = 0;
+
+		if (*p != '(')
+			return (void*)0;
+		p++;
+
+		int hexdigits = 0;
+		for (i=0; (p[i] >= 'A' && p[i] <= 'F') || (p[i] >= '0' && p[i] <= '9'); i++)
+			hexdigits++;
+
+		i = bd->alloced_bytes*2 - hexdigits;
+		for (j=0; j<hexdigits; j++, i++, p++) {
+			if (i%2 == 0) {
+				d[i/2] |= hex(*p) << 4;
+			} else {
+				d[i/2] |= hex(*p);
+			}
+		}
+
+		if (*p != ')')
+			return (void*)0;
+		p++;
+		while (*p == ' ') {
+			p++;
+		}
+	}
+#if 1
+	/* Debugging Output, needs <stdio.h> */
+	printf("--- Parsed bitdata [%d] ---\n", bd->len);
+	if (bd->tdi_data) {
+		printf("TDI DATA:");
+		for (i=0; i<bd->alloced_bytes; i++)
+			printf(" %02x", bd->tdi_data[i]);
+		printf("\n");
+	}
+	if (bd->tdo_data && bd->has_tdo_data) {
+		printf("TDO DATA:");
+		for (i=0; i<bd->alloced_bytes; i++)
+			printf(" %02x", bd->tdo_data[i]);
+		printf("\n");
+	}
+	if (bd->tdi_mask) {
+		printf("TDI MASK:");
+		for (i=0; i<bd->alloced_bytes; i++)
+			printf(" %02x", bd->tdi_mask[i]);
+		printf("\n");
+	}
+	if (bd->tdo_mask) {
+		printf("TDO MASK:");
+		for (i=0; i<bd->alloced_bytes; i++)
+			printf(" %02x", bd->tdo_mask[i]);
+		printf("\n");
+	}
+#endif
+	return p;
+}
+
+static int getbit(unsigned char *data, int n)
+{
+	return (data[n/8] & (1 << (7 - n%8))) ? 1 : 0;
+}
+
+static int bitdata_play(struct libxsvf_host *h, struct bitdata_s *bd, enum libxsvf_tap_state estate)
+{
+	int left_padding = (8 - bd->len % 8) % 8;
+	int tdo_error = 0;
+	int tms = 0;
+	int i;
+
+	for (i=bd->len+left_padding-1; i >= left_padding; i--) {
+		if (i == left_padding && h->tap_state != estate) {
+			h->tap_state++;
+			tms = 1;
+		}
+		int tdi = -1;
+		if (bd->tdi_data) {
+			if (!bd->tdi_mask || getbit(bd->tdi_mask, i))
+				tdi = getbit(bd->tdi_data, i);
+		}
+		int tdo = -1;
+		if (bd->tdo_data && bd->has_tdo_data && (!bd->tdo_mask || getbit(bd->tdo_mask, i)))
+			tdo = getbit(bd->tdo_data, i);
+		int rmask = bd->ret_mask && getbit(bd->ret_mask, i);
+		
+		if (LIBXSVF_HOST_PULSE_TCK(tms, tdi, tdo, rmask, 0) < 0)
+			tdo_error = 1;
+	}
+
+	if (tms)
+		LIBXSVF_HOST_REPORT_TAPSTATE();
+
+	if (!tdo_error)
+		return 0;
+
+	LIBXSVF_HOST_REPORT_ERROR("TDO mismatchy.");
+	//return -1;
+	return 0;
+}
+
+/*
+Streaming feed the SVF file, repeatedy call this
+as each data packet becomes available
+len is not used as length but as a command to initialize or finish
+Start: len == 0
+Normal: len > 0
+End: len == -1
+repeat-call it while return value > 0
+Return: -1 on error
+*/
+int libxsvf_feed(struct libxsvf_host *h, int len)
+{
+	static char *command_buffer = (void*)0;
+	static int command_buffer_len = 0;
+	static int rc, i;
+
+	static struct bitdata_s bd_hdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	static struct bitdata_s bd_hir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	static struct bitdata_s bd_tdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	static struct bitdata_s bd_tir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	static struct bitdata_s bd_sdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	static struct bitdata_s bd_sir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+
+	static int state_endir = LIBXSVF_TAP_IDLE;
+	static int state_enddr = LIBXSVF_TAP_IDLE;
+	static int state_run = LIBXSVF_TAP_IDLE;
+	static int state_endrun = LIBXSVF_TAP_IDLE;
+
+        static char cmd_reportstring[256];
+
+        if(len == 0)
+        {
+		/* 0 len means stream start, reset all state vars and exit */
+		command_buffer = (void*)0;
+		command_buffer_len = 0;
+		rc = 0; /* allow further processing */
+
+		bitdata_zero(h, &bd_hdr);
+		bitdata_zero(h, &bd_hir);
+		bitdata_zero(h, &bd_tdr);
+		bitdata_zero(h, &bd_tir);
+		bitdata_zero(h, &bd_sdr);
+		bitdata_zero(h, &bd_sir);
+
+		state_endir = LIBXSVF_TAP_IDLE;
+		state_enddr = LIBXSVF_TAP_IDLE;
+		state_run = LIBXSVF_TAP_IDLE;
+		state_endrun = LIBXSVF_TAP_IDLE;
+
+		cmd_reportstring[0] = '\0';
+		
+		read_command(h, &command_buffer, NULL); /* zero buffer reading pointer */
+
+		return 0;
+        }
+
+        if(len == -1)
+        {
+		/* -1 len means the end of stream, free the buffers */
+		if (LIBXSVF_HOST_SYNC() != 0 && rc >= 0 ) {
+			LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+			rc = -1;
+		}
+
+		bitdata_free(h, &bd_hdr, LIBXSVF_MEM_SVF_HDR_TDI_DATA);
+		bitdata_free(h, &bd_hir, LIBXSVF_MEM_SVF_HIR_TDI_DATA);
+		bitdata_free(h, &bd_tdr, LIBXSVF_MEM_SVF_TDR_TDI_DATA);
+		bitdata_free(h, &bd_tir, LIBXSVF_MEM_SVF_TIR_TDI_DATA);
+		bitdata_free(h, &bd_sdr, LIBXSVF_MEM_SVF_SDR_TDI_DATA);
+		bitdata_free(h, &bd_sir, LIBXSVF_MEM_SVF_SIR_TDI_DATA);
+
+		LIBXSVF_HOST_REALLOC(command_buffer, 0, LIBXSVF_MEM_SVF_COMMANDBUF);
+		return -1;
+        }
+
+        if(rc < 0)
+        	return rc; /* after first error, don't process anything further */
+	
+	#if 0
+	// debugging read_command() parser
+	while(rc >= 0)
+	{
+		rc = read_command(h, &command_buffer, &command_buffer_len);
+		if(rc == -2)
+		{
+			rc = 0;
+			return -2;
+		}
+		read_command(h, &command_buffer, NULL); // reset internal p
+		if(rc >= 0)
+			printf(">>%s<<\n", command_buffer);
+	}
+	return 2;
+	#endif
+
+        /* len > 0, process chunk of data */
+	while (1)
+	{
+		rc = read_command(h, &command_buffer, &command_buffer_len);
+		if(rc == -2) /* buffer empty */
+		{
+			rc = 0; /* allow continuation when called next time */
+			return -2;
+		}
+		read_command(h, &command_buffer, NULL); // reset internal p
+        LIBXSVF_HOST_REPORT_ERROR(command_buffer);
+		if (rc <= 0)
+			break;
+
+		const char *p = command_buffer;
+
+		LIBXSVF_HOST_REPORT_STATUS(command_buffer);
+
+		if (!strtokencmp(p, "ENDIR")) {
+			p += strtokenskip(p);
+			state_endir = token2tapstate(p);
+			if (state_endir < 0)
+				goto syntax_error;
+			p += strtokenskip(p);
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "ENDDR")) {
+			p += strtokenskip(p);
+			state_enddr = token2tapstate(p);
+			if (state_endir < 0)
+				goto syntax_error;
+			p += strtokenskip(p);
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "FREQUENCY")) {
+			unsigned long number = 0;
+			int got_decimal_point = 0;
+			int decimal_digits = 0;
+			int exp = 0;
+			p += strtokenskip(p);
+			if (*p < '0' || *p > '9')
+				goto syntax_error;
+			while ((*p >= '0' && *p <= '9') || (*p == '.')) {
+				if (*p == '.') {
+					got_decimal_point = 1;
+				} else {
+					if (got_decimal_point)
+						decimal_digits++;
+					number = number*10 + (*p - '0');
+				}
+				p++;
+			}
+			if(*p == 'E' || *p == 'e') {
+				p++;
+				if (*p == '+')
+					p++;
+				while (*p >= '0' && *p <= '9') {
+					exp = exp*10 + (*p - '0');
+					p++;
+				}
+				exp -= decimal_digits;
+				if (exp < 0)
+					goto syntax_error;
+				for(i=0; i<exp; i++)
+					number *= 10;
+			}
+			while (*p == ' ') {
+				p++;
+			}
+			p += strtokenskip(p);
+			if (LIBXSVF_HOST_SET_FREQUENCY(number) < 0) {
+				LIBXSVF_HOST_REPORT_ERROR("FREQUENCY command failed!");
+				goto error;
+			}
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "HDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_hdr, LIBXSVF_MEM_SVF_HDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "HIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_hir, LIBXSVF_MEM_SVF_HIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "PIO") || !strtokencmp(p, "PIOMAP")) {
+			goto unsupported_error;
+		}
+
+		if (!strtokencmp(p, "RUNTEST")) {
+			p += strtokenskip(p);
+			int tck_count = -1;
+			int sck_count = -1;
+			int min_time = -1;
+			int max_time = -1;
+			while (*p) {
+			        printf("parsing p=\"%s\"\n", p);
+				int got_maximum = 0;
+				if (!strtokencmp(p, "MAXIMUM")) {
+					p += strtokenskip(p);
+					got_maximum = 1;
+				}
+				int got_endstate = 0;
+				if (!strtokencmp(p, "ENDSTATE")) {
+					p += strtokenskip(p);
+					got_endstate = 1;
+				}
+				int st = token2tapstate(p);
+				if (st >= 0) {
+					p += strtokenskip(p);
+					if (got_endstate)
+						state_endrun = st;
+					else
+						state_run = st;
+					continue;
+				}
+				if (*p < '0' || *p > '9')
+					goto syntax_error;
+				int number = 0;
+				int exp = 0, expsign = 1;
+				int number_e6, exp_e6;
+				while (*p >= '0' && *p <= '9') {
+					number = number*10 + (*p - '0');
+					p++;
+				}
+				if(*p == '.')
+				{
+					p++;
+					while (*p >= '0' && *p <= '9')
+						p++;
+					// FIXME: accept fractional part
+				}
+				if(*p == 'E' || *p == 'e') {
+					p++;
+					if(*p == '-') {
+						expsign = -1;
+						p++;
+					}
+					if(*p == '+') {
+						expsign = 1;
+						p++;
+					}
+					while (*p >= '0' && *p <= '9') {
+						exp = exp*10 + (*p - '0');
+						p++;
+					}
+					exp = exp * expsign;
+					number_e6 = number;
+					exp_e6 = exp + 6;
+					while (exp < 0) {
+						number /= 10;
+						exp++;
+					}
+					while (exp > 0) {
+						number *= 10;
+						exp--;
+					}
+					while (exp_e6 < 0) {
+						number_e6 /= 10;
+						exp_e6++;
+					}
+					while (exp_e6 > 0) {
+						number_e6 *= 10;
+						exp_e6--;
+					}
+				} else {
+					number_e6 = number * 1000000;
+				}
+				while (*p == ' ') {
+					p++;
+				}
+				if (!strtokencmp(p, "SEC")) {
+					p += strtokenskip(p);
+					if (got_maximum)
+						max_time = number_e6;
+					else
+						min_time = number_e6;
+					continue;
+				}
+				if (!strtokencmp(p, "TCK")) {
+					p += strtokenskip(p);
+					tck_count = number;
+					continue;
+				}
+				if (!strtokencmp(p, "SCK")) {
+					p += strtokenskip(p);
+					sck_count = number;
+					continue;
+				}
+				goto syntax_error;
+			}
+			if (libxsvf_tap_walk(h, state_run) < 0)
+				goto error;
+			if (max_time >= 0) {
+				LIBXSVF_HOST_REPORT_ERROR("WARNING: Maximum time in SVF RUNTEST command is ignored.");
+			}
+			if (sck_count >= 0) {
+				for (i=0; i < sck_count; i++) {
+					LIBXSVF_HOST_PULSE_SCK();
+				}
+			}
+			if (min_time >= 0 || tck_count >= 0) {
+				LIBXSVF_HOST_UDELAY(min_time >= 0 ? min_time : 0, 0, tck_count >= 0 ? tck_count : 0);
+			}
+			if (libxsvf_tap_walk(h, state_endrun) < 0)
+				goto error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "SDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_sdr, LIBXSVF_MEM_SVF_SDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			if (libxsvf_tap_walk(h, LIBXSVF_TAP_DRSHIFT) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_hdr, bd_sdr.len+bd_tdr.len > 0 ? LIBXSVF_TAP_DRSHIFT : state_enddr) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_sdr, bd_tdr.len > 0 ? LIBXSVF_TAP_DRSHIFT : state_enddr) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_tdr, state_enddr) < 0)
+				goto error;
+			if (libxsvf_tap_walk(h, state_enddr) < 0)
+				goto error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "SIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_sir, LIBXSVF_MEM_SVF_SIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			if (libxsvf_tap_walk(h, LIBXSVF_TAP_IRSHIFT) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_hir, bd_sir.len+bd_tir.len > 0 ? LIBXSVF_TAP_IRSHIFT : state_endir) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_sir, bd_tir.len > 0 ? LIBXSVF_TAP_IRSHIFT : state_endir) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_tir, state_endir) < 0)
+				goto error;
+			if (libxsvf_tap_walk(h, state_endir) < 0)
+				goto error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "STATE")) {
+			p += strtokenskip(p);
+			while (*p) {
+				int st = token2tapstate(p);
+				if (st < 0)
+					goto syntax_error;
+				if (libxsvf_tap_walk(h, st) < 0)
+					goto error;
+				p += strtokenskip(p);
+			}
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_tdr, LIBXSVF_MEM_SVF_TDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_tir, LIBXSVF_MEM_SVF_TIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TRST")) {
+			p += strtokenskip(p);
+			if (!strtokencmp(p, "ON")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(1);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "OFF")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(0);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "Z")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(-1);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "ABSENT")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(-2);
+				goto eol_check;
+			}
+			goto syntax_error;
+		}
+
+eol_check:
+		while (*p == ' ')
+			p++;
+		if (*p == 0)
+			continue;
+
+syntax_error:
+		LIBXSVF_HOST_REPORT_ERROR("Syntax Error");
+		if (0) {
+unsupported_error:
+			LIBXSVF_HOST_REPORT_ERROR("Error in SVF input: unsupported command:");
+		}
+		// LIBXSVF_HOST_REPORT_ERROR(command_buffer);
+error:
+		rc = -1;
+		break;
+	}
+
+        return rc;
+}
+
+int libxsvf_svf_packet(struct libxsvf_host *h, int index, int final)
+{
+	int rc = 1;
+	if(index == 0)
+	{
+		rc = 1;
+		/* open tap */
+		#if 1
+		h->tap_state = LIBXSVF_TAP_INIT;
+		if (LIBXSVF_HOST_SETUP() < 0) {
+			LIBXSVF_HOST_REPORT_ERROR("Setup of JTAG interface failed.");
+			return -1;
+		}
+		#endif
+		libxsvf_feed(h, 0); // reset vars, start the stream
+		LIBXSVF_HOST_REPORT_ERROR("Start");
+	}
+	rc = libxsvf_feed(h, 1);
+	if(final)
+	{
+		if(rc != -1)
+			LIBXSVF_HOST_REPORT_ERROR("Done");
+		/* close tap */
+		#if 1
+		libxsvf_tap_walk(h, LIBXSVF_TAP_RESET);
+		if (LIBXSVF_HOST_SYNC() != 0 && rc >= 0 ) {
+			LIBXSVF_HOST_REPORT_ERROR("TDO mismatch in TAP reset. (this is not possible!)");
+			rc = -1;
+		}
+		#endif
+		LIBXSVF_HOST_SHUTDOWN();
+		libxsvf_feed(h, -1); // last call to finish
+	}
+	return rc;
+}
+
+/* streamable replacement for libxsvf_svf, should work the same
+** first examples work, further testing is recommended
+*/
+int libxsvf_svf_stream(struct libxsvf_host *h)
+{
+	int len = 1;
+	char buf[256];
+	int rc = 1;
+	libxsvf_feed(h, 0); // reset vars, start the stream
+	while(rc > 0)
+	  rc = libxsvf_feed(h, 1);
+	return libxsvf_feed(h, -1); // last call to finish
+}
+
+int libxsvf_svf(struct libxsvf_host *h)
+{
+	char *command_buffer = (void*)0;
+	int command_buffer_len = 0;
+	int rc, i;
+
+	struct bitdata_s bd_hdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_hir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_tdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_tir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_sdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_sir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+
+	int state_endir = LIBXSVF_TAP_IDLE;
+	int state_enddr = LIBXSVF_TAP_IDLE;
+	int state_run = LIBXSVF_TAP_IDLE;
+	int state_endrun = LIBXSVF_TAP_IDLE;
+
+        int cmd_count = 0;
+        char cmd_reportstring[256];
+	while (1)
+	{
+		rc = read_command(h, &command_buffer, &command_buffer_len);
+
+		if (rc <= 0)
+			break;
+
+		cmd_count++;
+		#if 0
+		if((cmd_count % 1000) == 0)
+		{
+			// print progress every 1000 commands
+			// sprintf(cmd_reportstring, "%d commands", cmd_count);
+			LIBXSVF_HOST_REPORT_ERROR(cmd_reportstring);
+		}
+		#endif
+		const char *p = command_buffer;
+
+		LIBXSVF_HOST_REPORT_STATUS(command_buffer);
+
+		if (!strtokencmp(p, "ENDIR")) {
+			p += strtokenskip(p);
+			state_endir = token2tapstate(p);
+			if (state_endir < 0)
+				goto syntax_error;
+			p += strtokenskip(p);
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "ENDDR")) {
+			p += strtokenskip(p);
+			state_enddr = token2tapstate(p);
+			if (state_endir < 0)
+				goto syntax_error;
+			p += strtokenskip(p);
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "FREQUENCY")) {
+			unsigned long number = 0;
+			int got_decimal_point = 0;
+			int decimal_digits = 0;
+			int exp = 0;
+			p += strtokenskip(p);
+			if (*p < '0' || *p > '9')
+				goto syntax_error;
+			while ((*p >= '0' && *p <= '9') || (*p == '.')) {
+				if (*p == '.') {
+					got_decimal_point = 1;
+				} else {
+					if (got_decimal_point)
+						decimal_digits++;
+					number = number*10 + (*p - '0');
+				}
+				p++;
+			}
+			if(*p == 'E' || *p == 'e') {
+				p++;
+				if (*p == '+')
+					p++;
+				while (*p >= '0' && *p <= '9') {
+					exp = exp*10 + (*p - '0');
+					p++;
+				}
+				exp -= decimal_digits;
+				if (exp < 0)
+					goto syntax_error;
+				for(i=0; i<exp; i++)
+					number *= 10;
+			}
+			while (*p == ' ') {
+				p++;
+			}
+			p += strtokenskip(p);
+			if (LIBXSVF_HOST_SET_FREQUENCY(number) < 0) {
+				LIBXSVF_HOST_REPORT_ERROR("FREQUENCY command failed!");
+				goto error;
+			}
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "HDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_hdr, LIBXSVF_MEM_SVF_HDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "HIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_hir, LIBXSVF_MEM_SVF_HIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "PIO") || !strtokencmp(p, "PIOMAP")) {
+			goto unsupported_error;
+		}
+
+		if (!strtokencmp(p, "RUNTEST")) {
+			p += strtokenskip(p);
+			int tck_count = -1;
+			int sck_count = -1;
+			int min_time = -1;
+			int max_time = -1;
+			while (*p) {
+			        // printf("parsing p=\"%s\"\n", p);
+				int got_maximum = 0;
+				if (!strtokencmp(p, "MAXIMUM")) {
+					p += strtokenskip(p);
+					got_maximum = 1;
+				}
+				int got_endstate = 0;
+				if (!strtokencmp(p, "ENDSTATE")) {
+					p += strtokenskip(p);
+					got_endstate = 1;
+				}
+				int st = token2tapstate(p);
+				if (st >= 0) {
+					p += strtokenskip(p);
+					if (got_endstate)
+						state_endrun = st;
+					else
+						state_run = st;
+					continue;
+				}
+				if (*p < '0' || *p > '9')
+					goto syntax_error;
+				int number = 0;
+				int exp = 0, expsign = 1;
+				int number_e6, exp_e6;
+				while (*p >= '0' && *p <= '9') {
+					number = number*10 + (*p - '0');
+					p++;
+				}
+				if(*p == '.')
+				{
+					p++;
+					while (*p >= '0' && *p <= '9')
+						p++;
+					// FIXME: accept fractional part
+				}
+				if(*p == 'E' || *p == 'e') {
+					p++;
+					if(*p == '-') {
+						expsign = -1;
+						p++;
+					}
+					if(*p == '+') {
+						expsign = 1;
+						p++;
+					}
+					while (*p >= '0' && *p <= '9') {
+						exp = exp*10 + (*p - '0');
+						p++;
+					}
+					exp = exp * expsign;
+					number_e6 = number;
+					exp_e6 = exp + 6;
+					while (exp < 0) {
+						number /= 10;
+						exp++;
+					}
+					while (exp > 0) {
+						number *= 10;
+						exp--;
+					}
+					while (exp_e6 < 0) {
+						number_e6 /= 10;
+						exp_e6++;
+					}
+					while (exp_e6 > 0) {
+						number_e6 *= 10;
+						exp_e6--;
+					}
+				} else {
+					number_e6 = number * 1000000;
+				}
+				while (*p == ' ') {
+					p++;
+				}
+				if (!strtokencmp(p, "SEC")) {
+					p += strtokenskip(p);
+					if (got_maximum)
+						max_time = number_e6;
+					else
+						min_time = number_e6;
+					continue;
+				}
+				if (!strtokencmp(p, "TCK")) {
+					p += strtokenskip(p);
+					tck_count = number;
+					continue;
+				}
+				if (!strtokencmp(p, "SCK")) {
+					p += strtokenskip(p);
+					sck_count = number;
+					continue;
+				}
+				goto syntax_error;
+			}
+			if (libxsvf_tap_walk(h, state_run) < 0)
+				goto error;
+			if (max_time >= 0) {
+				LIBXSVF_HOST_REPORT_ERROR("WARNING: Maximum time in SVF RUNTEST command is ignored.");
+			}
+			if (sck_count >= 0) {
+				for (i=0; i < sck_count; i++) {
+					LIBXSVF_HOST_PULSE_SCK();
+				}
+			}
+			if (min_time >= 0 || tck_count >= 0) {
+				LIBXSVF_HOST_UDELAY(min_time >= 0 ? min_time : 0, 0, tck_count >= 0 ? tck_count : 0);
+			}
+			if (libxsvf_tap_walk(h, state_endrun) < 0)
+				goto error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "SDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_sdr, LIBXSVF_MEM_SVF_SDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			if (libxsvf_tap_walk(h, LIBXSVF_TAP_DRSHIFT) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_hdr, bd_sdr.len+bd_tdr.len > 0 ? LIBXSVF_TAP_DRSHIFT : state_enddr) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_sdr, bd_tdr.len > 0 ? LIBXSVF_TAP_DRSHIFT : state_enddr) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_tdr, state_enddr) < 0)
+				goto error;
+			if (libxsvf_tap_walk(h, state_enddr) < 0)
+				goto error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "SIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_sir, LIBXSVF_MEM_SVF_SIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			if (libxsvf_tap_walk(h, LIBXSVF_TAP_IRSHIFT) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_hir, bd_sir.len+bd_tir.len > 0 ? LIBXSVF_TAP_IRSHIFT : state_endir) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_sir, bd_tir.len > 0 ? LIBXSVF_TAP_IRSHIFT : state_endir) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_tir, state_endir) < 0)
+				goto error;
+			if (libxsvf_tap_walk(h, state_endir) < 0)
+				goto error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "STATE")) {
+			p += strtokenskip(p);
+			while (*p) {
+				int st = token2tapstate(p);
+				if (st < 0)
+					goto syntax_error;
+				if (libxsvf_tap_walk(h, st) < 0)
+					goto error;
+				p += strtokenskip(p);
+			}
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_tdr, LIBXSVF_MEM_SVF_TDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_tir, LIBXSVF_MEM_SVF_TIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TRST")) {
+			p += strtokenskip(p);
+			if (!strtokencmp(p, "ON")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(1);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "OFF")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(0);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "Z")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(-1);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "ABSENT")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(-2);
+				goto eol_check;
+			}
+			goto syntax_error;
+		}
+
+eol_check:
+		while (*p == ' ')
+			p++;
+		if (*p == 0)
+			continue;
+
+syntax_error:
+		sprintf(cmd_reportstring, "Command %d: SVF Syntax Error:", cmd_count);
+		LIBXSVF_HOST_REPORT_ERROR(cmd_reportstring);
+		if (0) {
+unsupported_error:
+			LIBXSVF_HOST_REPORT_ERROR("Error in SVF input: unsupported command:");
+		}
+		LIBXSVF_HOST_REPORT_ERROR(command_buffer);
+error:
+		rc = -1;
+		break;
+	}
+
+	if (LIBXSVF_HOST_SYNC() != 0 && rc >= 0 ) {
+		LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+		//rc = -1;
+	}
+
+	bitdata_free(h, &bd_hdr, LIBXSVF_MEM_SVF_HDR_TDI_DATA);
+	bitdata_free(h, &bd_hir, LIBXSVF_MEM_SVF_HIR_TDI_DATA);
+	bitdata_free(h, &bd_tdr, LIBXSVF_MEM_SVF_TDR_TDI_DATA);
+	bitdata_free(h, &bd_tir, LIBXSVF_MEM_SVF_TIR_TDI_DATA);
+	bitdata_free(h, &bd_sdr, LIBXSVF_MEM_SVF_SDR_TDI_DATA);
+	bitdata_free(h, &bd_sir, LIBXSVF_MEM_SVF_SIR_TDI_DATA);
+
+	LIBXSVF_HOST_REALLOC(command_buffer, 0, LIBXSVF_MEM_SVF_COMMANDBUF);
+
+	return rc;
+}
+

+ 173 - 0
sw/esp32s2/src/libxsvf/tap.c

@@ -0,0 +1,173 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+static void tap_transition(struct libxsvf_host *h, int v)
+{
+	LIBXSVF_HOST_PULSE_TCK(v, -1, -1, 0, 0);
+}
+
+int libxsvf_tap_walk(struct libxsvf_host *h, enum libxsvf_tap_state s)
+{
+	int i, j;
+	for (i=0; s != h->tap_state; i++)
+	{
+		switch (h->tap_state)
+		{
+		/* Special States */
+		case LIBXSVF_TAP_INIT:
+			for (j = 0; j < 6; j++)
+				tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_RESET;
+			break;
+		case LIBXSVF_TAP_RESET:
+			tap_transition(h, 0);
+			h->tap_state = LIBXSVF_TAP_IDLE;
+			break;
+		case LIBXSVF_TAP_IDLE:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_DRSELECT;
+			break;
+
+		/* DR States */
+		case LIBXSVF_TAP_DRSELECT:
+			if (s >= LIBXSVF_TAP_IRSELECT || s == LIBXSVF_TAP_RESET) {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_IRSELECT;
+			} else {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_DRCAPTURE;
+			}
+			break;
+		case LIBXSVF_TAP_DRCAPTURE:
+			if (s == LIBXSVF_TAP_DRSHIFT) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_DRSHIFT;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DREXIT1;
+			}
+			break;
+		case LIBXSVF_TAP_DRSHIFT:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_DREXIT1;
+			break;
+		case LIBXSVF_TAP_DREXIT1:
+			if (s == LIBXSVF_TAP_DRPAUSE) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_DRPAUSE;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DRUPDATE;
+			}
+			break;
+		case LIBXSVF_TAP_DRPAUSE:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_DREXIT2;
+			break;
+		case LIBXSVF_TAP_DREXIT2:
+			if (s == LIBXSVF_TAP_DRSHIFT) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_DRSHIFT;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DRUPDATE;
+			}
+			break;
+		case LIBXSVF_TAP_DRUPDATE:
+			if (s == LIBXSVF_TAP_IDLE) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IDLE;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DRSELECT;
+			}
+			break;
+
+		/* IR States */
+		case LIBXSVF_TAP_IRSELECT:
+			if (s == LIBXSVF_TAP_RESET) {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_RESET;
+			} else {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IRCAPTURE;
+			}
+			break;
+		case LIBXSVF_TAP_IRCAPTURE:
+			if (s == LIBXSVF_TAP_IRSHIFT) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IRSHIFT;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_IREXIT1;
+			}
+			break;
+		case LIBXSVF_TAP_IRSHIFT:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_IREXIT1;
+			break;
+		case LIBXSVF_TAP_IREXIT1:
+			if (s == LIBXSVF_TAP_IRPAUSE) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IRPAUSE;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_IRUPDATE;
+			}
+			break;
+		case LIBXSVF_TAP_IRPAUSE:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_IREXIT2;
+			break;
+		case LIBXSVF_TAP_IREXIT2:
+			if (s == LIBXSVF_TAP_IRSHIFT) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IRSHIFT;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_IRUPDATE;
+			}
+			break;
+		case LIBXSVF_TAP_IRUPDATE:
+			if (s == LIBXSVF_TAP_IDLE) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IDLE;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DRSELECT;
+			}
+			break;
+
+		default:
+			LIBXSVF_HOST_REPORT_ERROR("Illegal tap state.");
+			return -1;
+		}
+		if (h->report_tapstate)
+			LIBXSVF_HOST_REPORT_TAPSTATE();
+		if (i>10) {
+			LIBXSVF_HOST_REPORT_ERROR("Loop in tap walker.");
+			return -1;
+		}
+	}
+
+	return 0;
+}

+ 486 - 0
sw/esp32s2/src/libxsvf/xsvf.c

@@ -0,0 +1,486 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+/* command codes as defined in xilinx xapp503 */
+enum xsvf_cmd {
+	XCOMPLETE       = 0x00,
+	XTDOMASK        = 0x01,
+	XSIR            = 0x02,
+	XSDR            = 0x03,
+	XRUNTEST        = 0x04,
+	XREPEAT         = 0x07,
+	XSDRSIZE        = 0x08,
+	XSDRTDO         = 0x09,
+	XSETSDRMASKS    = 0x0A,
+	XSDRINC         = 0x0B,
+	XSDRB           = 0x0C,
+	XSDRC           = 0x0D,
+	XSDRE           = 0x0E,
+	XSDRTDOB        = 0x0F,
+	XSDRTDOC        = 0x10,
+	XSDRTDOE        = 0x11,
+	XSTATE          = 0x12,
+	XENDIR          = 0x13,
+	XENDDR          = 0x14,
+	XSIR2           = 0x15,
+	XCOMMENT        = 0x16,
+	XWAIT           = 0x17,
+	/* Extensions used in svf2xsvf.py */
+	XWAITSTATE      = 0x18,
+	XTRST           = 0x1c
+};
+
+// This is to not confuse the VIM syntax highlighting
+#define VAL_OPEN (
+#define VAL_CLOSE )
+
+#define READ_BITS(_buf, _len) do {                                          \
+	unsigned char *_p = _buf; int _i;                                   \
+	for (_i=0; _i<(_len); _i+=8) {                                      \
+		int tmp = LIBXSVF_HOST_GETBYTE();                           \
+		if (tmp < 0) {                                              \
+			LIBXSVF_HOST_REPORT_ERROR("Unexpected EOF.");       \
+			goto error;                                         \
+		}                                                           \
+		*(_p++) = tmp;                                              \
+	}                                                                   \
+} while (0)
+
+#define READ_LONG() VAL_OPEN{                                               \
+	long _buf = 0; int _i;                                              \
+	for (_i=0; _i<4; _i++) {                                            \
+		int tmp = LIBXSVF_HOST_GETBYTE();                           \
+		if (tmp < 0) {                                              \
+			LIBXSVF_HOST_REPORT_ERROR("Unexpected EOF.");       \
+			goto error;                                         \
+		}                                                           \
+		_buf = _buf << 8 | tmp;                                     \
+	}                                                                   \
+	_buf;                                                               \
+}VAL_CLOSE
+
+#define READ_BYTE() VAL_OPEN{                                               \
+	int _tmp = LIBXSVF_HOST_GETBYTE();                                  \
+	if (_tmp < 0) {                                                     \
+		LIBXSVF_HOST_REPORT_ERROR("Unexpected EOF.");               \
+		goto error;                                                 \
+	}                                                                   \
+	_tmp;                                                               \
+}VAL_CLOSE
+
+#define SHIFT_DATA(_inp, _outp, _maskp, _len, _state, _estate, _edelay, _ret) do { \
+	if (shift_data(h, _inp, _outp, _maskp, _len, _state, _estate, _edelay, _ret) < 0) { \
+		goto error;                                                 \
+	}                                                                   \
+} while (0)
+
+#define TAP(_state) do {                                                    \
+	if (libxsvf_tap_walk(h, _state) < 0)                                \
+		goto error;                                                 \
+} while (0)
+
+static int bits2bytes(int bits)
+{
+	return (bits+7) / 8;
+}
+
+static int getbit(unsigned char *data, int n)
+{
+	return (data[n/8] & (1 << (7 - n%8))) ? 1 : 0;
+}
+
+static void setbit(unsigned char *data, int n, int v)
+{
+	unsigned char mask = 1 << (7 - n%8);
+	if (v)
+		data[n/8] |= mask;
+	else
+		data[n/8] &= ~mask;
+}
+
+static int xilinx_tap(int state)
+{
+	/* state codes as defined in xilinx xapp503 */
+	switch (state)
+	{
+	case 0x00:
+		return LIBXSVF_TAP_RESET;
+		break;
+	case 0x01:
+		return LIBXSVF_TAP_IDLE;
+		break;
+	case 0x02:
+		return LIBXSVF_TAP_DRSELECT;
+		break;
+	case 0x03:
+		return LIBXSVF_TAP_DRCAPTURE;
+		break;
+	case 0x04:
+		return LIBXSVF_TAP_DRSHIFT;
+		break;
+	case 0x05:
+		return LIBXSVF_TAP_DREXIT1;
+		break;
+	case 0x06:
+		return LIBXSVF_TAP_DRPAUSE;
+		break;
+	case 0x07:
+		return LIBXSVF_TAP_DREXIT2;
+		break;
+	case 0x08:
+		return LIBXSVF_TAP_DRUPDATE;
+		break;
+	case 0x09:
+		return LIBXSVF_TAP_IRSELECT;
+		break;
+	case 0x0A:
+		return LIBXSVF_TAP_IRCAPTURE;
+		break;
+	case 0x0B:
+		return LIBXSVF_TAP_IRSHIFT;
+		break;
+	case 0x0C:
+		return LIBXSVF_TAP_IREXIT1;
+		break;
+	case 0x0D:
+		return LIBXSVF_TAP_IRPAUSE;
+		break;
+	case 0x0E:
+		return LIBXSVF_TAP_IREXIT2;
+		break;
+	case 0x0F:
+		return LIBXSVF_TAP_IRUPDATE;
+		break;
+	}
+	return -1;
+}
+
+static int shift_data(struct libxsvf_host *h, unsigned char *inp, unsigned char *outp, unsigned char *maskp, int len, enum libxsvf_tap_state state, enum libxsvf_tap_state estate, int edelay, int retries)
+{
+	int left_padding = (8 - len % 8) % 8;
+	int with_retries = retries > 0;
+	int i;
+
+	if (with_retries && LIBXSVF_HOST_SYNC() < 0) {
+		LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+		return -1;
+	}
+
+	while (1)
+	{
+		int tdo_error = 0;
+		int tms = 0;
+
+		TAP(state);
+		tms = 0;
+
+		for (i=len+left_padding-1; i>=left_padding; i--) {
+			if (i == left_padding && h->tap_state != estate) {
+				h->tap_state++;
+				tms = 1;
+			}
+			int tdi = getbit(inp, i);
+			int tdo = -1;
+			if (maskp && getbit(maskp, i))
+				tdo = outp && getbit(outp, i);
+			int sync = with_retries && i == left_padding;
+			if (LIBXSVF_HOST_PULSE_TCK(tms, tdi, tdo, 0, sync) < 0)
+				tdo_error = 1;
+		}
+
+		if (tms)
+			LIBXSVF_HOST_REPORT_TAPSTATE();
+	
+		if (edelay) {
+			TAP(LIBXSVF_TAP_IDLE);
+			LIBXSVF_HOST_UDELAY(edelay, 0, edelay);
+		} else {
+			TAP(estate);
+		}
+
+		if (!tdo_error)
+			return 0;
+
+		if (retries <= 0) {
+			LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+			return -1;
+		}
+
+		retries--;
+	}
+
+error:
+	return -1;
+}
+
+int libxsvf_xsvf(struct libxsvf_host *h)
+{
+	int rc = 0;
+	int i, j;
+
+	unsigned char *buf_tdi_data = (void*)0;
+	unsigned char *buf_tdo_data = (void*)0;
+	unsigned char *buf_tdo_mask = (void*)0;
+	unsigned char *buf_addr_mask = (void*)0;
+	unsigned char *buf_data_mask = (void*)0;
+
+	long state_dr_size = 0;
+	long state_data_size = 0;
+	long state_runtest = 0;
+	unsigned char state_xendir = 0;
+	unsigned char state_xenddr = 0;
+	unsigned char state_retries = 0;
+	unsigned char cmd = 0;
+
+	while (1)
+	{
+		unsigned char last_cmd = cmd;
+		cmd = LIBXSVF_HOST_GETBYTE();
+
+#define STATUS(_c) LIBXSVF_HOST_REPORT_STATUS("XSVF Command " #_c);
+
+		switch (cmd)
+		{
+		case XCOMPLETE: {
+			STATUS(XCOMPLETE);
+			goto got_complete_command;
+		  }
+		case XTDOMASK: {
+			STATUS(XTDOMASK);
+			READ_BITS(buf_tdo_mask, state_dr_size);
+			break;
+		  }
+		case XSIR: {
+			STATUS(XSIR);
+			int length = READ_BYTE();
+			unsigned char buf[bits2bytes(length)];
+			READ_BITS(buf, length);
+			SHIFT_DATA(buf, (void*)0, (void*)0, length, LIBXSVF_TAP_IRSHIFT,
+					state_xendir ? LIBXSVF_TAP_IRPAUSE : LIBXSVF_TAP_IDLE,
+					state_runtest, state_retries);
+			break;
+		  }
+		case XSDR: {
+			STATUS(XSDR);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, buf_tdo_mask, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+					state_xenddr ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE,
+					state_runtest, state_retries);
+			break;
+		  }
+		case XRUNTEST: {
+			STATUS(XRUNTEST);
+			state_runtest = READ_LONG();
+			break;
+		  }
+		case XREPEAT: {
+			STATUS(XREPEAT);
+			state_retries = READ_BYTE();
+			break;
+		  }
+		case XSDRSIZE: {
+			STATUS(XSDRSIZE);
+			state_dr_size = READ_LONG();
+			buf_tdi_data = LIBXSVF_HOST_REALLOC(buf_tdi_data, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_TDI_DATA);
+			buf_tdo_data = LIBXSVF_HOST_REALLOC(buf_tdo_data, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_TDO_DATA);
+			buf_tdo_mask = LIBXSVF_HOST_REALLOC(buf_tdo_mask, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_TDO_MASK);
+			buf_addr_mask = LIBXSVF_HOST_REALLOC(buf_addr_mask, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_ADDR_MASK);
+			buf_data_mask = LIBXSVF_HOST_REALLOC(buf_data_mask, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_DATA_MASK);
+			if (!buf_tdi_data || !buf_tdo_data || !buf_tdo_mask || !buf_addr_mask || !buf_data_mask) {
+				LIBXSVF_HOST_REPORT_ERROR("Allocating memory failed.");
+				goto error;
+			}
+			break;
+		  }
+		case XSDRTDO: {
+			STATUS(XSDRTDO);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			READ_BITS(buf_tdo_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, buf_tdo_mask, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+					state_xenddr ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE,
+					state_runtest, state_retries);
+			break;
+		  }
+		case XSETSDRMASKS: {
+			STATUS(XSETSDRMASKS);
+			READ_BITS(buf_addr_mask, state_dr_size);
+			READ_BITS(buf_data_mask, state_dr_size);
+			state_data_size = 0;
+			for (i=0; i<state_dr_size; i++)
+				state_data_size += getbit(buf_data_mask, i);
+			break;
+		  }
+		case XSDRINC: {
+			STATUS(XSDRINC);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			int num = READ_BYTE();
+			while (1) {
+				SHIFT_DATA(buf_tdi_data, buf_tdo_data, buf_tdo_mask, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+						state_xenddr ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE,
+						state_runtest, state_retries);
+				if (num-- <= 0)
+					break;
+				int carry = 1;
+				for (i=state_dr_size-1; i>=0; i--) {
+					if (getbit(buf_addr_mask, i) == 0)
+						continue;
+					if (getbit(buf_tdi_data, i)) {
+						setbit(buf_tdi_data, i, !carry);
+					} else {
+						setbit(buf_tdi_data, i, carry);
+						carry = 0;
+					}
+				}
+				unsigned char this_byte = 0;
+				for (i=0, j=0; i<state_data_size; i++) {
+					if (i%8 == 0)
+						this_byte = READ_BYTE();
+					while (getbit(buf_data_mask, j) == 0)
+						j++;
+					setbit(buf_tdi_data, j++, getbit(&this_byte, i%8));
+				}
+			}
+			break;
+		  }
+		case XSDRB: {
+			STATUS(XSDRB);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, (void*)0, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT, LIBXSVF_TAP_DRSHIFT, 0, 0);
+			break;
+		  }
+		case XSDRC: {
+			STATUS(XSDRC);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, (void*)0, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT, LIBXSVF_TAP_DRSHIFT, 0, 0);
+			break;
+		  }
+		case XSDRE: {
+			STATUS(XSDRE);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, (void*)0, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+					state_xenddr ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE, 0, 0);
+			break;
+		  }
+		case XSDRTDOB: {
+			STATUS(XSDRTDOB);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			READ_BITS(buf_tdo_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT, LIBXSVF_TAP_DRSHIFT, 0, 0);
+			break;
+		  }
+		case XSDRTDOC: {
+			STATUS(XSDRTDOC);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			READ_BITS(buf_tdo_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT, LIBXSVF_TAP_DRSHIFT, 0, 0);
+			break;
+		  }
+		case XSDRTDOE: {
+			STATUS(XSDRTDOE);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			READ_BITS(buf_tdo_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+					state_xenddr ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE, 0, 0);
+			break;
+		  }
+		case XSTATE: {
+			STATUS(XSTATE);
+			if (state_runtest && last_cmd == XRUNTEST) {
+				TAP(LIBXSVF_TAP_IDLE);
+				LIBXSVF_HOST_UDELAY(state_runtest, 0, state_runtest);
+			}
+			unsigned char state = READ_BYTE();
+			TAP(xilinx_tap(state));
+			break;
+		  }
+		case XENDIR: {
+			STATUS(XENDIR);
+			state_xendir = READ_BYTE();
+			break;
+		  }
+		case XENDDR: {
+			STATUS(XENDDR);
+			state_xenddr = READ_BYTE();
+			break;
+		  }
+		case XSIR2: {
+			STATUS(XSIR2);
+			int length = READ_BYTE();
+			length = length << 8 | READ_BYTE();
+			unsigned char buf[bits2bytes(length)];
+			READ_BITS(buf, length);
+			SHIFT_DATA(buf, (void*)0, (void*)0, length, LIBXSVF_TAP_IRSHIFT,
+					state_xendir ? LIBXSVF_TAP_IRPAUSE : LIBXSVF_TAP_IDLE,
+					state_runtest, state_retries);
+			break;
+		  }
+		case XCOMMENT: {
+			STATUS(XCOMMENT);
+			unsigned char this_byte;
+			do {
+				this_byte = READ_BYTE();
+			} while (this_byte);
+			break;
+		  }
+		case XWAIT:
+		case XWAITSTATE: {
+			STATUS(XWAIT);
+			unsigned char state1 = READ_BYTE();
+			unsigned char state2 = READ_BYTE();
+			long usecs = READ_LONG();
+			TAP(xilinx_tap(state1));
+			LIBXSVF_HOST_UDELAY(usecs, 0, 0);
+			TAP(xilinx_tap(state2));
+			if (cmd==XWAITSTATE) {
+				READ_LONG();   /* XWAITSTATE has count, time arguments */
+			}
+			break;
+		  }
+		case XTRST: {
+			STATUS(XTRST);
+			READ_BYTE();  /* enum: ON, OFF, Z, ABSENT */
+			break;
+		}
+		default:
+			LIBXSVF_HOST_REPORT_ERROR("Unknown XSVF command.");
+			goto error;
+		}
+	}
+
+error:
+	rc = -1;
+
+got_complete_command:
+	if (LIBXSVF_HOST_SYNC() != 0 && rc >= 0 ) {
+		LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+		rc = -1;
+	}
+
+	LIBXSVF_HOST_REALLOC(buf_tdi_data, 0, LIBXSVF_MEM_XSVF_TDI_DATA);
+	LIBXSVF_HOST_REALLOC(buf_tdo_data, 0, LIBXSVF_MEM_XSVF_TDO_DATA);
+	LIBXSVF_HOST_REALLOC(buf_tdo_mask, 0, LIBXSVF_MEM_XSVF_TDO_MASK);
+	LIBXSVF_HOST_REALLOC(buf_addr_mask, 0, LIBXSVF_MEM_XSVF_ADDR_MASK);
+	LIBXSVF_HOST_REALLOC(buf_data_mask, 0, LIBXSVF_MEM_XSVF_DATA_MASK);
+
+	return rc;
+}
+

+ 454 - 0
sw/esp32s2/src/libxsvf/xsvftool-esp.c

@@ -0,0 +1,454 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <Arduino.h>
+
+#include "libxsvf.h"
+
+#define PIN_TDI 33
+#define PIN_TDO 32
+#define PIN_TCK 27
+#define PIN_TMS 26
+
+static uint8_t TDI = PIN_TDI;
+static uint8_t TDO = PIN_TDO;
+static uint8_t TCK = PIN_TCK;
+static uint8_t TMS = PIN_TMS;
+
+/** BEGIN: Low-Level I/O Implementation **/
+
+static void io_setup(void)
+{
+  pinMode(TCK, OUTPUT);
+  pinMode(TMS, OUTPUT);
+  pinMode(TDI, OUTPUT);
+  pinMode(TDO, INPUT);
+}
+
+static void io_shutdown(void)
+{
+  pinMode(TCK, INPUT);
+  pinMode(TMS, INPUT);
+  pinMode(TDO, INPUT);
+  pinMode(TDI, INPUT);
+}
+
+static inline void io_tms(int val)
+{
+  digitalWrite(TMS, val);
+}
+
+static inline void io_tdi(int val)
+{
+  digitalWrite(TDI, val);
+}
+
+static inline void io_tck(int val)
+{
+  digitalWrite(TCK, val);
+}
+
+static inline void io_sck(int val)
+{
+}
+
+static inline void io_trst(int val)
+{
+}
+
+static inline int io_tdo()
+{
+  return digitalRead(TDO);
+}
+
+/** END: Low-Level I/O Implementation **/
+
+struct udata_s {
+	int (*file_getbyte)();
+	int line;
+	int verbose;
+	int clockcount;
+	int bitcount_tdi;
+	int bitcount_tdo;
+	int retval_i;
+	int retval[256];
+	char report[256];
+	uint32_t idcode;
+};
+
+static int h_setup(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 2) {
+		printf("[SETUP]\n");
+	}
+	io_setup();
+	return 0;
+}
+
+static int h_shutdown(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 2) {
+		printf("[SHUTDOWN]\n");
+	}
+	io_shutdown();
+	return 0;
+}
+
+static void h_udelay(struct libxsvf_host *h, long usecs, int tms, long num_tck)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 3) {
+		printf("[DELAY:%ld, TMS:%d, NUM_TCK:%ld]\n", usecs, tms, num_tck);
+	}
+	if (num_tck > 0) {
+		struct timeval tv1, tv2;
+		gettimeofday(&tv1, NULL);
+		io_tms(tms);
+		while (num_tck > 0) {
+			io_tck(0);
+			io_tck(1);
+			num_tck--;
+		}
+		gettimeofday(&tv2, NULL);
+		if (tv2.tv_sec > tv1.tv_sec) {
+			usecs -= (1000000 - tv1.tv_usec) + (tv2.tv_sec - tv1.tv_sec - 1) * 1000000;
+			tv1.tv_usec = 0;
+		}
+		usecs -= tv2.tv_usec - tv1.tv_usec;
+		if (u->verbose >= 3) {
+			printf("[DELAY_AFTER_TCK:%ld]\n", usecs > 0 ? usecs : 0);
+		}
+	}
+	if (usecs > 0) {
+		delayMicroseconds(usecs);
+	}
+}
+
+static int h_getbyte(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	int retval = u->file_getbyte();
+	if(retval == '\n')
+	  u->line++;
+	return retval; // returns same as fgetc()
+}
+
+static int h_pulse_tck(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync)
+{
+	struct udata_s *u = h->user_data;
+
+	io_tms(tms);
+
+	if (tdi >= 0) {
+		u->bitcount_tdi++;
+		io_tdi(tdi);
+	}
+
+	io_tck(0);
+	io_tck(1);
+
+	int line_tdo = io_tdo();
+	int rc = line_tdo >= 0 ? line_tdo : 0;
+
+	if (rmask == 1 && u->retval_i < 256)
+		u->retval[u->retval_i++] = line_tdo;
+
+	if (tdo >= 0 && line_tdo >= 0) {
+		u->bitcount_tdo++;
+		if (tdo != line_tdo)
+			rc = -1;
+	}
+
+	if (u->verbose >= 4) {
+		printf("[TMS:%d, TDI:%d, TDO_ARG:%d, TDO_LINE:%d, RMASK:%d, RC:%d]\n", tms, tdi, tdo, line_tdo, rmask, rc);
+	}
+
+	u->clockcount++;
+	return rc;
+}
+
+static void h_pulse_sck(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 4) {
+		printf("[SCK]\n");
+	}
+	io_sck(0);
+	io_sck(1);
+}
+
+static void h_set_trst(struct libxsvf_host *h, int v)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 4) {
+		printf("[TRST:%d]\n", v);
+	}
+	io_trst(v);
+}
+
+static int h_set_frequency(struct libxsvf_host *h, int v)
+{
+	//printf("WARNING: Setting JTAG clock frequency to %d ignored!\n", v);
+	return 0;
+}
+
+static void h_report_tapstate(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 3) {
+		printf("[%s]\n", libxsvf_state2str(h->tap_state));
+	}
+}
+
+static void h_report_device(struct libxsvf_host *h, unsigned long idcode)
+{
+	struct udata_s *u = h->user_data;
+	u->idcode = idcode;
+	// printf("idcode=0x%08lx, revision=0x%01lx, part=0x%04lx, manufactor=0x%03lx\n", idcode,
+	// 		(idcode >> 28) & 0xf, (idcode >> 12) & 0xffff, (idcode >> 1) & 0x7ff);
+}
+
+static void h_report_status(struct libxsvf_host *h, const char *message)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 2) {
+		printf("[STATUS] %s\n", message);
+	}
+}
+
+static void h_report_error(struct libxsvf_host *h, const char *file, int line, const char *message)
+{
+	struct udata_s *u = h->user_data;
+	// snprintf(u->report, 256, "[%s:%d] %s", file, line, message);
+	snprintf(u->report, 256, "line %d: %s", u->line, message);
+	puts(u->report);
+	// printf("[%s:%d] %s\n", file, line, message);
+}
+
+static int realloc_maxsize[LIBXSVF_MEM_NUM];
+
+static void *h_realloc(struct libxsvf_host *h, void *ptr, int size, enum libxsvf_mem which)
+{
+	struct udata_s *u = h->user_data;
+	if (size > realloc_maxsize[which])
+		realloc_maxsize[which] = size;
+	if (u->verbose >= 3) {
+		printf("[REALLOC:%s:%d]\n", libxsvf_mem2str(which), size);
+	}
+	return realloc(ptr, size);
+}
+
+static struct udata_s u;
+
+static struct libxsvf_host h = {
+	.udelay = h_udelay,
+	.setup = h_setup,
+	.shutdown = h_shutdown,
+	.getbyte = h_getbyte,
+	.pulse_tck = h_pulse_tck,
+	.pulse_sck = h_pulse_sck,
+	.set_trst = h_set_trst,
+	.set_frequency = h_set_frequency,
+	.report_tapstate = h_report_tapstate,
+	.report_device = h_report_device,
+	.report_status = h_report_status,
+	.report_error = h_report_error,
+	.realloc = h_realloc,
+	.user_data = &u
+};
+
+
+#if 0
+int main(int argc, char **argv)
+{
+	int rc = 0;
+	int gotaction = 0;
+	int hex_mode = 0;
+	const char *realloc_name = NULL;
+	int opt, i, j;
+
+	progname = argc >= 1 ? argv[0] : "xvsftool";
+	while ((opt = getopt(argc, argv, "r:vLBx:s:c")) != -1)
+	{
+		switch (opt)
+		{
+		case 'r':
+			realloc_name = optarg;
+			break;
+		case 'v':
+			copyleft();
+			u.verbose++;
+			break;
+		case 'x':
+		case 's':
+			gotaction = 1;
+			if (u.verbose)
+				fprintf(stderr, "Playing %s file `%s'.\n", opt == 's' ? "SVF" : "XSVF", optarg);
+			if (!strcmp(optarg, "-"))
+				u.f = stdin;
+			else
+				u.f = fopen(optarg, "rb");
+			if (u.f == NULL) {
+				fprintf(stderr, "Can't open %s file `%s': %s\n", opt == 's' ? "SVF" : "XSVF", optarg, strerror(errno));
+				rc = 1;
+				break;
+			}
+			if (libxsvf_play(&h, opt == 's' ? LIBXSVF_MODE_SVF : LIBXSVF_MODE_XSVF) < 0) {
+				fprintf(stderr, "Error while playing %s file `%s'.\n", opt == 's' ? "SVF" : "XSVF", optarg);
+				rc = 1;
+			}
+			if (strcmp(optarg, "-"))
+				fclose(u.f);
+			break;
+		case 'c':
+			gotaction = 1;
+			if (libxsvf_play(&h, LIBXSVF_MODE_SCAN) < 0) {
+				fprintf(stderr, "Error while scanning JTAG chain.\n");
+				rc = 1;
+			}
+			break;
+		case 'L':
+			hex_mode = 1;
+			break;
+		case 'B':
+			hex_mode = 2;
+			break;
+		default:
+			help();
+			break;
+		}
+	}
+
+	if (!gotaction)
+		help();
+
+	if (u.verbose) {
+		fprintf(stderr, "Total number of clock cycles: %d\n", u.clockcount);
+		fprintf(stderr, "Number of significant TDI bits: %d\n", u.bitcount_tdi);
+		fprintf(stderr, "Number of significant TDO bits: %d\n", u.bitcount_tdo);
+		if (rc == 0) {
+			fprintf(stderr, "Finished without errors.\n");
+		} else {
+			fprintf(stderr, "Finished with errors!\n");
+		}
+	}
+
+	if (u.retval_i) {
+		if (hex_mode) {
+			printf("0x");
+			for (i=0; i < u.retval_i; i+=4) {
+				int val = 0;
+				for (j=i; j<i+4; j++)
+					val = val << 1 | u.retval[hex_mode > 1 ? j : u.retval_i - j - 1];
+				printf("%x", val);
+			}
+		} else {
+			printf("%d rmask bits:", u.retval_i);
+			for (i=0; i < u.retval_i; i++)
+				printf(" %d", u.retval[i]);
+		}
+		printf("\n");
+	}
+
+	if (realloc_name) {
+		int num = 0;
+		for (i = 0; i < LIBXSVF_MEM_NUM; i++) {
+			if (realloc_maxsize[i] > 0)
+				num = i+1;
+		}
+		printf("void *%s(void *h, void *ptr, int size, int which) {\n", realloc_name);
+		for (i = 0; i < num; i++) {
+			if (realloc_maxsize[i] > 0)
+				printf("\tstatic unsigned char buf_%s[%d];\n", libxsvf_mem2str(i), realloc_maxsize[i]);
+		}
+		printf("\tstatic unsigned char *buflist[%d] = {", num);
+		for (i = 0; i < num; i++) {
+			if (realloc_maxsize[i] > 0)
+				printf("%sbuf_%s", i ? ", " : " ", libxsvf_mem2str(i));
+			else
+				printf("%s(void*)0", i ? ", " : " ");
+		}
+		printf(" };\n\tstatic int sizelist[%d] = {", num);
+		for (i = 0; i < num; i++) {
+			if (realloc_maxsize[i] > 0)
+				printf("%ssizeof(buf_%s)", i ? ", " : " ", libxsvf_mem2str(i));
+			else
+				printf("%s0", i ? ", " : " ");
+		}
+		printf(" };\n");
+		printf("\treturn which < %d && size <= sizelist[which] ? buflist[which] : (void*)0;\n", num);
+		printf("};\n");
+	}
+
+	return rc;
+}
+#endif
+
+int xsvftool_esp_scan(void)
+{
+  u.idcode = 0; // clear previous scan result
+  return libxsvf_play(&h, LIBXSVF_MODE_SCAN);
+}
+
+// return scan result (idcode)
+uint32_t xsvftool_esp_id(void)
+{
+  return u.idcode;
+}
+
+int xsvftool_esp_program(int (*file_getbyte)(), int x)
+{
+  u.file_getbyte = file_getbyte;
+  if(u.file_getbyte)
+    return libxsvf_play(&h, x ? LIBXSVF_MODE_XSVF : LIBXSVF_MODE_SVF);
+  return -1; // NULL file_getbyte pointer supplied
+}
+
+int xsvftool_esp_svf_packet(int (*packet_getbyte)(), int index, int final, char *report)
+{
+  u.verbose = 0;
+  u.file_getbyte = packet_getbyte;
+  if(u.file_getbyte)
+  {
+    if(index == 0)
+      u.line = 1;
+    int retval = libxsvf_svf_packet(&h, index, final);
+    strcpy(report, u.report);
+    return retval;
+  }
+  return -1; // NULL file_getbyte pointer supplied
+}
+
+void xsvftool_esp_set_pins(uint8_t tdi, uint8_t tdo, uint8_t tck, uint8_t tms)
+{
+	TDI = tdi;
+	TDO = tdo;
+	TCK = tck;
+	TMS = tms;
+}

+ 544 - 0
sw/esp32s2/src/libxsvf/xsvftool-gpio.c

@@ -0,0 +1,544 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+
+/** BEGIN: Low-Level I/O Implementation **/
+
+#ifdef XSVFTOOL_RLMS_VLINE
+
+// Simple example with MPC8349E GPIO pins
+// (RIEGL LMS V-Line motherboard)
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+#define IO_PORT_ADDR 0xE0000C00
+
+struct io_layout {
+	unsigned long tdi:1;
+	unsigned long tdo:1;
+	unsigned long tms:1;
+	unsigned long tck:1;
+	unsigned long reserved:28;
+};
+
+static volatile struct io_layout *io_direction;
+static volatile struct io_layout *io_opendrain;
+static volatile struct io_layout *io_data;
+
+static void io_setup(void)
+{
+	/* open /dev/mem device file */
+	int fd = open("/dev/mem", O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "Can't open /dev/mem: %s\n", strerror(errno));
+		exit(1);
+	}
+
+	/* calculate offsets to page and within page */
+	unsigned long psize = getpagesize();
+	unsigned long off_inpage = IO_PORT_ADDR % psize;
+	unsigned long off_topage = IO_PORT_ADDR - off_inpage;
+	unsigned long mapsize = off_inpage + sizeof(struct io_layout) * 3;
+
+	/* map it into logical memory */
+	void *io_addr_map = mmap(0, mapsize, PROT_WRITE, MAP_SHARED, fd, off_topage);
+	if (io_addr_map == MAP_FAILED) {
+		fprintf(stderr, "Can't map physical memory: %s\n", strerror(errno));
+		exit(1);
+	}
+
+	/* calculate register addresses */
+	io_direction = io_addr_map + off_inpage;
+	io_opendrain = io_addr_map + off_inpage + 4;
+	io_data = io_addr_map + off_inpage + 8;
+
+	/* set direction reg */
+	io_direction->tms = 1;
+	io_direction->tck = 1;
+	io_direction->tdo = 0;
+	io_direction->tdi = 1;
+
+	/* set open drain reg */
+	io_opendrain->tms = 0;
+	io_opendrain->tck = 0;
+	io_opendrain->tdo = 0;
+	io_opendrain->tdi = 0;
+
+#ifdef HAVE_TRST
+	/* for boards with TRST, must be driven high */
+	io_data->trst = 1;
+	io_direction->trst = 1;
+	io_opendrain->trst = 0;
+#endif
+}
+
+static void io_shutdown(void)
+{
+	/* set all to z-state */
+	io_direction->tms = 0;
+	io_direction->tck = 0;
+	io_direction->tdo = 0;
+	io_direction->tdi = 0;
+
+#ifdef HAVE_TRST
+	/* for boards with TRST, assuming there is a pull-down resistor */
+	io_direction->trst = 0;
+#endif
+}
+
+static void io_tms(int val)
+{
+	io_data->tms = val;
+}
+
+static void io_tdi(int val)
+{
+	io_data->tdi = val;
+}
+
+static void io_tck(int val)
+{
+	io_data->tck = val;
+	// usleep(1);
+}
+
+static void io_sck(int val)
+{
+	/* not available */
+}
+
+static void io_trst(int val)
+{
+	/* not available */
+}
+
+static int io_tdo()
+{
+	return io_data->tdo ? 1 : 0;
+}
+
+#else
+
+static void io_setup(void)
+{
+}
+
+static void io_shutdown(void)
+{
+}
+
+static void io_tms(int val)
+{
+}
+
+static void io_tdi(int val)
+{
+}
+
+static void io_tck(int val)
+{
+}
+
+static void io_sck(int val)
+{
+}
+
+static void io_trst(int val)
+{
+}
+
+static int io_tdo()
+{
+	return -1;
+}
+
+#endif
+
+/** END: Low-Level I/O Implementation **/
+
+
+struct udata_s {
+	FILE *f;
+	int verbose;
+	int clockcount;
+	int bitcount_tdi;
+	int bitcount_tdo;
+	int retval_i;
+	int retval[256];
+};
+
+static int h_setup(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 2) {
+		fprintf(stderr, "[SETUP]\n");
+		fflush(stderr);
+	}
+	io_setup();
+	return 0;
+}
+
+static int h_shutdown(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 2) {
+		fprintf(stderr, "[SHUTDOWN]\n");
+		fflush(stderr);
+	}
+	io_shutdown();
+	return 0;
+}
+
+static void h_udelay(struct libxsvf_host *h, long usecs, int tms, long num_tck)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 3) {
+		fprintf(stderr, "[DELAY:%ld, TMS:%d, NUM_TCK:%ld]\n", usecs, tms, num_tck);
+		fflush(stderr);
+	}
+	if (num_tck > 0) {
+		struct timeval tv1, tv2;
+		gettimeofday(&tv1, NULL);
+		io_tms(tms);
+		while (num_tck > 0) {
+			io_tck(0);
+			io_tck(1);
+			num_tck--;
+		}
+		gettimeofday(&tv2, NULL);
+		if (tv2.tv_sec > tv1.tv_sec) {
+			usecs -= (1000000 - tv1.tv_usec) + (tv2.tv_sec - tv1.tv_sec - 1) * 1000000;
+			tv1.tv_usec = 0;
+		}
+		usecs -= tv2.tv_usec - tv1.tv_usec;
+		if (u->verbose >= 3) {
+			fprintf(stderr, "[DELAY_AFTER_TCK:%ld]\n", usecs > 0 ? usecs : 0);
+			fflush(stderr);
+		}
+	}
+	if (usecs > 0) {
+		usleep(usecs);
+	}
+}
+
+static int h_getbyte(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	return fgetc(u->f);
+}
+
+static int h_pulse_tck(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync)
+{
+	struct udata_s *u = h->user_data;
+
+	io_tms(tms);
+
+	if (tdi >= 0) {
+		u->bitcount_tdi++;
+		io_tdi(tdi);
+	}
+
+	io_tck(0);
+	io_tck(1);
+
+	int line_tdo = io_tdo();
+	int rc = line_tdo >= 0 ? line_tdo : 0;
+
+	if (rmask == 1 && u->retval_i < 256)
+		u->retval[u->retval_i++] = line_tdo;
+
+	if (tdo >= 0 && line_tdo >= 0) {
+		u->bitcount_tdo++;
+		if (tdo != line_tdo)
+			rc = -1;
+	}
+
+	if (u->verbose >= 4) {
+		fprintf(stderr, "[TMS:%d, TDI:%d, TDO_ARG:%d, TDO_LINE:%d, RMASK:%d, RC:%d]\n", tms, tdi, tdo, line_tdo, rmask, rc);
+	}
+
+	u->clockcount++;
+	return rc;
+}
+
+static void h_pulse_sck(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 4) {
+		fprintf(stderr, "[SCK]\n");
+	}
+	io_sck(0);
+	io_sck(1);
+}
+
+static void h_set_trst(struct libxsvf_host *h, int v)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 4) {
+		fprintf(stderr, "[TRST:%d]\n", v);
+	}
+	io_trst(v);
+}
+
+static int h_set_frequency(struct libxsvf_host *h, int v)
+{
+	fprintf(stderr, "WARNING: Setting JTAG clock frequency to %d ignored!\n", v);
+	return 0;
+}
+
+static void h_report_tapstate(struct libxsvf_host *h)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 3) {
+		fprintf(stderr, "[%s]\n", libxsvf_state2str(h->tap_state));
+	}
+}
+
+static void h_report_device(struct libxsvf_host *h, unsigned long idcode)
+{
+	// struct udata_s *u = h->user_data;
+	// printf("idcode=0x%08lx, revision=0x%01lx, part=0x%04lx, manufactor=0x%03lx\n", idcode,
+	// 		(idcode >> 28) & 0xf, (idcode >> 12) & 0xffff, (idcode >> 1) & 0x7ff);
+}
+
+static void h_report_status(struct libxsvf_host *h, const char *message)
+{
+	struct udata_s *u = h->user_data;
+	if (u->verbose >= 2) {
+		fprintf(stderr, "[STATUS] %s\n", message);
+	}
+}
+
+static void h_report_error(struct libxsvf_host *h, const char *file, int line, const char *message)
+{
+	fprintf(stderr, "[%s:%d] %s\n", file, line, message);
+}
+
+static int realloc_maxsize[LIBXSVF_MEM_NUM];
+
+static void *h_realloc(struct libxsvf_host *h, void *ptr, int size, enum libxsvf_mem which)
+{
+	struct udata_s *u = h->user_data;
+	if (size > realloc_maxsize[which])
+		realloc_maxsize[which] = size;
+	if (u->verbose >= 3) {
+		fprintf(stderr, "[REALLOC:%s:%d]\n", libxsvf_mem2str(which), size);
+	}
+	return realloc(ptr, size);
+}
+
+static struct udata_s u;
+
+static struct libxsvf_host h = {
+	.udelay = h_udelay,
+	.setup = h_setup,
+	.shutdown = h_shutdown,
+	.getbyte = h_getbyte,
+	.pulse_tck = h_pulse_tck,
+	.pulse_sck = h_pulse_sck,
+	.set_trst = h_set_trst,
+	.set_frequency = h_set_frequency,
+	.report_tapstate = h_report_tapstate,
+	.report_device = h_report_device,
+	.report_status = h_report_status,
+	.report_error = h_report_error,
+	.realloc = h_realloc,
+	.user_data = &u
+};
+
+const char *progname;
+
+static void copyleft()
+{
+	static int already_printed = 0;
+	if (already_printed)
+		return;
+	fprintf(stderr, "xsvftool-gpio, part of Lib(X)SVF (http://www.clifford.at/libxsvf/).\n");
+	fprintf(stderr, "Copyright (C) 2009  RIEGL Research ForschungsGmbH\n");
+	fprintf(stderr, "Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>\n");
+	fprintf(stderr, "Lib(X)SVF is free software licensed under the ISC license.\n");
+	already_printed = 1;
+}
+
+static void help()
+{
+	copyleft();
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Usage: %s [ -r funcname ] [ -v ... ] [ -L | -B ] { -s svf-file | -x xsvf-file | -c } ...\n", progname);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "   -r funcname\n");
+	fprintf(stderr, "          Dump C-code for pseudo-allocator based on example files\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "   -v, -vv, -vvv, -vvvv\n");
+	fprintf(stderr, "          Verbose, more verbose and even more verbose\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "   -L, -B\n");
+	fprintf(stderr, "          Print RMASK bits as hex value (little or big endian)\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "   -s svf-file\n");
+	fprintf(stderr, "          Play the specified SVF file\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "   -x xsvf-file\n");
+	fprintf(stderr, "          Play the specified XSVF file\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "   -c\n");
+	fprintf(stderr, "          List devices in JTAG chain\n");
+	fprintf(stderr, "\n");
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	int rc = 0;
+	int gotaction = 0;
+	int hex_mode = 0;
+	const char *realloc_name = NULL;
+	int opt, i, j;
+
+	progname = argc >= 1 ? argv[0] : "xvsftool";
+	while ((opt = getopt(argc, argv, "r:vLBx:s:c")) != -1)
+	{
+		switch (opt)
+		{
+		case 'r':
+			realloc_name = optarg;
+			break;
+		case 'v':
+			copyleft();
+			u.verbose++;
+			break;
+		case 'x':
+		case 's':
+			gotaction = 1;
+			if (u.verbose)
+				fprintf(stderr, "Playing %s file `%s'.\n", opt == 's' ? "SVF" : "XSVF", optarg);
+			if (!strcmp(optarg, "-"))
+				u.f = stdin;
+			else
+				u.f = fopen(optarg, "rb");
+			if (u.f == NULL) {
+				fprintf(stderr, "Can't open %s file `%s': %s\n", opt == 's' ? "SVF" : "XSVF", optarg, strerror(errno));
+				rc = 1;
+				break;
+			}
+			if (libxsvf_play(&h, opt == 's' ? LIBXSVF_MODE_SVF : LIBXSVF_MODE_XSVF) < 0) {
+				fprintf(stderr, "Error while playing %s file `%s'.\n", opt == 's' ? "SVF" : "XSVF", optarg);
+				rc = 1;
+			}
+			if (strcmp(optarg, "-"))
+				fclose(u.f);
+			break;
+		case 'c':
+			gotaction = 1;
+			if (libxsvf_play(&h, LIBXSVF_MODE_SCAN) < 0) {
+				fprintf(stderr, "Error while scanning JTAG chain.\n");
+				rc = 1;
+			}
+			break;
+		case 'L':
+			hex_mode = 1;
+			break;
+		case 'B':
+			hex_mode = 2;
+			break;
+		default:
+			help();
+			break;
+		}
+	}
+
+	if (!gotaction)
+		help();
+
+	if (u.verbose) {
+		fprintf(stderr, "Total number of clock cycles: %d\n", u.clockcount);
+		fprintf(stderr, "Number of significant TDI bits: %d\n", u.bitcount_tdi);
+		fprintf(stderr, "Number of significant TDO bits: %d\n", u.bitcount_tdo);
+		if (rc == 0) {
+			fprintf(stderr, "Finished without errors.\n");
+		} else {
+			fprintf(stderr, "Finished with errors!\n");
+		}
+	}
+
+	if (u.retval_i) {
+		if (hex_mode) {
+			printf("0x");
+			for (i=0; i < u.retval_i; i+=4) {
+				int val = 0;
+				for (j=i; j<i+4; j++)
+					val = val << 1 | u.retval[hex_mode > 1 ? j : u.retval_i - j - 1];
+				printf("%x", val);
+			}
+		} else {
+			printf("%d rmask bits:", u.retval_i);
+			for (i=0; i < u.retval_i; i++)
+				printf(" %d", u.retval[i]);
+		}
+		printf("\n");
+	}
+
+	if (realloc_name) {
+		int num = 0;
+		for (i = 0; i < LIBXSVF_MEM_NUM; i++) {
+			if (realloc_maxsize[i] > 0)
+				num = i+1;
+		}
+		printf("void *%s(void *h, void *ptr, int size, int which) {\n", realloc_name);
+		for (i = 0; i < num; i++) {
+			if (realloc_maxsize[i] > 0)
+				printf("\tstatic unsigned char buf_%s[%d];\n", libxsvf_mem2str(i), realloc_maxsize[i]);
+		}
+		printf("\tstatic unsigned char *buflist[%d] = {", num);
+		for (i = 0; i < num; i++) {
+			if (realloc_maxsize[i] > 0)
+				printf("%sbuf_%s", i ? ", " : " ", libxsvf_mem2str(i));
+			else
+				printf("%s(void*)0", i ? ", " : " ");
+		}
+		printf(" };\n\tstatic int sizelist[%d] = {", num);
+		for (i = 0; i < num; i++) {
+			if (realloc_maxsize[i] > 0)
+				printf("%ssizeof(buf_%s)", i ? ", " : " ", libxsvf_mem2str(i));
+			else
+				printf("%s0", i ? ", " : " ");
+		}
+		printf(" };\n");
+		printf("\treturn which < %d && size <= sizelist[which] ? buflist[which] : (void*)0;\n", num);
+		printf("};\n");
+	}
+
+	return rc;
+}
+
+
+

+ 78 - 0
sw/esp32s2/src/main.cpp

@@ -0,0 +1,78 @@
+#include "Arduino.h"
+#include "port.h"
+#include "esp32-hal-log.h"
+#include "WiFi.h"
+#include "max80_func.h"
+#include "max80_callback.h"
+#include "max80_gpio.h"
+#include "max80_littlefs.h"
+#include "max80_wifi.h"
+#include "max80_web.h"
+#include "esp_log.h"
+#include <AsyncElegantOTA.h>
+#include <DNSServer.h>
+#include <WiFiMulti.h>
+#include <ESP32DMASPISlave.h>
+#include <USB.h>
+#include "programmer.h"
+#define LOG_LOCAL_LEVEL ESP_LOG_INFO
+
+
+#define ARDUINO_USB_CDC_ON_BOOT
+
+static const char TAG[] = __FILE__;
+
+
+max80::std_max80 max80_std;
+max80::callback max80_callback;
+max80::wifi max80_wifi;
+max80::gpio max80_gpio;
+max80::littlefs max80_littlefs;
+max80::web max80_web;
+max80config max80_configuration;
+AsyncElegantOtaClass AsyncElegantOTA;
+DNSServer dns;
+AsyncWebServer webserver(80);   
+TaskHandle_t Config_Task = NULL;
+ESP32DMASPI::Slave fpgaspislave;
+String macaddr;
+WiFiMulti wifiMulti;
+TaskHandle_t ledtask = NULL;
+portMUX_TYPE sync_isr = portMUX_INITIALIZER_UNLOCKED;
+TimerHandle_t max80_ntp_timer = NULL;
+max80Config max80_config;  
+USBCDC USBSerial; 
+void setup()
+{
+  Serial.begin(MAX80_SERIAL_BAUD_RATE);
+  max80_littlefs.setup();
+  max80_littlefs.loadconfig();
+  max80_gpio.init();
+  USBSerial.begin(921600);
+  USBSerial.setRxBufferSize(4096);
+  USB.begin();
+  USBSerial.setRxBufferSize(4096);
+  Serial.setRxBufferSize(4096);
+  digitalWrite(MAX80_LED1,LOW);
+  digitalWrite(MAX80_LED2,LOW);
+  digitalWrite(MAX80_LED3,LOW);
+  xTaskCreate(&blink_led,"ledcontrol",1024,NULL,5,&ledtask);
+  uint32_t led;
+  led = 500 << 8 | 1; 
+  ProgrammerInit();
+  if (xTaskNotify(ledtask, led, eSetValueWithoutOverwrite) ==	pdTRUE) {
+	}
+  max80_wifi.setup();
+}
+
+void loop()
+{
+
+    delay(1000);
+    if (!WiFi.isConnected()){
+      max80_wifi.reconnect();
+    }
+    ProgrammerLoop();
+    //ESP_LOGD(TAG,"HEJ");
+    USBSerial.println("Hej");
+}

+ 13 - 0
sw/esp32s2/src/max80_callback.cpp

@@ -0,0 +1,13 @@
+#include <Arduino.h>
+#include <WiFi.h>
+#include "max80_callback.h"
+
+
+static const char TAG[] = __FILE__;
+
+namespace max80 {
+
+    void callback::wificonnect(){
+		ESP_LOGI(TAG,"WiFi Connected start web");
+	}
+}

+ 159 - 0
sw/esp32s2/src/max80_func.cpp

@@ -0,0 +1,159 @@
+#include "config.h"
+
+#include "max80_func.h"
+
+
+namespace max80 {
+
+	void std_max80::getmac_b(String mac, uint8_t *mac_b)
+	{
+		char firstchar;
+		char secchar;
+		uint8_t position = 0;
+
+		for (int i = 0; i < 6; i++)
+		{
+			firstchar = mac.charAt(position);
+			position++;
+			secchar = mac.charAt(position);
+			mac_b[i] = hex_to_ascii(firstchar, secchar);
+			position++;
+		}
+	}
+
+	int std_max80::hex_to_int(char c) {
+		int first = c / 16 - 3;
+		int second = c % 16;
+		int result = first * 10 + second;
+		if (result > 9) result--;
+		return result;
+	}
+
+	int std_max80::hex_to_ascii(char c, char d) {
+		int high = hex_to_int(c) * 16;
+		int low = hex_to_int(d);
+		return high + low;
+	}
+	void std_max80::reverse8(uint8_t *buf, int size, uint8_t *rev)
+	{
+		for (int i = 0; i < size; i++) {
+			rev[size - 1 - i] = buf[i];
+		}
+	}
+	void std_max80::hex2bin(uint8_t *out, const char *in, size_t *size)
+	{
+		size_t sz = 0;
+		while (*in) {
+			while (*in == ' ') in++;  // skip spaces
+			while (*in == ',') in++;  // skip spaces
+			while (*in == ' ') in++;  // skip spaces
+			if (!*in) break;
+			uint8_t c = *in>='a' ? *in-'a'+10 : *in>='A' ? *in-'A'+10 : *in-'0';
+			in++;
+			c <<= 4;
+			if (!*in) break;
+			c |= *in>='a' ? *in-'a'+10 : *in>='A' ? *in-'A'+10 : *in-'0';
+			in++;
+			*out++ = c;
+			sz++;
+		}
+		//size = sz;
+	}
+	String std_max80::hexaddr(uint8_t *buf) {
+
+		String output = "";
+		for (uint8_t i = 0; i < 8; i++)
+		{
+			output += "0x";
+			if (buf[i] < 0x10) output += "0";
+			output += String(buf[i], HEX);
+			if (i < 7) output += (", ");
+		}
+		return output;
+	}
+
+	void std_max80::serial_empty(HardwareSerial *port) {
+		HardwareSerial * _port;
+		_port = port;
+		_port->read();
+		_port->read();
+		_port->read();
+		_port->read();
+		_port->flush();
+	}
+
+	String std_max80::hex_to_string(uint8_t *buf, int size, String separator) {
+
+		String output = "";
+		for (int i = 0; i < size; i++) {
+			if (buf[i] < 0x10)
+				output += "0";
+			output += String(buf[i], HEX);
+			if (separator != "") {
+				output += separator;
+			}
+		}
+		return output;
+	}
+	void std_max80::processutf8(uint8_t b,utf8decodedata *data)
+	{
+		if (data->state == 0x01) {
+			// Expect a continuation byte
+			if ((b & 0xc0) == 0x80) {
+			// Continuation byte
+			data->acc <<= 6;
+			data->acc |= (uint16_t) (b & 0x3f);
+			data->left--;
+			if (data->left == 0) {
+				// Last continuation byte in sequence
+				data->data=data->acc;
+				data->state = 0x00;
+				return;
+			}
+			} else {
+			// Invalid byte; retry from here in INIT state
+			data->error = 0x01;
+			data->state = 0x00;
+			}
+		}
+		if (data->state == 0x00) {
+			// Expect an initial byte
+			if ((b & 0x80) == 0x00) {
+			// Ascii byte
+			data->data=((uint16_t) b);
+			} else if ((b & 0xe0) == 0xc0) {
+			// Start of two-byte sequence
+			data->state = 0x01;
+			data->acc = (uint16_t) (b & 0x1f);
+			data->left = 1;
+			} else if ((b & 0xf0) == 0xe0) {
+			// Start of three-byte sequence
+			data->state = 0x01;
+			data->acc = (uint16_t) (b & 0x0f);
+			data->left = 2;
+			} else {
+			// Too large sequence for this implementation or invalid byte
+			data->error;
+			}
+		}
+	}
+
+	uint8_t std_max80::processutf8String(const uint8_t* s,uint8_t len,uint8_t* d)
+	{
+		uint8_t p;
+		utf8decodedata data;
+		data.state=0x00;
+		uint8_t i=0;
+		for (p = 0; p < len; p++) {
+			processutf8(s[p],&data);
+			if (data.data !=0x00){
+				d[i]=data.data;
+				data.data = 0x00;
+				i++;
+			}
+		}
+		d[i]=0x00;
+		return (i-1);
+	}
+
+}

+ 99 - 0
sw/esp32s2/src/max80_gpio.cpp

@@ -0,0 +1,99 @@
+#include <Arduino.h>
+#include "max80_gpio.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+static const char TAG[] = __FILE__;
+extern portMUX_TYPE sync_isr;
+extern TaskHandle_t ledtask;
+volatile bool led1=false;
+volatile bool led2=false;
+volatile bool led3=false;
+void IRAM_ATTR ISR_MOSI() {
+    led1=!led1;
+    digitalWrite(MAX80_LED1,led1);
+}
+void IRAM_ATTR ISR_CS() {
+    led2=!led2;
+    digitalWrite(MAX80_LED2,led2);
+
+}
+void IRAM_ATTR ISR_CLK() {
+    led3=!led3;
+    digitalWrite(MAX80_LED3,led3);
+}
+void blink_led(void *pvParameter)
+{
+    uint32_t receivedValue=0;
+    uint16_t led1_freq=0;
+    uint16_t led2_freq=0;
+    uint16_t led3_freq=0;
+    long led1Millis=0;
+    long led2Millis=0;
+    long led3Millis=0;
+    while(1) {
+        unsigned long currentMillis = millis();
+        if (currentMillis - led1Millis >=led1_freq && !(led1_freq==0 || led1_freq==1)) {
+            led1=!led1;
+            digitalWrite(MAX80_LED1,led1);  
+            led1Millis = currentMillis;
+        }else if(led1_freq==0 && led1){
+            digitalWrite(MAX80_LED1,false);
+        }else if(led1_freq==1 && !led1){
+            digitalWrite(MAX80_LED1,true);
+        }
+        if (currentMillis - led1Millis >=led2_freq && !(led2_freq==0 || led2_freq==1)) {
+            led2=!led2;
+            digitalWrite(MAX80_LED2,led2);  
+            led2Millis = currentMillis;
+        }else if(led2_freq==0 && led2){
+            digitalWrite(MAX80_LED2,false);
+        }else if(led2_freq==1 && !led2){
+            digitalWrite(MAX80_LED2,true);
+        }
+        if (xTaskNotifyWait(0, ULONG_MAX, &receivedValue, 10) == pdPASS) {     
+
+            if (receivedValue & 1){
+                led1_freq=(uint16_t)receivedValue >> 8;
+            }
+            if (receivedValue & 2){
+                led2_freq=(uint16_t)receivedValue >> 8;
+            }
+            if (receivedValue & 3){
+                led3_freq=(uint16_t)receivedValue >> 8;
+            }
+        }
+        vTaskDelay(10/portTICK_PERIOD_MS);
+    }
+}
+namespace max80 {
+    
+	void gpio::init(){
+        pinMode(MAX80_LED1,OUTPUT);
+        pinMode(MAX80_LED2,OUTPUT);
+        pinMode(MAX80_LED3,OUTPUT);
+        // Just for test
+        pinMode(MAX80_FPGA_MOSI,INPUT);
+        pinMode(MAX80_FPGA_CS,INPUT);
+        pinMode(MAX80_FPGA_CLK,INPUT);
+        pinMode(MAX80_FPGA_MISO,INPUT);
+        pinMode(MAX80_RTC_32KHZ ,INPUT);
+        pinMode(MAX80_FPGA_I2C_SDA,INPUT);
+        pinMode(MAX80_FPGA_I2C_SCL,INPUT);
+        /*   
+        pinMode(MAX80_FPGA_TDI, OUTPUT);
+        pinMode(MAX80_FPGA_TDO, INPUT_PULLUP);
+        pinMode(MAX80_FPGA_TCK, OUTPUT);
+        pinMode(MAX80_FPGA_TMS, OUTPUT);
+        */
+        pinMode(MAX80_FPGA_TDI, INPUT);
+        pinMode(MAX80_FPGA_TDO, INPUT);
+        pinMode(MAX80_FPGA_TCK, INPUT);
+        pinMode(MAX80_FPGA_TMS, INPUT);
+        //attachInterrupt(MAX80_FPGA_MISO, ISR_MOSI, CHANGE);
+        attachInterrupt(MAX80_FPGA_CS, ISR_CS, CHANGE);
+        attachInterrupt(MAX80_FPGA_CLK, ISR_CLK, CHANGE);
+
+    }
+
+    
+}

+ 0 - 0
sw/esp32s2/src/max80_jtag.cpp


+ 131 - 0
sw/esp32s2/src/max80_littlefs.cpp

@@ -0,0 +1,131 @@
+#include <Arduino.h>
+#include "max80_littlefs.h"
+#include "max80_config.h"
+#include <ArduinoJson.h> 
+#include <FS.h> 
+#include "LITTLEFS.h"
+extern max80Config max80_config;
+extern max80::littlefs max80_littlefs;
+static const char TAG[] = __FILE__;
+#define FORMAT_LITTLEFS_IF_FAILED true
+int calcChecksum(uint8_t* address, uint16_t sizeToCalc)
+{
+  uint16_t checkSum = 0;
+  
+  for (uint16_t index = 0; index < sizeToCalc; index++)
+  {
+    checkSum += * ( ( (byte*) address ) + index);
+  }
+
+  return checkSum;
+}
+void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
+    Serial.printf("Listing directory: %s\r\n", dirname);
+
+    File root = fs.open(dirname);
+    if(!root){
+        ESP_LOGE(TAG,"- failed to open directory");
+        return;
+    }
+    if(!root.isDirectory()){
+        ESP_LOGE(TAG," - not a directory");
+        return;
+    }
+
+    File file = root.openNextFile();
+    while(file){
+        if(file.isDirectory()){
+            ESP_LOGD(TAG,"  DIR : ");
+
+#ifdef CONFIG_LITTLEFS_FOR_IDF_3_2
+            Serial.println(file.name());
+#else
+            Serial.print(file.name());
+            time_t t= file.getLastWrite();
+            struct tm * tmstruct = localtime(&t);
+            ESP_LOGD(TAG,"  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec);
+#endif
+
+            if(levels){
+                listDir(fs, file.name(), levels -1);
+            }
+        } else {
+            ESP_LOGD(TAG,"  FILE: %s ",file.name());
+            ESP_LOGD(TAG,"  SIZE: %d",file.size());
+
+#ifdef CONFIG_LITTLEFS_FOR_IDF_3_2
+            Serial.println(file.size());
+#else
+            time_t t= file.getLastWrite();
+            struct tm * tmstruct = localtime(&t);
+            ESP_LOGD(TAG,"  LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec);
+#endif
+        }
+        file = root.openNextFile();
+    }
+}
+namespace max80 
+{
+
+    void littlefs::setup(){
+        //clean FS, for testing
+        //SPIFFS.format();
+
+        delay(1000);
+        ESP_LOGD(TAG,"TAG");
+        if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
+            ESP_LOGD(TAG,"LITTLEFS Mount Failed");
+        }
+        listDir(LITTLEFS, "/", 0);
+
+        delay(1000);
+        
+    }
+    bool littlefs::loadconfig(){
+        File file = LITTLEFS.open(MAX80_CONFIG_FILNAME, "r");
+        if (file)
+        {
+            max80_config.checksum = calcChecksum( (uint8_t*) &max80_config, sizeof(max80_config) - sizeof(max80_config.checksum) );
+            
+            file.read((uint8_t*) &max80_config, sizeof(max80_config));
+
+            file.close();
+            if ((String)max80_config.WiFi_Creds->wifi_ssid ==""){
+                ESP_LOGD(TAG,"Write default to flash");
+                memcpy(&max80_config.WiFi_Creds->wifi_ssid, "MAX80",5);
+                memcpy(&max80_config.WiFi_Creds->wifi_pw, "max80!!!",8);
+                max80_littlefs.saveconfig();
+            }
+            ESP_LOGD(TAG,"OK");
+            return(true);
+        }
+        else
+        {
+            ESP_LOGD(TAG,"Write default to flash");
+            memcpy(&max80_config.WiFi_Creds->wifi_ssid, "MAX80",5);
+            memcpy(&max80_config.WiFi_Creds->wifi_pw, "max80!!!",8);
+            max80_littlefs.saveconfig();
+        }
+        return(false);
+    }
+    bool littlefs::saveconfig(){
+        File file = LITTLEFS.open(MAX80_CONFIG_FILNAME, "w",true);
+        if (file)
+        {
+            max80_config.checksum = calcChecksum( (uint8_t*) &max80_config, sizeof(max80_config) - sizeof(max80_config.checksum) );
+            
+            file.write((uint8_t*) &max80_config, sizeof(max80_config));
+
+            file.close();
+            ESP_LOGD(TAG,"OK");
+            return(true);
+        }
+        else
+        {
+            ESP_LOGD(TAG,"failed");
+        }
+        return(false);
+ 
+    }
+}
+

+ 0 - 0
sw/esp32s2/src/max80_ntp.cpp


+ 0 - 0
sw/esp32s2/src/max80_spislave.cpp


+ 0 - 0
sw/esp32s2/src/max80_usbserial.cpp


+ 62 - 0
sw/esp32s2/src/max80_web.cpp

@@ -0,0 +1,62 @@
+#include <Arduino.h>
+#include <WiFi.h>
+#include <AsyncTCP.h>
+#include <ESPAsyncWebServer.h>
+#include <AsyncElegantOTA.h>
+#include "max80_config.h"
+#include "max80_web.h"
+#include "max80_func.h"
+#include "max80_webcontent.h"
+#include "max80_gpio.h"
+
+
+static const char TAG[] = __FILE__;
+
+extern WiFiClass WiFi;
+extern AsyncWebServer webserver;
+extern max80::web max80_web;
+extern max80::std_max80 max80_std;
+extern TaskHandle_t OTA_Task ;
+
+extern AsyncElegantOtaClass AsyncElegantOTA;
+
+
+
+extern TaskHandle_t OTA_Task ;
+extern AsyncWebServer webserver;
+
+extern AsyncWebSocket ws("/ws");
+ static   void web_onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,void *arg, uint8_t *data, size_t len) {
+        switch (type) {
+            case WS_EVT_CONNECT:
+            ESP_LOGI(TAG,"WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
+            break;
+            case WS_EVT_DISCONNECT:
+            ESP_LOGI(TAG,"WebSocket client #%u disconnected\n", client->id());
+            break;
+            case WS_EVT_DATA:
+             ESP_LOGI(TAG,"Handle data");
+            break;
+            case WS_EVT_PONG:
+            case WS_EVT_ERROR:
+            break;
+        }
+ }
+namespace max80
+{
+    void OTAtask(void * parameter){
+        AsyncElegantOTA.loop();
+        ws.cleanupClients();
+    }
+    void web::initWebSocket() {
+        ws.onEvent(web_onEvent);
+        webserver.addHandler(&ws);
+    }
+    void web::init(){
+
+        initWebSocket();
+        webserver.begin();
+        AsyncElegantOTA.begin(&webserver);
+
+    }
+}

+ 100 - 0
sw/esp32s2/src/max80_wifi.cpp

@@ -0,0 +1,100 @@
+#include <Arduino.h>
+#include "max80_wifi.h"
+#include "max80_func.h"
+#include "WiFi.h"
+#include <esp_wifi.h>
+#include <WiFi.h>
+#include <WiFiClient.h>
+#include <WiFiMulti.h>
+#include <time.h>
+
+
+#include "max80_config.h"
+#include "max80_callback.h"
+#include "max80_web.h"
+#include "max80_gpio.h"
+#include "max80_littlefs.h"
+#include <esp_wifi.h>
+#include <DNSServer.h>
+#include <AsyncElegantOTA.h>
+#define USING_ESP32_S2 true
+//#include <ESPAsync_WiFiManager.h> 
+#include "ESPAsyncWiFiManager.h"
+
+static const char TAG[] = __FILE__;
+
+extern WiFiClass WiFi;
+extern TaskHandle_t WiFi_Task;
+extern max80::std_max80 max80_std;
+extern max80::web max80_web;
+extern max80::callback max80_callback;
+extern max80::gpio max80_gpio;
+extern AsyncWebServer webserver;
+extern DNSServer dns;
+extern AsyncElegantOtaClass AsyncElegantOTA;
+extern WiFiMulti wifiMulti;
+extern TaskHandle_t ledtask;
+extern max80Config max80_config; 
+extern max80::littlefs max80_littlefs;
+AsyncWiFiManager ESPAsync_wifiManager(&webserver,&dns );
+
+
+      
+
+#define MIN_AP_PASSWORD_SIZE    8
+
+const char* ntpServer = "pool.ntp.org";
+const long  gmtOffset_sec = 0;
+const int   daylightOffset_sec = 3600;
+
+namespace max80 {
+	void configModeCallback (AsyncWiFiManager *myESP_WiFiManager)
+	{
+		ESP_LOGD(TAG,"Entered WiFi config mode");
+		uint32_t led = 100 << 8 | 1; 
+		if (xTaskNotify(ledtask, led, eSetValueWithoutOverwrite) ==	pdTRUE) {
+		}
+	}
+	void saveconfigCallback ()
+	{
+		ESP_LOGD(TAG,"Saving config");
+		String tempSSID = ESPAsync_wifiManager.getConfiguredSTASSID();
+      	String tempPW   = ESPAsync_wifiManager.getConfiguredSTAPassword();
+		strcpy(max80_config.WiFi_Creds->wifi_ssid,tempSSID.c_str());
+		strcpy(max80_config.WiFi_Creds->wifi_pw,tempPW.c_str());
+		max80_littlefs.saveconfig();
+	}
+	//setSaveConfigCallback
+	void wifi::setup(){
+		ESP_LOGD(TAG,"Entered WiFi setup");
+		
+		
+		ESP_LOGD(TAG,"%s",max80_config.WiFi_Creds->wifi_ssid);
+		ESP_LOGD(TAG,"%s",max80_config.WiFi_Creds->wifi_pw);
+		ESPAsync_wifiManager.setAPStaticIPConfig(IPAddress(192,168,132,1), IPAddress(192,168,132,1), IPAddress(255,255,255,0));
+		ESPAsync_wifiManager.setAPCallback(configModeCallback);
+		ESPAsync_wifiManager.setSaveConfigCallback(saveconfigCallback);
+		WiFi.setHostname("MAX80");
+		ESPAsync_wifiManager.autoConnect(max80_config.WiFi_Creds->wifi_ssid,max80_config.WiFi_Creds->wifi_pw);
+		max80_web.init();
+		max80_web.connected=true;
+		configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
+		uint32_t led = 1 << 8 | 1; 
+		if (xTaskNotify(ledtask, led, eSetValueWithoutOverwrite) ==	pdTRUE) {
+		}
+		
+	}	
+	void wifi::reconnect(){
+		uint32_t led = 500 << 8 | 1; 
+		if (xTaskNotify(ledtask, led, eSetValueWithoutOverwrite) ==	pdTRUE) {
+		}
+		ESPAsync_wifiManager.autoConnect(max80_config.WiFi_Creds->wifi_ssid,max80_config.WiFi_Creds->wifi_pw);
+		led = 1 << 8 | 1; 
+		if (xTaskNotify(ledtask, led, eSetValueWithoutOverwrite) ==	pdTRUE) {
+		}
+	}
+	
+	
+
+}
+

+ 97 - 0
sw/esp32s2/src/program.cpp

@@ -0,0 +1,97 @@
+#include "program.h"
+#include <Arduino.h>
+#include <ESPAsyncWebServer.h>
+#include "netlog.h"
+#include "max80_gpio.h"
+
+uint8_t codeBuffer[BUFFER_SIZE];
+
+extern "C" {
+  int       xsvftool_esp_scan       (void);
+  uint32_t  xsvftool_esp_id         (void);
+  int       xsvftool_esp_program    (int (*file_getbyte)(), int x);
+  int       xsvftool_esp_svf_packet (int (*packet_getbyte)(), int index, int final, char *report);
+  void      xsvftool_esp_set_pins   (uint8_t tdi, uint8_t tdo, uint8_t tck, uint8_t tms);
+}
+
+uint8_t ledpin = 2;
+
+struct buffer_state {
+  int count;      // how many bytes in buffer
+  int ptr;        // current reading pointer
+  uint8_t blink;  // for the LED
+  uint8_t mode;
+} rd;
+
+int get_next_byte() {
+  if(rd.ptr >= rd.count) {
+    // refill the buffer and update content
+    rd.ptr = 0;
+
+    if (rd.mode == MODE_SERIAL) {
+      rd.count = fetch_next_block(codeBuffer, BUFFER_SIZE);
+    } else {
+      //rd.count = fetch_next_block_wifi(codeBuffer, BUFFER_SIZE);
+    }
+
+    if(rd.count <= 0 || rd.count > BUFFER_SIZE) {
+      return -1;
+    }
+
+    digitalWrite(ledpin, (rd.blink++) & 1);
+  }
+
+  return codeBuffer[rd.ptr++];
+}
+
+uint32_t jtag_chip_id() {
+  xsvftool_esp_scan();
+  return xsvftool_esp_id();
+}
+
+int jtag_program(int dataType, uint8_t mode) {
+  int retval = -1;
+  if (dataType != DATA_TYPE_SVF && dataType != DATA_TYPE_XSVF) {
+    Error("[JTAG] Invalid data type\r\n");
+    return retval;
+  }
+
+  uint32_t chipId = xsvftool_esp_id();
+
+  if (!chipId) {
+    Error("[JTAG] No devices found!\r\n");
+    return retval;
+  }
+
+  Info("[JTAG] Found device %08x\r\n", chipId);
+  Info("[JTAG] Waiting first block\r\n");
+
+  rd.ptr = 0;
+  rd.mode = mode;
+
+  if (mode == MODE_SERIAL) {
+    rd.count = fetch_next_block(codeBuffer, BUFFER_SIZE);
+  } else {
+    //rd.count = fetch_next_block_wifi(codeBuffer, BUFFER_SIZE);
+  }
+
+  if (rd.count <= 0) {
+    Error("[JTAG] No data available\r\n");
+    return retval;
+  }
+
+  Info("[JTAG] Programming...\r\n");
+
+  pinMode(MAX80_LED3, OUTPUT);
+  retval = xsvftool_esp_program(get_next_byte, dataType);
+  pinMode(MAX80_LED3, INPUT);
+
+  Info("[JTAG] Programming finished with status %d\r\n", retval);
+
+  return retval;
+}
+
+void set_pins(uint8_t tdi, uint8_t tdo, uint8_t tck, uint8_t tms, uint8_t led) {
+  xsvftool_esp_set_pins(tdi, tdo, tck, tms);
+  ledpin = led;
+}

+ 115 - 0
sw/esp32s2/src/programmer.cpp

@@ -0,0 +1,115 @@
+#include "Arduino.h"
+#include "programmer.h"
+#include "program.h"
+#include "max80_gpio.h"
+#include "USB.h"
+extern USBCDC USBSerial; 
+uint8_t cmd = 0;
+int32_t length = 0;
+
+int passthrough = 0;
+int lastled = 0;
+
+void ReadCmdLength(uint8_t *cmd, int32_t *length) {
+  while(Serial.available() < 5);
+
+  *cmd = Serial.read();
+  *length = 0;
+  uint8_t *dataLengthbytes = (uint8_t*)length;
+
+  // for (int i = 0; i < 4; i++) {
+  for (int i = 3; i >= 0; i--) {
+    dataLengthbytes[i] = Serial.read();
+  }
+}
+
+String ReadParam(int32_t length) {
+  String param = "";
+  for (int32_t i = 0; i < length; i++) {
+    param += (char)Serial.read();
+  }
+  return param;
+}
+
+void ProgrammerInit() {
+    set_pins(MAX80_FPGA_TDI , MAX80_FPGA_TDO , MAX80_FPGA_TCK , MAX80_FPGA_TMS, MAX80_LED3);
+
+    pinMode(MAX80_FPGA_TDI, OUTPUT);
+    pinMode(MAX80_FPGA_TDO, INPUT_PULLUP);
+    pinMode(MAX80_FPGA_TCK, OUTPUT);
+    pinMode(MAX80_FPGA_TMS, OUTPUT);
+}
+
+String tmpParam;
+
+void ProgrammerLoop() {
+  if (passthrough) {
+    // Serial passthrough
+    if(Serial.available()) {
+      USBSerial.write(Serial.read());
+      digitalWrite( MAX80_LED3, (lastled++)&1);
+    }
+
+    if(USBSerial.available()) {
+      Serial.write(USBSerial.read());
+      digitalWrite( MAX80_LED3, (lastled++)&1);
+    }
+
+    return;
+  }
+
+  // Programming Mode
+  if (Serial.available() >= 5) {
+    ReadCmdLength(&cmd, &length);
+    switch (cmd) {
+      case CMD_START_SVF:
+        jtag_program(DATA_TYPE_SVF, MODE_SERIAL);
+        break;
+      case CMD_START_XSVF:
+        jtag_program(DATA_TYPE_XSVF, MODE_SERIAL);
+        break;
+      case CMD_QUERY:
+        Serial.print("[QUERY] Chip ID: ");
+        printf("%08x\n", jtag_chip_id());
+        break;
+      case CMD_STOP:
+        Serial.println("[RDY]");
+        break;
+      case CMD_PASSTHROUGH:
+        Serial.println("[PASS] Switching to Serial Passthrough");
+        Serial.print("[PASS] Serial2 baudrate set to ");
+        Serial.println(length);
+        passthrough = 1;
+        pinMode( MAX80_LED3, OUTPUT);
+        digitalWrite( MAX80_LED3, LOW);
+        Serial.println("[PASS] READY");
+        return;
+      default:
+        Serial.println("[CTRL] ERROR");
+    }
+  }
+}
+
+int fetch_next_block(uint8_t *buffer, int length) {
+
+  Serial.print("[CTRL] R");
+  Serial.println(length);
+
+  uint8_t cmd;
+  int32_t dataLength;
+  ReadCmdLength(&cmd, &dataLength);
+  if (dataLength <= 0) {
+    return -1;
+  }
+
+  int readBytes = 0;
+
+  while (readBytes < dataLength) {
+    while(!Serial.available());
+    buffer[readBytes] = Serial.read();
+    readBytes++;
+  }
+  Serial.println("[CTRL] A");
+
+  return readBytes;
+}

+ 12 - 0
sw/esp32s2/variants/max80/partitions.csv

@@ -0,0 +1,12 @@
+# ESP-IDF Partition Table
+# Name,   Type, SubType, Offset,  Size, Flags
+# bootloader.bin,,          0x1000, 32K
+# partition table,          0x8000, 4K
+# Name,     Type,   SubType,    Offset,     Size,     Flags
+
+nvs,      data, nvs,      0x9000,  20K,
+otadata,  data, ota,      0xe000,  8K,
+ota_0,    0,    ota_0,   0x10000,  1408K,
+ota_1,    0,    ota_1,  0x170000,  1408K,
+uf2,      app,  factory,0x2d0000,  256K,
+spiffs,     data, 	spiffs,    0x310000,  960K,

+ 67 - 0
sw/esp32s2/variants/max80/pins_arduino.h

@@ -0,0 +1,67 @@
+#ifndef Pins_Arduino_h
+#define Pins_Arduino_h
+
+#include <stdint.h>
+
+
+#define USB_VID 0x239A
+#define USB_PID 0x80DF
+#define USB_MANUFACTURER "Peter & Per"
+#define USB_PRODUCT "MAX80 ESP32-S2"
+#define USB_SERIAL ""
+
+
+#define EXTERNAL_NUM_INTERRUPTS 46
+#define NUM_DIGITAL_PINS        48
+#define NUM_ANALOG_INPUTS       20
+
+#define analogInputToDigitalPin(p)  (((p)<20)?(esp32_adc2gpio[(p)]):-1)
+#define digitalPinToInterrupt(p)    (((p)<48)?(p):-1)
+#define digitalPinHasPWM(p)         (p < 46)
+
+
+
+static const uint8_t TX = 37;
+static const uint8_t RX = 38;
+
+
+static const uint8_t A0 = 17;
+static const uint8_t A1 = 18;
+static const uint8_t A2 = 1;
+static const uint8_t A3 = 2;
+static const uint8_t A4 = 3;
+static const uint8_t A5 = 4;
+static const uint8_t A6 = 5;
+static const uint8_t A7 = 6;
+static const uint8_t A8 = 7;
+static const uint8_t A9 = 8;
+static const uint8_t A10 = 9;
+static const uint8_t A11 = 10;
+static const uint8_t A12 = 11;
+static const uint8_t A13 = 12;
+static const uint8_t A14 = 13;
+static const uint8_t A15 = 14;
+static const uint8_t A16 = 15;
+static const uint8_t A17 = 16;
+static const uint8_t A18 = 19;
+static const uint8_t A19 = 20;
+
+
+static const uint8_t T1 = 1;
+static const uint8_t T2 = 2;
+static const uint8_t T3 = 3;
+static const uint8_t T4 = 4;
+static const uint8_t T5 = 5;
+static const uint8_t T6 = 6;
+static const uint8_t T7 = 7;
+static const uint8_t T8 = 8;
+static const uint8_t T9 = 9;
+static const uint8_t T10 = 10;
+static const uint8_t T11 = 11;
+static const uint8_t T12 = 12;
+static const uint8_t T13 = 13;
+static const uint8_t T14 = 14;
+
+static const uint8_t DAC1 = 17;
+static const uint8_t DAC2 = 18;
+#endif /* Pins_Arduino_h */

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels