Library to use Arduino USB host shield on mbed

Dependents:   USBHOST_PS5

ArduinoのUSB Host Shield 2.0をmbedで使えるようにしたライブラリです。
大体のコードがArduinoからそのまま移植可能です。

Arduino UNOやMega用のホストシールド以外にもミニサイズのホストシールドでも使用可能です https://os.mbed.com/media/uploads/kotakku/dffgfddswa.png

シールドについて

3.3VのI/O用にシールドの改造が必要になりますがネット上に記事がたくさんあるのでそちらを参考にしてください

接続例

https://os.mbed.com/media/uploads/kotakku/esgsvfvhjrekldkcjxvb.png

使い方

Arduinoのコードと違うのはUSBのインスタンスの宣言部分のみです。
ピンを自分で指定できるようにしたので使いやすくなりました。

仕様

  • Arduinoのmillis関数、micros関数の移植のために内部でTimerクラスを使用しています。

main.cpp

#include "mbed.h"
#include <PS3BT.h>
#include <usbhub.h>

Serial pc(USBTX, USBRX, 115200);

//Nucleo f303k8用
USB Usb(A6, A5, A4, A3, A2); // mosi, miso, sclk, ssel, intr
BTD Btd(&Usb);
PS3BT PS3(&Btd);

int main()
{
    bool printAngle = false;

    if (Usb.Init() == -1)
    {
        pc.printf("\r\nOSC did not start");
        while (1); // Halt
    }
    pc.printf("\r\nPS3 USB Library Started");

    while (1)
    {
        Usb.Task();
        
        if (PS3.PS3Connected || PS3.PS3NavigationConnected) {
            if (PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117)
            {
                pc.printf("\r\nLeftHatX: %d", PS3.getAnalogHat(LeftHatX));
                pc.printf("\tLeftHatY: %d", PS3.getAnalogHat(LeftHatY));
                if (PS3.PS3Connected)
                { // The Navigation controller only have one joystick
                    pc.printf("\tRightHatX: %d", PS3.getAnalogHat(RightHatX));
                    pc.printf("\tRightHatY: %d", PS3.getAnalogHat(RightHatY));
                }
            }
            // Analog button values can be read from almost all buttons
            if (PS3.getAnalogButton(L2) || PS3.getAnalogButton(R2))
            {
                pc.printf("\r\nL2: %d", PS3.getAnalogButton(L2));
                if (!PS3.PS3NavigationConnected)
                {
                    pc.printf("\tR2: %d", PS3.getAnalogButton(R2));
                }
            }
            if (PS3.getButtonClick(PS))
            {
                PS3.disconnect();
                pc.printf("\r\nPS");
            }
    
            if (PS3.getButtonClick(TRIANGLE))
                pc.printf("\r\nTriangle");
            if (PS3.getButtonClick(CIRCLE))
                pc.printf("\r\nCircle");
            if (PS3.getButtonClick(CROSS))
                pc.printf("\r\nCross");
            if (PS3.getButtonClick(SQUARE))
                pc.printf("\r\nSquare");
    
            if (PS3.getButtonClick(UP))
            {
                pc.printf("\r\nUp");
                PS3.setLedOff();
                PS3.setLedOn(CONTROLLER_LED4);
            }
            if (PS3.getButtonClick(RIGHT))
            {
                pc.printf("\r\nRight");
                PS3.setLedOff();
                PS3.setLedOn(CONTROLLER_LED1);
            }
            if (PS3.getButtonClick(DOWN))
            {
                pc.printf("\r\nDown");
                PS3.setLedOff();
                PS3.setLedOn(CONTROLLER_LED2);
            }
            if (PS3.getButtonClick(LEFT))
            {
                pc.printf("\r\nLeft");
                PS3.setLedOff();
                PS3.setLedOn(CONTROLLER_LED3);
            }
    
            if (PS3.getButtonClick(L1))
                pc.printf("\r\nL1");
            if (PS3.getButtonClick(L3))
                pc.printf("\r\nL3");
            if (PS3.getButtonClick(R1))
                pc.printf("\r\nR1");
            if (PS3.getButtonClick(R3))
                pc.printf("\r\nR3");
    
            if (PS3.getButtonClick(SELECT))
            {
                pc.printf("\r\nSelect - ");
                PS3.printStatusString();
            }
            if (PS3.getButtonClick(START))
            {
                pc.printf("\r\nStart");
                printAngle = !printAngle;
            }
            if (printAngle)
            {
                pc.printf("\r\nPitch: %.3lf", PS3.getAngle(Pitch));
                pc.printf("\tRoll: %.3lf", PS3.getAngle(Roll));
            }
        }
        else
        {
            pc.printf("not connect\n");
        }
    }
}

Files at this revision

API Documentation at this revision

Comitter:
kotakku
Date:
Sat Jan 18 15:06:35 2020 +0000
Child:
1:da31140f2a1c
Commit message:
1.0.0 first commit

Changed in this revision

Arduino_lib/Arduino.h Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/Print.cpp Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/Print.h Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/Printable.h Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/SerialClass.cpp Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/SerialClass.h Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/WString.cpp Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/WString.h Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/arStream.cpp Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/arStream.h Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/avr/dtostrf.c Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/avr/dtostrf.h Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/avr/pgmspace.h Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/itoa.c Show annotated file Show diff for this revision Revisions of this file
Arduino_lib/itoa.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/BTD.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/BTD.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/BTHID.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/BTHID.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS3BT.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS3BT.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS3Enums.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS3USB.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS3USB.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS4BT.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS4Parser.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS4Parser.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/PS4USB.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/PSBuzz.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/PSBuzz.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/SPP.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/SPP.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/UHS2_gpio.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/UHS2_gpio.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/Usb.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/Usb.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/UsbCore.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/Wii.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/Wii.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/XBOXOLD.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/XBOXOLD.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/XBOXONE.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/XBOXONE.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/XBOXRECV.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/XBOXRECV.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/XBOXUSB.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/XBOXUSB.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/address.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/adk.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/adk.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/avrpins.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/cdc_XR21B1411.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/cdc_XR21B1411.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/cdcacm.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/cdcacm.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/cdcftdi.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/cdcftdi.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/cdcprolific.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/cdcprolific.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/confdescparser.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/controllerEnums.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/hexdump.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/hidboot.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/hidboot.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/hidcomposite.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/hidcomposite.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/hidescriptorparser.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/hidescriptorparser.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/hiduniversal.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/hiduniversal.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/hidusagestr.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/hidusagetitlearrays.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/macros.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/masstorage.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/masstorage.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/max3421e.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/max_LCD.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/max_LCD.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/message.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/message.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/parsetools.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/parsetools.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/printhex.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/settings.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/sink_parser.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/usb_ch9.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/usbh_midi.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/usbh_midi.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/usbhid.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/usbhid.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/usbhost.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/usbhost.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/usbhub.cpp Show annotated file Show diff for this revision Revisions of this file
USB_Host/usbhub.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/version_helper.h Show annotated file Show diff for this revision Revisions of this file
USB_Host/xboxEnums.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/Arduino.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,27 @@
+#ifndef __arduino_h__
+#define __arduino_h__
+
+#include "mbed.h"
+#ifndef M_PI
+#define M_PI 3.159265358979
+#endif
+
+#include <math.h>
+#include "Print.h"
+#include "Stream.h"
+
+//#include "SerialClass.h" // Arduino style Serial class
+
+#define min(a,b) ((a)<(b)?(a):(b))
+#define max(a,b) ((a)>(b)?(a):(b))
+
+#define PI M_PI
+#define RAD_TO_DEG (180.0 / M_PI)
+
+#define delay(ms) wait_ms(ms)
+#define delayMicroseconds(us) wait_us(us)
+#define millis() (USB::read_ms())
+#define micros() (USB::read_us())
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/Print.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,256 @@
+/*
+ Print.cpp - Base class that provides print() and println()
+ Copyright (c) 2008 David A. Mellis.  All right reserved.
+ 
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ 
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+ 
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ 
+ Modified 23 November 2006 by David A. Mellis
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "Arduino.h"
+
+#include "Print.h"
+
+// Public Methods //////////////////////////////////////////////////////////////
+
+/* default implementation: may be overridden */
+size_t Print::write(const uint8_t *buffer, size_t size)
+{
+  size_t n = 0;
+  while (size--) {
+    n += write(*buffer++);
+  }
+  return n;
+}
+
+size_t Print::print(const __FlashStringHelper *ifsh)
+{
+  return print(reinterpret_cast<const char *>(ifsh));
+}
+
+size_t Print::print(const String &s)
+{
+  return write(s.c_str(), s.length());
+}
+
+size_t Print::print(const char str[])
+{
+  return write(str);
+}
+
+size_t Print::print(char c)
+{
+  return write(c);
+}
+
+size_t Print::print(unsigned char b, int base)
+{
+  return print((unsigned long) b, base);
+}
+
+size_t Print::print(int n, int base)
+{
+  return print((long) n, base);
+}
+
+size_t Print::print(unsigned int n, int base)
+{
+  return print((unsigned long) n, base);
+}
+
+size_t Print::print(long n, int base)
+{
+  if (base == 0) {
+    return write(n);
+  } else if (base == 10) {
+    if (n < 0) {
+      int t = print('-');
+      n = -n;
+      return printNumber(n, 10) + t;
+    }
+    return printNumber(n, 10);
+  } else {
+    return printNumber(n, base);
+  }
+}
+
+size_t Print::print(unsigned long n, int base)
+{
+  if (base == 0) return write(n);
+  else return printNumber(n, base);
+}
+
+size_t Print::print(double n, int digits)
+{
+  return printFloat(n, digits);
+}
+
+size_t Print::println(const __FlashStringHelper *ifsh)
+{
+  size_t n = print(ifsh);
+  n += println();
+  return n;
+}
+
+size_t Print::print(const Printable& x)
+{
+  return x.printTo(*this);
+}
+
+size_t Print::println(void)
+{
+  return write("\r\n");
+}
+
+size_t Print::println(const String &s)
+{
+  size_t n = print(s);
+  n += println();
+  return n;
+}
+
+size_t Print::println(const char c[])
+{
+  size_t n = print(c);
+  n += println();
+  return n;
+}
+
+size_t Print::println(char c)
+{
+  size_t n = print(c);
+  n += println();
+  return n;
+}
+
+size_t Print::println(unsigned char b, int base)
+{
+  size_t n = print(b, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(int num, int base)
+{
+  size_t n = print(num, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(unsigned int num, int base)
+{
+  size_t n = print(num, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(long num, int base)
+{
+  size_t n = print(num, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(unsigned long num, int base)
+{
+  size_t n = print(num, base);
+  n += println();
+  return n;
+}
+
+size_t Print::println(double num, int digits)
+{
+  size_t n = print(num, digits);
+  n += println();
+  return n;
+}
+
+size_t Print::println(const Printable& x)
+{
+  size_t n = print(x);
+  n += println();
+  return n;
+}
+
+// Private Methods /////////////////////////////////////////////////////////////
+
+size_t Print::printNumber(unsigned long n, uint8_t base) {
+  char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
+  char *str = &buf[sizeof(buf) - 1];
+
+  *str = '\0';
+
+  // prevent crash if called with base == 1
+  if (base < 2) base = 10;
+
+  do {
+    unsigned long m = n;
+    n /= base;
+    char c = m - base * n;
+    *--str = c < 10 ? c + '0' : c + 'A' - 10;
+  } while(n);
+
+  return write(str);
+}
+
+size_t Print::printFloat(double number, uint8_t digits) 
+{ 
+  size_t n = 0;
+  
+  if (isnan(number)) return print("nan");
+  if (isinf(number)) return print("inf");
+  if (number > 4294967040.0) return print ("ovf");  // constant determined empirically
+  if (number <-4294967040.0) return print ("ovf");  // constant determined empirically
+  
+  // Handle negative numbers
+  if (number < 0.0)
+  {
+     n += print('-');
+     number = -number;
+  }
+
+  // Round correctly so that print(1.999, 2) prints as "2.00"
+  double rounding = 0.5;
+  for (uint8_t i=0; i<digits; ++i)
+    rounding /= 10.0;
+  
+  number += rounding;
+
+  // Extract the integer part of the number and print it
+  unsigned long int_part = (unsigned long)number;
+  double remainder = number - (double)int_part;
+  n += print(int_part);
+
+  // Print the decimal point, but only if there are digits beyond
+  if (digits > 0) {
+    n += print("."); 
+  }
+
+  // Extract digits from the remainder one at a time
+  while (digits-- > 0)
+  {
+    remainder *= 10.0;
+    int toPrint = int(remainder);
+    n += print(toPrint);
+    remainder -= toPrint; 
+  } 
+  
+  return n;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/Print.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,85 @@
+/*
+  Print.h - Base class that provides print() and println()
+  Copyright (c) 2008 David A. Mellis.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef Print_h
+#define Print_h
+
+#include <inttypes.h>
+#include <stdio.h> // for size_t
+
+#include "WString.h"
+#include "Printable.h"
+
+#define DEC 10
+#define HEX 16
+#define OCT 8
+#define BIN 2
+
+class Print
+{
+  private:
+    int write_error;
+    size_t printNumber(unsigned long, uint8_t);
+    size_t printFloat(double, uint8_t);
+  protected:
+    void setWriteError(int err = 1) { write_error = err; }
+  public:
+    Print() : write_error(0) {}
+  
+    int getWriteError() { return write_error; }
+    void clearWriteError() { setWriteError(0); }
+  
+    virtual size_t write(uint8_t) = 0;
+    size_t write(const char *str) {
+      if (str == NULL) return 0;
+      return write((const uint8_t *)str, strlen(str));
+    }
+    virtual size_t write(const uint8_t *buffer, size_t size);
+    size_t write(const char *buffer, size_t size) {
+      return write((const uint8_t *)buffer, size);
+    }
+    
+    size_t print(const __FlashStringHelper *);
+    size_t print(const String &);
+    size_t print(const char[]);
+    size_t print(char);
+    size_t print(unsigned char, int = DEC);
+    size_t print(int, int = DEC);
+    size_t print(unsigned int, int = DEC);
+    size_t print(long, int = DEC);
+    size_t print(unsigned long, int = DEC);
+    size_t print(double, int = 2);
+    size_t print(const Printable&);
+
+    size_t println(const __FlashStringHelper *);
+    size_t println(const String &s);
+    size_t println(const char[]);
+    size_t println(char);
+    size_t println(unsigned char, int = DEC);
+    size_t println(int, int = DEC);
+    size_t println(unsigned int, int = DEC);
+    size_t println(long, int = DEC);
+    size_t println(unsigned long, int = DEC);
+    size_t println(double, int = 2);
+    size_t println(const Printable&);
+    size_t println(void);
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/Printable.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,41 @@
+/*
+  Printable.h - Interface class that allows printing of complex types
+  Copyright (c) 2011 Adrian McEwen.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef Printable_h
+#define Printable_h
+
+#include <stdlib.h>
+
+class Print;
+
+/** The Printable class provides a way for new classes to allow themselves to be printed.
+    By deriving from Printable and implementing the printTo method, it will then be possible
+    for users to print out instances of this class by passing them into the usual
+    Print::print and Print::println methods.
+*/
+
+class Printable
+{
+  public:
+    virtual size_t printTo(Print& p) const = 0;
+};
+
+#endif
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/SerialClass.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,30 @@
+/*
+#include "SerialClass.h"
+
+size_t SerialClass::write(uint8_t data) {
+	return write(&data, 1);
+}
+
+size_t SerialClass::write(const uint8_t *buffer, size_t size) {
+	uint8_t *pBuffer = (uint8_t*)buffer;
+	HAL_UART_Transmit(pUART_Handle, pBuffer, size, HAL_MAX_DELAY);
+	return size;
+}
+
+int SerialClass::read() {
+	uint8_t data;
+	HAL_UART_Receive(pUART_Handle, &data, 1, HAL_MAX_DELAY);
+	return data;
+}
+
+int SerialClass::available() {
+	return -1;
+}
+
+int SerialClass::peek() {
+	return -1;
+}
+
+void SerialClass::flush() {
+}
+*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/SerialClass.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,30 @@
+#ifndef __serial_h__
+#define __serial_h__
+
+/*
+#include "arStream.h"
+#include "stm32f4xx_hal.h"
+
+class SerialClass : public arStream {
+public:
+	SerialClass(UART_HandleTypeDef *UART_Handle) : pUART_Handle(UART_Handle) {
+	};
+
+	size_t write(uint8_t data);
+	size_t write(const uint8_t *buffer, size_t size);
+	using Print::write; // Pull in write(const char *str) from Print
+	int read();
+
+	// These are NOT implemented!
+	int available();
+	int peek();
+	void flush();
+
+private:
+	UART_HandleTypeDef *pUART_Handle;
+};
+
+extern SerialClass Serial; // Create this constructor in your main.cpp
+*/
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/WString.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,748 @@
+/*
+  WString.cpp - String library for Wiring & Arduino
+  ...mostly rewritten by Paul Stoffregen...
+  Copyright (c) 2009-10 Hernando Barragan.  All rights reserved.
+  Copyright 2011, Paul Stoffregen, paul@pjrc.com
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "WString.h"
+#include "itoa.h"
+#include "avr/dtostrf.h"
+
+/*********************************************/
+/*  Constructors                             */
+/*********************************************/
+
+String::String(const char *cstr)
+{
+	init();
+	if (cstr) copy(cstr, strlen(cstr));
+}
+
+String::String(const String &value)
+{
+	init();
+	*this = value;
+}
+
+String::String(const __FlashStringHelper *pstr)
+{
+	init();
+	*this = pstr;
+}
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+String::String(String &&rval)
+{
+	init();
+	move(rval);
+}
+String::String(StringSumHelper &&rval)
+{
+	init();
+	move(rval);
+}
+#endif
+
+String::String(char c)
+{
+	init();
+	char buf[2];
+	buf[0] = c;
+	buf[1] = 0;
+	*this = buf;
+}
+
+String::String(unsigned char value, unsigned char base)
+{
+	init();
+	char buf[1 + 8 * sizeof(unsigned char)];
+	utoa(value, buf, base);
+	*this = buf;
+}
+
+String::String(int value, unsigned char base)
+{
+	init();
+	char buf[2 + 8 * sizeof(int)];
+	itoa(value, buf, base);
+	*this = buf;
+}
+
+String::String(unsigned int value, unsigned char base)
+{
+	init();
+	char buf[1 + 8 * sizeof(unsigned int)];
+	utoa(value, buf, base);
+	*this = buf;
+}
+
+String::String(long value, unsigned char base)
+{
+	init();
+	char buf[2 + 8 * sizeof(long)];
+	ltoa(value, buf, base);
+	*this = buf;
+}
+
+String::String(unsigned long value, unsigned char base)
+{
+	init();
+	char buf[1 + 8 * sizeof(unsigned long)];
+	ultoa(value, buf, base);
+	*this = buf;
+}
+
+String::String(float value, unsigned char decimalPlaces)
+{
+	init();
+	char buf[33];
+	*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
+}
+
+String::String(double value, unsigned char decimalPlaces)
+{
+	init();
+	char buf[33];
+	*this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf);
+}
+
+String::~String()
+{
+	free(buffer);
+}
+
+/*********************************************/
+/*  Memory Management                        */
+/*********************************************/
+
+inline void String::init(void)
+{
+	buffer = NULL;
+	capacity = 0;
+	len = 0;
+}
+
+void String::invalidate(void)
+{
+	if (buffer) free(buffer);
+	buffer = NULL;
+	capacity = len = 0;
+}
+
+unsigned char String::reserve(unsigned int size)
+{
+	if (buffer && capacity >= size) return 1;
+	if (changeBuffer(size)) {
+		if (len == 0) buffer[0] = 0;
+		return 1;
+	}
+	return 0;
+}
+
+unsigned char String::changeBuffer(unsigned int maxStrLen)
+{
+	char *newbuffer = (char *)realloc(buffer, maxStrLen + 1);
+	if (newbuffer) {
+		buffer = newbuffer;
+		capacity = maxStrLen;
+		return 1;
+	}
+	return 0;
+}
+
+/*********************************************/
+/*  Copy and Move                            */
+/*********************************************/
+
+String & String::copy(const char *cstr, unsigned int length)
+{
+	if (!reserve(length)) {
+		invalidate();
+		return *this;
+	}
+	len = length;
+	strcpy(buffer, cstr);
+	return *this;
+}
+
+String & String::copy(const __FlashStringHelper *pstr, unsigned int length)
+{
+	if (!reserve(length)) {
+		invalidate();
+		return *this;
+	}
+	len = length;
+	strcpy_P(buffer, (PGM_P)pstr);
+	return *this;
+}
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+void String::move(String &rhs)
+{
+	if (buffer) {
+		if (capacity >= rhs.len) {
+			strcpy(buffer, rhs.buffer);
+			len = rhs.len;
+			rhs.len = 0;
+			return;
+		} else {
+			free(buffer);
+		}
+	}
+	buffer = rhs.buffer;
+	capacity = rhs.capacity;
+	len = rhs.len;
+	rhs.buffer = NULL;
+	rhs.capacity = 0;
+	rhs.len = 0;
+}
+#endif
+
+String & String::operator = (const String &rhs)
+{
+	if (this == &rhs) return *this;
+	
+	if (rhs.buffer) copy(rhs.buffer, rhs.len);
+	else invalidate();
+	
+	return *this;
+}
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+String & String::operator = (String &&rval)
+{
+	if (this != &rval) move(rval);
+	return *this;
+}
+
+String & String::operator = (StringSumHelper &&rval)
+{
+	if (this != &rval) move(rval);
+	return *this;
+}
+#endif
+
+String & String::operator = (const char *cstr)
+{
+	if (cstr) copy(cstr, strlen(cstr));
+	else invalidate();
+	
+	return *this;
+}
+
+String & String::operator = (const __FlashStringHelper *pstr)
+{
+	if (pstr) copy(pstr, strlen_P((PGM_P)pstr));
+	else invalidate();
+
+	return *this;
+}
+
+/*********************************************/
+/*  concat                                   */
+/*********************************************/
+
+unsigned char String::concat(const String &s)
+{
+	return concat(s.buffer, s.len);
+}
+
+unsigned char String::concat(const char *cstr, unsigned int length)
+{
+	unsigned int newlen = len + length;
+	if (!cstr) return 0;
+	if (length == 0) return 1;
+	if (!reserve(newlen)) return 0;
+	strcpy(buffer + len, cstr);
+	len = newlen;
+	return 1;
+}
+
+unsigned char String::concat(const char *cstr)
+{
+	if (!cstr) return 0;
+	return concat(cstr, strlen(cstr));
+}
+
+unsigned char String::concat(char c)
+{
+	char buf[2];
+	buf[0] = c;
+	buf[1] = 0;
+	return concat(buf, 1);
+}
+
+unsigned char String::concat(unsigned char num)
+{
+	char buf[1 + 3 * sizeof(unsigned char)];
+	itoa(num, buf, 10);
+	return concat(buf, strlen(buf));
+}
+
+unsigned char String::concat(int num)
+{
+	char buf[2 + 3 * sizeof(int)];
+	itoa(num, buf, 10);
+	return concat(buf, strlen(buf));
+}
+
+unsigned char String::concat(unsigned int num)
+{
+	char buf[1 + 3 * sizeof(unsigned int)];
+	utoa(num, buf, 10);
+	return concat(buf, strlen(buf));
+}
+
+unsigned char String::concat(long num)
+{
+	char buf[2 + 3 * sizeof(long)];
+	ltoa(num, buf, 10);
+	return concat(buf, strlen(buf));
+}
+
+unsigned char String::concat(unsigned long num)
+{
+	char buf[1 + 3 * sizeof(unsigned long)];
+	ultoa(num, buf, 10);
+	return concat(buf, strlen(buf));
+}
+
+unsigned char String::concat(float num)
+{
+	char buf[20];
+	char* string = dtostrf(num, 4, 2, buf);
+	return concat(string, strlen(string));
+}
+
+unsigned char String::concat(double num)
+{
+	char buf[20];
+	char* string = dtostrf(num, 4, 2, buf);
+	return concat(string, strlen(string));
+}
+
+unsigned char String::concat(const __FlashStringHelper * str)
+{
+	if (!str) return 0;
+	int length = strlen_P((const char *) str);
+	if (length == 0) return 1;
+	unsigned int newlen = len + length;
+	if (!reserve(newlen)) return 0;
+	strcpy_P(buffer + len, (const char *) str);
+	len = newlen;
+	return 1;
+}
+
+/*********************************************/
+/*  Concatenate                              */
+/*********************************************/
+
+StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(rhs.buffer, rhs.len)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, char c)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(c)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(num)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, int num)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(num)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(num)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, long num)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(num)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(num)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, float num)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(num)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, double num)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(num)) a.invalidate();
+	return a;
+}
+
+StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs)
+{
+	StringSumHelper &a = const_cast<StringSumHelper&>(lhs);
+	if (!a.concat(rhs))	a.invalidate();
+	return a;
+}
+
+/*********************************************/
+/*  Comparison                               */
+/*********************************************/
+
+int String::compareTo(const String &s) const
+{
+	if (!buffer || !s.buffer) {
+		if (s.buffer && s.len > 0) return 0 - *(unsigned char *)s.buffer;
+		if (buffer && len > 0) return *(unsigned char *)buffer;
+		return 0;
+	}
+	return strcmp(buffer, s.buffer);
+}
+
+unsigned char String::equals(const String &s2) const
+{
+	return (len == s2.len && compareTo(s2) == 0);
+}
+
+unsigned char String::equals(const char *cstr) const
+{
+	if (len == 0) return (cstr == NULL || *cstr == 0);
+	if (cstr == NULL) return buffer[0] == 0;
+	return strcmp(buffer, cstr) == 0;
+}
+
+unsigned char String::operator<(const String &rhs) const
+{
+	return compareTo(rhs) < 0;
+}
+
+unsigned char String::operator>(const String &rhs) const
+{
+	return compareTo(rhs) > 0;
+}
+
+unsigned char String::operator<=(const String &rhs) const
+{
+	return compareTo(rhs) <= 0;
+}
+
+unsigned char String::operator>=(const String &rhs) const
+{
+	return compareTo(rhs) >= 0;
+}
+
+unsigned char String::equalsIgnoreCase( const String &s2 ) const
+{
+	if (this == &s2) return 1;
+	if (len != s2.len) return 0;
+	if (len == 0) return 1;
+	const char *p1 = buffer;
+	const char *p2 = s2.buffer;
+	while (*p1) {
+		if (tolower(*p1++) != tolower(*p2++)) return 0;
+	} 
+	return 1;
+}
+
+unsigned char String::startsWith( const String &s2 ) const
+{
+	if (len < s2.len) return 0;
+	return startsWith(s2, 0);
+}
+
+unsigned char String::startsWith( const String &s2, unsigned int offset ) const
+{
+	if (offset > len - s2.len || !buffer || !s2.buffer) return 0;
+	return strncmp( &buffer[offset], s2.buffer, s2.len ) == 0;
+}
+
+unsigned char String::endsWith( const String &s2 ) const
+{
+	if ( len < s2.len || !buffer || !s2.buffer) return 0;
+	return strcmp(&buffer[len - s2.len], s2.buffer) == 0;
+}
+
+/*********************************************/
+/*  Character Access                         */
+/*********************************************/
+
+char String::charAt(unsigned int loc) const
+{
+	return operator[](loc);
+}
+
+void String::setCharAt(unsigned int loc, char c) 
+{
+	if (loc < len) buffer[loc] = c;
+}
+
+char & String::operator[](unsigned int index)
+{
+	static char dummy_writable_char;
+	if (index >= len || !buffer) {
+		dummy_writable_char = 0;
+		return dummy_writable_char;
+	}
+	return buffer[index];
+}
+
+char String::operator[]( unsigned int index ) const
+{
+	if (index >= len || !buffer) return 0;
+	return buffer[index];
+}
+
+void String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const
+{
+	if (!bufsize || !buf) return;
+	if (index >= len) {
+		buf[0] = 0;
+		return;
+	}
+	unsigned int n = bufsize - 1;
+	if (n > len - index) n = len - index;
+	strncpy((char *)buf, buffer + index, n);
+	buf[n] = 0;
+}
+
+/*********************************************/
+/*  Search                                   */
+/*********************************************/
+
+int String::indexOf(char c) const
+{
+	return indexOf(c, 0);
+}
+
+int String::indexOf( char ch, unsigned int fromIndex ) const
+{
+	if (fromIndex >= len) return -1;
+	const char* temp = strchr(buffer + fromIndex, ch);
+	if (temp == NULL) return -1;
+	return temp - buffer;
+}
+
+int String::indexOf(const String &s2) const
+{
+	return indexOf(s2, 0);
+}
+
+int String::indexOf(const String &s2, unsigned int fromIndex) const
+{
+	if (fromIndex >= len) return -1;
+	const char *found = strstr(buffer + fromIndex, s2.buffer);
+	if (found == NULL) return -1;
+	return found - buffer;
+}
+
+int String::lastIndexOf( char theChar ) const
+{
+	return lastIndexOf(theChar, len - 1);
+}
+
+int String::lastIndexOf(char ch, unsigned int fromIndex) const
+{
+	if (fromIndex >= len) return -1;
+	char tempchar = buffer[fromIndex + 1];
+	buffer[fromIndex + 1] = '\0';
+	char* temp = strrchr( buffer, ch );
+	buffer[fromIndex + 1] = tempchar;
+	if (temp == NULL) return -1;
+	return temp - buffer;
+}
+
+int String::lastIndexOf(const String &s2) const
+{
+	return lastIndexOf(s2, len - s2.len);
+}
+
+int String::lastIndexOf(const String &s2, unsigned int fromIndex) const
+{
+  	if (s2.len == 0 || len == 0 || s2.len > len) return -1;
+	if (fromIndex >= len) fromIndex = len - 1;
+	int found = -1;
+	for (char *p = buffer; p <= buffer + fromIndex; p++) {
+		p = strstr(p, s2.buffer);
+		if (!p) break;
+		if ((unsigned int)(p - buffer) <= fromIndex) found = p - buffer;
+	}
+	return found;
+}
+
+String String::substring(unsigned int left, unsigned int right) const
+{
+	if (left > right) {
+		unsigned int temp = right;
+		right = left;
+		left = temp;
+	}
+	String out;
+	if (left >= len) return out;
+	if (right > len) right = len;
+	char temp = buffer[right];  // save the replaced character
+	buffer[right] = '\0';	
+	out = buffer + left;  // pointer arithmetic
+	buffer[right] = temp;  //restore character
+	return out;
+}
+
+/*********************************************/
+/*  Modification                             */
+/*********************************************/
+
+void String::replace(char find, char replace)
+{
+	if (!buffer) return;
+	for (char *p = buffer; *p; p++) {
+		if (*p == find) *p = replace;
+	}
+}
+
+void String::replace(const String& find, const String& replace)
+{
+	if (len == 0 || find.len == 0) return;
+	int diff = replace.len - find.len;
+	char *readFrom = buffer;
+	char *foundAt;
+	if (diff == 0) {
+		while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
+			memcpy(foundAt, replace.buffer, replace.len);
+			readFrom = foundAt + replace.len;
+		}
+	} else if (diff < 0) {
+		char *writeTo = buffer;
+		while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
+			unsigned int n = foundAt - readFrom;
+			memcpy(writeTo, readFrom, n);
+			writeTo += n;
+			memcpy(writeTo, replace.buffer, replace.len);
+			writeTo += replace.len;
+			readFrom = foundAt + find.len;
+			len += diff;
+		}
+		strcpy(writeTo, readFrom);
+	} else {
+		unsigned int size = len; // compute size needed for result
+		while ((foundAt = strstr(readFrom, find.buffer)) != NULL) {
+			readFrom = foundAt + find.len;
+			size += diff;
+		}
+		if (size == len) return;
+		if (size > capacity && !changeBuffer(size)) return; // XXX: tell user!
+		int index = len - 1;
+		while (index >= 0 && (index = lastIndexOf(find, index)) >= 0) {
+			readFrom = buffer + index + find.len;
+			memmove(readFrom + diff, readFrom, len - (readFrom - buffer));
+			len += diff;
+			buffer[len] = 0;
+			memcpy(buffer + index, replace.buffer, replace.len);
+			index--;
+		}
+	}
+}
+
+void String::remove(unsigned int index){
+	// Pass the biggest integer as the count. The remove method
+	// below will take care of truncating it at the end of the
+	// string.
+	remove(index, (unsigned int)-1);
+}
+
+void String::remove(unsigned int index, unsigned int count){
+	if (index >= len) { return; }
+	if (count <= 0) { return; }
+	if (count > len - index) { count = len - index; }
+	char *writeTo = buffer + index;
+	len = len - count;
+	strncpy(writeTo, buffer + index + count,len - index);
+	buffer[len] = 0;
+}
+
+void String::toLowerCase(void)
+{
+	if (!buffer) return;
+	for (char *p = buffer; *p; p++) {
+		*p = tolower(*p);
+	}
+}
+
+void String::toUpperCase(void)
+{
+	if (!buffer) return;
+	for (char *p = buffer; *p; p++) {
+		*p = toupper(*p);
+	}
+}
+
+void String::trim(void)
+{
+	if (!buffer || len == 0) return;
+	char *begin = buffer;
+	while (isspace(*begin)) begin++;
+	char *end = buffer + len - 1;
+	while (isspace(*end) && end >= begin) end--;
+	len = end + 1 - begin;
+	if (begin > buffer) memcpy(buffer, begin, len);
+	buffer[len] = 0;
+}
+
+/*********************************************/
+/*  Parsing / Conversion                     */
+/*********************************************/
+
+long String::toInt(void) const
+{
+	if (buffer) return atol(buffer);
+	return 0;
+}
+
+float String::toFloat(void) const
+{
+	if (buffer) return float(atof(buffer));
+	return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/WString.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,225 @@
+/*
+  WString.h - String library for Wiring & Arduino
+  ...mostly rewritten by Paul Stoffregen...
+  Copyright (c) 2009-10 Hernando Barragan.  All right reserved.
+  Copyright 2011, Paul Stoffregen, paul@pjrc.com
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef String_class_h
+#define String_class_h
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <avr/pgmspace.h>
+
+// When compiling programs with this class, the following gcc parameters
+// dramatically increase performance and memory (RAM) efficiency, typically
+// with little or no increase in code size.
+//     -felide-constructors
+//     -std=c++0x
+
+class __FlashStringHelper;
+#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))
+
+// An inherited class for holding the result of a concatenation.  These
+// result objects are assumed to be writable by subsequent concatenations.
+class StringSumHelper;
+
+// The string class
+class String
+{
+	// use a function pointer to allow for "if (s)" without the
+	// complications of an operator bool(). for more information, see:
+	// http://www.artima.com/cppsource/safebool.html
+	typedef void (String::*StringIfHelperType)() const;
+	void StringIfHelper() const {}
+
+public:
+	// constructors
+	// creates a copy of the initial value.
+	// if the initial value is null or invalid, or if memory allocation
+	// fails, the string will be marked as invalid (i.e. "if (s)" will
+	// be false).
+	String(const char *cstr = "");
+	String(const String &str);
+	String(const __FlashStringHelper *str);
+       #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+	String(String &&rval);
+	String(StringSumHelper &&rval);
+	#endif
+	explicit String(char c);
+	explicit String(unsigned char, unsigned char base=10);
+	explicit String(int, unsigned char base=10);
+	explicit String(unsigned int, unsigned char base=10);
+	explicit String(long, unsigned char base=10);
+	explicit String(unsigned long, unsigned char base=10);
+	explicit String(float, unsigned char decimalPlaces=2);
+	explicit String(double, unsigned char decimalPlaces=2);
+	~String(void);
+
+	// memory management
+	// return true on success, false on failure (in which case, the string
+	// is left unchanged).  reserve(0), if successful, will validate an
+	// invalid string (i.e., "if (s)" will be true afterwards)
+	unsigned char reserve(unsigned int size);
+	inline unsigned int length(void) const {return len;}
+
+	// creates a copy of the assigned value.  if the value is null or
+	// invalid, or if the memory allocation fails, the string will be 
+	// marked as invalid ("if (s)" will be false).
+	String & operator = (const String &rhs);
+	String & operator = (const char *cstr);
+	String & operator = (const __FlashStringHelper *str);
+       #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+	String & operator = (String &&rval);
+	String & operator = (StringSumHelper &&rval);
+	#endif
+
+	// concatenate (works w/ built-in types)
+	
+	// returns true on success, false on failure (in which case, the string
+	// is left unchanged).  if the argument is null or invalid, the 
+	// concatenation is considered unsucessful.  
+	unsigned char concat(const String &str);
+	unsigned char concat(const char *cstr);
+	unsigned char concat(char c);
+	unsigned char concat(unsigned char c);
+	unsigned char concat(int num);
+	unsigned char concat(unsigned int num);
+	unsigned char concat(long num);
+	unsigned char concat(unsigned long num);
+	unsigned char concat(float num);
+	unsigned char concat(double num);
+	unsigned char concat(const __FlashStringHelper * str);
+	
+	// if there's not enough memory for the concatenated value, the string
+	// will be left unchanged (but this isn't signalled in any way)
+	String & operator += (const String &rhs)	{concat(rhs); return (*this);}
+	String & operator += (const char *cstr)		{concat(cstr); return (*this);}
+	String & operator += (char c)			{concat(c); return (*this);}
+	String & operator += (unsigned char num)		{concat(num); return (*this);}
+	String & operator += (int num)			{concat(num); return (*this);}
+	String & operator += (unsigned int num)		{concat(num); return (*this);}
+	String & operator += (long num)			{concat(num); return (*this);}
+	String & operator += (unsigned long num)	{concat(num); return (*this);}
+	String & operator += (float num)		{concat(num); return (*this);}
+	String & operator += (double num)		{concat(num); return (*this);}
+	String & operator += (const __FlashStringHelper *str){concat(str); return (*this);}
+
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, char c);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, int num);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, long num);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, float num);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, double num);
+	friend StringSumHelper & operator + (const StringSumHelper &lhs, const __FlashStringHelper *rhs);
+
+	// comparison (only works w/ Strings and "strings")
+	operator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; }
+	int compareTo(const String &s) const;
+	unsigned char equals(const String &s) const;
+	unsigned char equals(const char *cstr) const;
+	unsigned char operator == (const String &rhs) const {return equals(rhs);}
+	unsigned char operator == (const char *cstr) const {return equals(cstr);}
+	unsigned char operator != (const String &rhs) const {return !equals(rhs);}
+	unsigned char operator != (const char *cstr) const {return !equals(cstr);}
+	unsigned char operator <  (const String &rhs) const;
+	unsigned char operator >  (const String &rhs) const;
+	unsigned char operator <= (const String &rhs) const;
+	unsigned char operator >= (const String &rhs) const;
+	unsigned char equalsIgnoreCase(const String &s) const;
+	unsigned char startsWith( const String &prefix) const;
+	unsigned char startsWith(const String &prefix, unsigned int offset) const;
+	unsigned char endsWith(const String &suffix) const;
+
+	// character acccess
+	char charAt(unsigned int index) const;
+	void setCharAt(unsigned int index, char c);
+	char operator [] (unsigned int index) const;
+	char& operator [] (unsigned int index);
+	void getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const;
+	void toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const
+		{getBytes((unsigned char *)buf, bufsize, index);}
+	const char * c_str() const { return buffer; }
+
+	// search
+	int indexOf( char ch ) const;
+	int indexOf( char ch, unsigned int fromIndex ) const;
+	int indexOf( const String &str ) const;
+	int indexOf( const String &str, unsigned int fromIndex ) const;
+	int lastIndexOf( char ch ) const;
+	int lastIndexOf( char ch, unsigned int fromIndex ) const;
+	int lastIndexOf( const String &str ) const;
+	int lastIndexOf( const String &str, unsigned int fromIndex ) const;
+	String substring( unsigned int beginIndex ) const { return substring(beginIndex, len); };
+	String substring( unsigned int beginIndex, unsigned int endIndex ) const;
+
+	// modification
+	void replace(char find, char replace);
+	void replace(const String& find, const String& replace);
+	void remove(unsigned int index);
+	void remove(unsigned int index, unsigned int count);
+	void toLowerCase(void);
+	void toUpperCase(void);
+	void trim(void);
+
+	// parsing/conversion
+	long toInt(void) const;
+	float toFloat(void) const;
+
+protected:
+	char *buffer;	        // the actual char array
+	unsigned int capacity;  // the array length minus one (for the '\0')
+	unsigned int len;       // the String length (not counting the '\0')
+protected:
+	void init(void);
+	void invalidate(void);
+	unsigned char changeBuffer(unsigned int maxStrLen);
+	unsigned char concat(const char *cstr, unsigned int length);
+
+	// copy and move
+	String & copy(const char *cstr, unsigned int length);
+	String & copy(const __FlashStringHelper *pstr, unsigned int length);
+       #if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+	void move(String &rhs);
+	#endif
+};
+
+class StringSumHelper : public String
+{
+public:
+	StringSumHelper(const String &s) : String(s) {}
+	StringSumHelper(const char *p) : String(p) {}
+	StringSumHelper(char c) : String(c) {}
+	StringSumHelper(unsigned char num) : String(num) {}
+	StringSumHelper(int num) : String(num) {}
+	StringSumHelper(unsigned int num) : String(num) {}
+	StringSumHelper(long num) : String(num) {}
+	StringSumHelper(unsigned long num) : String(num) {}
+	StringSumHelper(float num) : String(num) {}
+	StringSumHelper(double num) : String(num) {}
+};
+
+#endif  // __cplusplus
+#endif  // String_class_h
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/arStream.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,321 @@
+/*
+ arStream.cpp - adds parsing methods to arStream class
+ Copyright (c) 2008 David A. Mellis.  All right reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+ Created July 2011
+ parsing functions based on TextFinder library by Michael Margolis
+
+ findMulti/findUntil routines written by Jim Leonard/Xuth
+ */
+
+#include "Arduino.h"
+#include "arStream.h"
+#include "Usb.h"
+
+#define PARSE_TIMEOUT 1000  // default number of milli-seconds to wait
+#define NO_SKIP_CHAR  1  // a magic char not found in a valid ASCII numeric field
+
+extern Timer USB::arduinoTimer;
+
+// private method to read stream with timeout
+int arStream::timedRead()
+{
+  int c;
+  _startMillis = millis();
+  do {
+    c = read();
+    if (c >= 0) return c;
+  } while(millis() - _startMillis < _timeout);
+  return -1;     // -1 indicates timeout
+}
+
+// private method to peek stream with timeout
+int arStream::timedPeek()
+{
+  int c;
+  _startMillis = millis();
+  do {
+    c = peek();
+    if (c >= 0) return c;
+  } while(millis() - _startMillis < _timeout);
+  return -1;     // -1 indicates timeout
+}
+
+// returns peek of the next digit in the stream or -1 if timeout
+// discards non-numeric characters
+int arStream::peekNextDigit()
+{
+  int c;
+  while (1) {
+    c = timedPeek();
+    if (c < 0) return c;  // timeout
+    if (c == '-') return c;
+    if (c >= '0' && c <= '9') return c;
+    read();  // discard non-numeric
+  }
+}
+
+// Public Methods
+//////////////////////////////////////////////////////////////
+
+void arStream::setTimeout(unsigned long timeout)  // sets the maximum number of milliseconds to wait
+{
+  _timeout = timeout;
+}
+
+ // find returns true if the target string is found
+bool  arStream::find(char *target)
+{
+  return findUntil(target, strlen(target), NULL, 0);
+}
+
+// reads data from the stream until the target string of given length is found
+// returns true if target string is found, false if timed out
+bool arStream::find(char *target, size_t length)
+{
+  return findUntil(target, length, NULL, 0);
+}
+
+// as find but search ends if the terminator string is found
+bool  arStream::findUntil(char *target, char *terminator)
+{
+  return findUntil(target, strlen(target), terminator, strlen(terminator));
+}
+
+// reads data from the stream until the target string of the given length is found
+// search terminated if the terminator string is found
+// returns true if target string is found, false if terminated or timed out
+bool arStream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen)
+{
+  if (terminator == NULL) {
+    MultiTarget t[1] = {{target, targetLen, 0}};
+    return findMulti(t, 1) == 0 ? true : false;
+  } else {
+    MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
+    return findMulti(t, 2) == 0 ? true : false;
+  }
+}
+
+
+// returns the first valid (long) integer value from the current position.
+// initial characters that are not digits (or the minus sign) are skipped
+// function is terminated by the first character that is not a digit.
+long arStream::parseInt()
+{
+  return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
+}
+
+// as above but a given skipChar is ignored
+// this allows format characters (typically commas) in values to be ignored
+long arStream::parseInt(char skipChar)
+{
+  bool isNegative = false;
+  long value = 0;
+  int c;
+
+  c = peekNextDigit();
+  // ignore non numeric leading characters
+  if(c < 0)
+    return 0; // zero returned if timeout
+
+  do{
+    if(c == skipChar)
+      ; // ignore this charactor
+    else if(c == '-')
+      isNegative = true;
+    else if(c >= '0' && c <= '9')        // is c a digit?
+      value = value * 10 + c - '0';
+    read();  // consume the character we got with peek
+    c = timedPeek();
+  }
+  while( (c >= '0' && c <= '9') || c == skipChar );
+
+  if(isNegative)
+    value = -value;
+  return value;
+}
+
+
+// as parseInt but returns a floating point value
+float arStream::parseFloat()
+{
+  return parseFloat(NO_SKIP_CHAR);
+}
+
+// as above but the given skipChar is ignored
+// this allows format characters (typically commas) in values to be ignored
+float arStream::parseFloat(char skipChar){
+  bool isNegative = false;
+  bool isFraction = false;
+  long value = 0;
+  char c;
+  float fraction = 1.0;
+
+  c = peekNextDigit();
+    // ignore non numeric leading characters
+  if(c < 0)
+    return 0; // zero returned if timeout
+
+  do{
+    if(c == skipChar)
+      ; // ignore
+    else if(c == '-')
+      isNegative = true;
+    else if (c == '.')
+      isFraction = true;
+    else if(c >= '0' && c <= '9')  {      // is c a digit?
+      value = value * 10 + c - '0';
+      if(isFraction)
+         fraction *= 0.1;
+    }
+    read();  // consume the character we got with peek
+    c = timedPeek();
+  }
+  while( (c >= '0' && c <= '9')  || c == '.' || c == skipChar );
+
+  if(isNegative)
+    value = -value;
+  if(isFraction)
+    return value * fraction;
+  else
+    return value;
+}
+
+// read characters from stream into buffer
+// terminates if length characters have been read, or timeout (see setTimeout)
+// returns the number of characters placed in the buffer
+// the buffer is NOT null terminated.
+//
+size_t arStream::readBytes(char *buffer, size_t length)
+{
+  size_t count = 0;
+  while (count < length) {
+    int c = timedRead();
+    if (c < 0) break;
+    *buffer++ = (char)c;
+    count++;
+  }
+  return count;
+}
+
+
+// as readBytes with terminator character
+// terminates if length characters have been read, timeout, or if the terminator character  detected
+// returns the number of characters placed in the buffer (0 means no valid data found)
+
+size_t arStream::readBytesUntil(char terminator, char *buffer, size_t length)
+{
+  if (length < 1) return 0;
+  size_t index = 0;
+  while (index < length) {
+    int c = timedRead();
+    if (c < 0 || c == terminator) break;
+    *buffer++ = (char)c;
+    index++;
+  }
+  return index; // return number of characters, not including null terminator
+}
+
+String arStream::readString()
+{
+  String ret;
+  int c = timedRead();
+  while (c >= 0)
+  {
+    ret += (char)c;
+    c = timedRead();
+  }
+  return ret;
+}
+
+String arStream::readStringUntil(char terminator)
+{
+  String ret;
+  int c = timedRead();
+  while (c >= 0 && c != terminator)
+  {
+    ret += (char)c;
+    c = timedRead();
+  }
+  return ret;
+}
+
+int arStream::findMulti( struct arStream::MultiTarget *targets, int tCount) {
+  // any zero length target string automatically matches and would make
+  // a mess of the rest of the algorithm.
+  for (struct MultiTarget *t = targets; t < targets+tCount; ++t) {
+    if (t->len <= 0)
+      return t - targets;
+  }
+
+  while (1) {
+    int c = timedRead();
+    if (c < 0)
+      return -1;
+
+    for (struct MultiTarget *t = targets; t < targets+tCount; ++t) {
+      // the simple case is if we match, deal with that first.
+      if (c == t->str[t->index]) {
+        if (++t->index == t->len)
+          return t - targets;
+        else
+          continue;
+      }
+
+      // if not we need to walk back and see if we could have matched further
+      // down the stream (ie '1112' doesn't match the first position in '11112'
+      // but it will match the second position so we can't just reset the current
+      // index to 0 when we find a mismatch.
+      if (t->index == 0)
+        continue;
+
+      int origIndex = t->index;
+      do {
+        --t->index;
+        // first check if current char works against the new current index
+        if (c != t->str[t->index])
+          continue;
+
+        // if it's the only char then we're good, nothing more to check
+        if (t->index == 0) {
+          t->index++;
+          break;
+        }
+
+        // otherwise we need to check the rest of the found string
+        int diff = origIndex - t->index;
+        size_t i;
+        for (i = 0; i < t->index; ++i) {
+          if (t->str[i] != t->str[i + diff])
+            break;
+        }
+
+        // if we successfully got through the previous loop then our current
+        // index is good.
+        if (i == t->index) {
+          t->index++;
+          break;
+        }
+
+        // otherwise we just try the next index
+      } while (t->index);
+    }
+  }
+  // unreachable
+  return -1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/arStream.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,113 @@
+/*
+  arStream.h - base class for character-based streams.
+  Copyright (c) 2010 David A. Mellis.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+  parsing functions based on TextFinder library by Michael Margolis
+*/
+
+#ifndef Stream_h
+#define Stream_h
+
+#include <inttypes.h>
+#include "Print.h"
+
+// compatability macros for testing
+/*
+#define   getInt()            parseInt()
+#define   getInt(skipChar)    parseInt(skipchar)
+#define   getFloat()          parseFloat()
+#define   getFloat(skipChar)  parseFloat(skipChar)
+#define   getString( pre_string, post_string, buffer, length)
+readBytesBetween( pre_string, terminator, buffer, length)
+*/
+
+class arStream : public Print
+{
+  protected:
+    unsigned long _timeout;      // number of milliseconds to wait for the next char before aborting timed read
+    unsigned long _startMillis;  // used for timeout measurement
+    int timedRead();    // private method to read stream with timeout
+    int timedPeek();    // private method to peek stream with timeout
+    int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
+
+  public:
+    virtual int available() = 0;
+    virtual int read() = 0;
+    virtual int peek() = 0;
+    virtual void flush() = 0;
+
+    arStream() {_timeout=1000;}
+
+// parsing methods
+
+  void setTimeout(unsigned long timeout);  // sets maximum milliseconds to wait for stream data, default is 1 second
+
+  bool find(char *target);   // reads data from the stream until the target string is found
+  bool find(uint8_t *target) { return find ((char *)target); }
+  // returns true if target string is found, false if timed out (see setTimeout)
+
+  bool find(char *target, size_t length);   // reads data from the stream until the target string of given length is found
+  bool find(uint8_t *target, size_t length) { return find ((char *)target, length); }
+  // returns true if target string is found, false if timed out
+
+  bool findUntil(char *target, char *terminator);   // as find but search ends if the terminator string is found
+  bool findUntil(uint8_t *target, char *terminator) { return findUntil((char *)target, terminator); }
+
+  bool findUntil(char *target, size_t targetLen, char *terminate, size_t termLen);   // as above but search ends if the terminate string is found
+  bool findUntil(uint8_t *target, size_t targetLen, char *terminate, size_t termLen) {return findUntil((char *)target, targetLen, terminate, termLen); }
+
+
+  long parseInt(); // returns the first valid (long) integer value from the current position.
+  // initial characters that are not digits (or the minus sign) are skipped
+  // integer is terminated by the first character that is not a digit.
+
+  float parseFloat();               // float version of parseInt
+
+  size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer
+  size_t readBytes( uint8_t *buffer, size_t length) { return readBytes((char *)buffer, length); }
+  // terminates if length characters have been read or timeout (see setTimeout)
+  // returns the number of characters placed in the buffer (0 means no valid data found)
+
+  size_t readBytesUntil( char terminator, char *buffer, size_t length); // as readBytes with terminator character
+  size_t readBytesUntil( char terminator, uint8_t *buffer, size_t length) { return readBytesUntil(terminator, (char *)buffer, length); }
+  // terminates if length characters have been read, timeout, or if the terminator character  detected
+  // returns the number of characters placed in the buffer (0 means no valid data found)
+
+  // Arduino String functions to be added here
+  String readString();
+  String readStringUntil(char terminator);
+
+  protected:
+  long parseInt(char skipChar); // as above but the given skipChar is ignored
+  // as above but the given skipChar is ignored
+  // this allows format characters (typically commas) in values to be ignored
+
+  float parseFloat(char skipChar);  // as above but the given skipChar is ignored
+
+  struct MultiTarget {
+    const char *str;  // string you're searching for
+    size_t len;       // length of string you're searching for
+    size_t index;     // index used by the search routine.
+  };
+
+  // This allows you to search for an arbitrary number of strings.
+  // Returns index of the target that is found first or -1 if timeout occurs.
+  int findMulti(struct MultiTarget *targets, int tCount);
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/avr/dtostrf.c	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,30 @@
+/*
+  dtostrf - Emulation for dtostrf function from avr-libc
+  Copyright (c) 2013 Arduino.  All rights reserved.
+  Written by Cristian Maglie <c.maglie@arduino.cc>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include <stdio.h>
+
+char *dtostrf (double val, signed char width, unsigned char prec, char *sout) {
+  char fmt[20];
+  sprintf(fmt, "%%%d.%df", width, prec);
+  sprintf(sout, fmt, val);
+  return sout;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/avr/dtostrf.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,30 @@
+/*
+  dtostrf - Emulation for dtostrf function from avr-libc
+  Copyright (c) 2013 Arduino.  All rights reserved.
+  Written by Cristian Maglie <c.maglie@arduino.cc>
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char *dtostrf (double val, signed char width, unsigned char prec, char *sout);
+
+#ifdef __cplusplus
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/avr/pgmspace.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,71 @@
+/*
+  pgmspace.h - Definitions for compatibility with AVR pgmspace macros
+
+  Copyright (c) 2015 Arduino LLC
+
+  Based on work of Paul Stoffregen on Teensy 3 (http://pjrc.com)
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE
+*/
+
+#ifndef __PGMSPACE_H_
+#define __PGMSPACE_H_ 1
+
+#include <inttypes.h>
+
+#define PROGMEM
+#define PGM_P  const char *
+#define PSTR(str) (str)
+
+#define _SFR_BYTE(n) (n)
+
+typedef void prog_void;
+typedef char prog_char;
+typedef unsigned char prog_uchar;
+typedef int8_t prog_int8_t;
+typedef uint8_t prog_uint8_t;
+typedef int16_t prog_int16_t;
+typedef uint16_t prog_uint16_t;
+typedef int32_t prog_int32_t;
+typedef uint32_t prog_uint32_t;
+
+#define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
+#define strcpy_P(dest, src) strcpy((dest), (src))
+#define strcat_P(dest, src) strcat((dest), (src))
+#define strcmp_P(a, b) strcmp((a), (b))
+#define strstr_P(a, b) strstr((a), (b))
+#define strlen_P(a) strlen((a))
+#define sprintf_P(s, f, ...) sprintf((s), (f), __VA_ARGS__)
+
+#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
+#define pgm_read_word(addr) (*(const unsigned short *)(addr))
+#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
+#define pgm_read_float(addr) (*(const float *)(addr))
+
+#define pgm_read_byte_near(addr) pgm_read_byte(addr)
+#define pgm_read_word_near(addr) pgm_read_word(addr)
+#define pgm_read_dword_near(addr) pgm_read_dword(addr)
+#define pgm_read_float_near(addr) pgm_read_float(addr)
+#define pgm_read_byte_far(addr) pgm_read_byte(addr)
+#define pgm_read_word_far(addr) pgm_read_word(addr)
+#define pgm_read_dword_far(addr) pgm_read_dword(addr)
+#define pgm_read_float_far(addr) pgm_read_float(addr)
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/itoa.c	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,171 @@
+/*
+  Copyright (c) 2011 Arduino.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+  See the GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "itoa.h"
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C"{
+#endif // __cplusplus
+
+#if 0
+/* reverse:  reverse string s in place */
+static void reverse( char s[] )
+{
+  int i, j ;
+  char c ;
+
+  for ( i = 0, j = strlen(s)-1 ; i < j ; i++, j-- )
+  {
+    c = s[i] ;
+    s[i] = s[j] ;
+    s[j] = c ;
+  }
+}
+
+/* itoa:  convert n to characters in s */
+extern void itoa( int n, char s[] )
+{
+  int i, sign ;
+
+  if ( (sign = n) < 0 )  /* record sign */
+  {
+    n = -n;          /* make n positive */
+  }
+
+  i = 0;
+  do
+  {       /* generate digits in reverse order */
+    s[i++] = n % 10 + '0';   /* get next digit */
+  } while ((n /= 10) > 0) ;     /* delete it */
+
+  if (sign < 0 )
+  {
+    s[i++] = '-';
+  }
+
+  s[i] = '\0';
+
+  reverse( s ) ;
+}
+
+#else
+
+extern char* itoa( int value, char *string, int radix )
+{
+  return ltoa( value, string, radix ) ;
+}
+
+extern char* ltoa( long value, char *string, int radix )
+{
+  char tmp[33];
+  char *tp = tmp;
+  long i;
+  unsigned long v;
+  int sign;
+  char *sp;
+
+  if ( string == NULL )
+  {
+    return 0 ;
+  }
+
+  if (radix > 36 || radix <= 1)
+  {
+    return 0 ;
+  }
+
+  sign = (radix == 10 && value < 0);
+  if (sign)
+  {
+    v = -value;
+  }
+  else
+  {
+    v = (unsigned long)value;
+  }
+
+  while (v || tp == tmp)
+  {
+    i = v % radix;
+    v = v / radix;
+    if (i < 10)
+      *tp++ = i+'0';
+    else
+      *tp++ = i + 'a' - 10;
+  }
+
+  sp = string;
+
+  if (sign)
+    *sp++ = '-';
+  while (tp > tmp)
+    *sp++ = *--tp;
+  *sp = 0;
+
+  return string;
+}
+
+extern char* utoa( unsigned long value, char *string, int radix )
+{
+  return ultoa( value, string, radix ) ;
+}
+
+extern char* ultoa( unsigned long value, char *string, int radix )
+{
+  char tmp[33];
+  char *tp = tmp;
+  long i;
+  unsigned long v = value;
+  char *sp;
+
+  if ( string == NULL )
+  {
+    return 0;
+  }
+
+  if (radix > 36 || radix <= 1)
+  {
+    return 0;
+  }
+ 
+  while (v || tp == tmp)
+  {
+    i = v % radix;
+    v = v / radix;
+    if (i < 10)
+      *tp++ = i+'0';
+    else
+      *tp++ = i + 'a' - 10;
+  }
+
+  sp = string;
+
+ 
+  while (tp > tmp)
+    *sp++ = *--tp;
+  *sp = 0;
+
+  return string;
+}
+#endif /* 0 */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Arduino_lib/itoa.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,43 @@
+/*
+  Copyright (c) 2011 Arduino.  All right reserved.
+
+  This library is free software; you can redistribute it and/or
+  modify it under the terms of the GNU Lesser General Public
+  License as published by the Free Software Foundation; either
+  version 2.1 of the License, or (at your option) any later version.
+
+  This library is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
+  See the GNU Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public
+  License along with this library; if not, write to the Free Software
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef _ITOA_
+#define _ITOA_
+
+#ifdef __cplusplus
+extern "C"{
+#endif // __cplusplus
+
+#if 0
+
+extern void itoa( int n, char s[] ) ;
+
+#else
+
+extern char* itoa( int value, char *string, int radix ) ;
+extern char* ltoa( long value, char *string, int radix ) ;
+extern char* utoa( unsigned long value, char *string, int radix ) ;
+extern char* ultoa( unsigned long value, char *string, int radix ) ;
+#endif /* 0 */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif // _ITOA_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/BTD.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,1385 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "BTD.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+
+const uint8_t BTD::BTD_CONTROL_PIPE = 0;
+const uint8_t BTD::BTD_EVENT_PIPE = 1;
+const uint8_t BTD::BTD_DATAIN_PIPE = 2;
+const uint8_t BTD::BTD_DATAOUT_PIPE = 3;
+
+BTD::BTD(USB *p) :
+connectToWii(false),
+pairWithWii(false),
+connectToHIDDevice(false),
+pairWithHIDDevice(false),
+pUsb(p), // Pointer to USB class instance - mandatory
+bAddress(0), // Device address - mandatory
+bNumEP(1), // If config descriptor needs to be parsed
+qNextPollTime(0), // Reset NextPollTime
+pollInterval(0),
+bPollEnable(false) // Don't start polling before dongle is connected
+{
+        for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++)
+                btService[i] = NULL;
+
+        Initialize(); // Set all variables, endpoint structs etc. to default values
+
+        if(pUsb) // Register in USB subsystem
+                pUsb->RegisterDeviceClass(this); // Set devConfig[] entry
+}
+
+uint8_t BTD::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+
+        Initialize(); // Set all variables, endpoint structs etc. to default values
+
+        AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nBTD ConfigureDevice"), 0x80);
+#endif
+
+        if(bAddress) { // Check if address has already been assigned to an instance
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress in use"), 0x80);
+#endif
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+
+        p = addrPool.GetUsbDevicePtr(0); // Get pointer to pseudo device with address 0 assigned
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        if(!p->epinfo) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nepinfo is null"), 0x80);
+#endif
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        oldep_ptr = p->epinfo; // Save old pointer to EP_RECORD of address 0
+        p->epinfo = epInfo; // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->lowspeed = lowspeed;
+        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
+
+        p->epinfo = oldep_ptr; // Restore p->epinfo
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        bAddress = addrPool.AllocAddress(parent, false, port); // Allocate new address according to device class
+
+        if(!bAddress) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nOut of address space"), 0x80);
+#endif
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+        }
+
+        if (udd->bDeviceClass == 0x09) // Some dongles have an USB hub inside
+                goto FailHub;
+
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0; // Extract Max Packet Size from device descriptor
+        epInfo[1].epAddr = udd->bNumConfigurations; // Steal and abuse from epInfo structure to save memory
+
+        VID = udd->idVendor;
+        PID = udd->idProduct;
+
+        return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;
+
+FailHub:
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nPlease create a hub instance in your code: \"USBHub Hub1(&Usb);\""), 0x80);
+#endif
+        pUsb->setAddr(bAddress, 0, 0); // Reset address
+        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+        Release();
+        return rcode;
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr(rcode);
+#endif
+        if(rcode != hrJERR)
+                rcode = USB_ERROR_FailGetDevDescr;
+        Release();
+        return rcode;
+};
+
+uint8_t BTD::Init(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed) {
+        uint8_t rcode;
+        uint8_t num_of_conf = epInfo[1].epAddr; // Number of configurations
+        epInfo[1].epAddr = 0;
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nBTD Init"), 0x80);
+#endif
+        UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record
+
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        delay(300); // Assign new address to the device
+
+        rcode = pUsb->setAddr(0, 0, bAddress); // Assign new address to the device
+        if(rcode) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nsetAddr: "), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+#endif
+                p->lowspeed = false;
+                goto Fail;
+        }
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nAddr: "), 0x80);
+        D_PrintHex<uint8_t > (bAddress, 0x80);
+#endif
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        p->lowspeed = lowspeed;
+
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); // Assign epInfo to epinfo pointer - only EP0 is known
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        if(VID == PS3_VID && (PID == PS3_PID || PID == PS3NAVIGATION_PID || PID == PS3MOVE_PID)) {
+                delay(100);
+                rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 1); // We only need the Control endpoint, so we don't have to initialize the other endpoints of device
+                if(rcode)
+                        goto FailSetConfDescr;
+
+#ifdef DEBUG_USB_HOST
+                if(PID == PS3_PID || PID == PS3NAVIGATION_PID) {
+                        if(PID == PS3_PID)
+                                Notify(PSTR("\r\nDualshock 3 Controller Connected"), 0x80);
+                        else // It must be a navigation controller
+                                Notify(PSTR("\r\nNavigation Controller Connected"), 0x80);
+                } else // It must be a Motion controller
+                        Notify(PSTR("\r\nMotion Controller Connected"), 0x80);
+#endif
+
+                if(my_bdaddr[0] == 0x00 && my_bdaddr[1] == 0x00 && my_bdaddr[2] == 0x00 && my_bdaddr[3] == 0x00 && my_bdaddr[4] == 0x00 && my_bdaddr[5] == 0x00) {
+#ifdef DEBUG_USB_HOST
+                        Notify(PSTR("\r\nPlease plug in the dongle before trying to pair with the PS3 Controller\r\nor set the Bluetooth address in the constructor of the PS3BT class"), 0x80);
+#endif
+                } else {
+                        if(PID == PS3_PID || PID == PS3NAVIGATION_PID)
+                                setBdaddr(my_bdaddr); // Set internal Bluetooth address
+                        else
+                                setMoveBdaddr(my_bdaddr); // Set internal Bluetooth address
+#ifdef DEBUG_USB_HOST
+                        Notify(PSTR("\r\nBluetooth Address was set to: "), 0x80);
+                        for(int8_t i = 5; i > 0; i--) {
+                                D_PrintHex<uint8_t > (my_bdaddr[i], 0x80);
+                                Notify(PSTR(":"), 0x80);
+                        }
+                        D_PrintHex<uint8_t > (my_bdaddr[0], 0x80);
+#endif
+                }
+
+                pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 0); // Reset configuration value
+                pUsb->setAddr(bAddress, 0, 0); // Reset address
+                Release(); // Release device
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; // Return
+        } else {
+                // Check if attached device is a Bluetooth dongle and fill endpoint data structure
+                // First interface in the configuration must have Bluetooth assigned Class/Subclass/Protocol
+                // And 3 endpoints - interrupt-IN, bulk-IN, bulk-OUT, not necessarily in this order
+                for(uint8_t i = 0; i < num_of_conf; i++) {
+                        if((VID == IOGEAR_GBU521_VID && PID == IOGEAR_GBU521_PID) || (VID == BELKIN_F8T065BF_VID && PID == BELKIN_F8T065BF_PID)) {
+                                ConfigDescParser<USB_CLASS_VENDOR_SPECIFIC, WI_SUBCLASS_RF, WI_PROTOCOL_BT, CP_MASK_COMPARE_ALL> confDescrParser(this); // Workaround issue with some dongles
+                                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+                        } else {
+                                ConfigDescParser<USB_CLASS_WIRELESS_CTRL, WI_SUBCLASS_RF, WI_PROTOCOL_BT, CP_MASK_COMPARE_ALL> confDescrParser(this); // Set class id according to the specification
+                                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+                        }
+                        if(rcode) // Check error code
+                                goto FailGetConfDescr;
+                        if(bNumEP >= BTD_MAX_ENDPOINTS) // All endpoints extracted
+                                break;
+                }
+
+                if(bNumEP < BTD_MAX_ENDPOINTS)
+                        goto FailUnknownDevice;
+
+                // Assign epInfo to epinfo pointer - this time all 3 endpoins
+                rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+                if(rcode)
+                        goto FailSetDevTblEntry;
+
+                // Set Configuration Value
+                rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bConfNum);
+                if(rcode)
+                        goto FailSetConfDescr;
+
+                hci_num_reset_loops = 100; // only loop 100 times before trying to send the hci reset command
+                hci_counter = 0;
+                hci_state = HCI_INIT_STATE;
+                waitingForConnection = false;
+                bPollEnable = true;
+
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nBluetooth Dongle Initialized"), 0x80);
+#endif
+        }
+        return 0; // Successful configuration
+
+        /* Diagnostic messages */
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+#endif
+        goto Fail;
+
+FailUnknownDevice:
+#ifdef DEBUG_USB_HOST
+        NotifyFailUnknownDevice(VID, PID);
+#endif
+        pUsb->setAddr(bAddress, 0, 0); // Reset address
+        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+Fail:
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nBTD Init Failed, error code: "), 0x80);
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+void BTD::Initialize() {
+        uint8_t i;
+        for(i = 0; i < BTD_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+        for(i = 0; i < BTD_NUM_SERVICES; i++) {
+                if(btService[i])
+                        btService[i]->Reset(); // Reset all Bluetooth services
+        }
+
+        connectToWii = false;
+        incomingWii = false;
+        connectToHIDDevice = false;
+        incomingHIDDevice = false;
+        incomingPS4 = false;
+        bAddress = 0; // Clear device address
+        bNumEP = 1; // Must have to be reset to 1
+        qNextPollTime = 0; // Reset next poll time
+        pollInterval = 0;
+        bPollEnable = false; // Don't start polling before dongle is connected
+}
+
+/* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */
+void BTD::EndpointXtract(uint8_t conf, uint8_t iface __attribute__((unused)), uint8_t alt, uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR *pep) {
+        //ErrorMessage<uint8_t>(PSTR("Conf.Val"),conf);
+        //ErrorMessage<uint8_t>(PSTR("Iface Num"),iface);
+        //ErrorMessage<uint8_t>(PSTR("Alt.Set"),alt);
+
+        if(alt) // Wrong interface - by BT spec, no alt setting
+                return;
+
+        bConfNum = conf;
+        uint8_t index;
+
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT && (pep->bEndpointAddress & 0x80) == 0x80) { // Interrupt In endpoint found
+                index = BTD_EVENT_PIPE;
+                epInfo[index].bmNakPower = USB_NAK_NOWAIT;
+        } else if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_BULK) // Bulk endpoint found
+                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? BTD_DATAIN_PIPE : BTD_DATAOUT_PIPE;
+        else
+            return;
+
+        // Fill the rest of endpoint data structure
+        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+#ifdef EXTRADEBUG
+        PrintEndpointDescriptor(pep);
+#endif
+        if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints
+                pollInterval = pep->bInterval;
+        bNumEP++;
+}
+
+void BTD::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr __attribute__((unused))) {
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nEndpoint descriptor:"), 0x80);
+        Notify(PSTR("\r\nLength:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
+        Notify(PSTR("\r\nType:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
+        Notify(PSTR("\r\nAddress:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
+        Notify(PSTR("\r\nAttributes:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
+        Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
+        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
+        Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
+#endif
+}
+
+/* Performs a cleanup after failed Init() attempt */
+uint8_t BTD::Release() {
+        Initialize(); // Set all variables, endpoint structs etc. to default values
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+        return 0;
+}
+
+uint8_t BTD::Poll() {
+        if(!bPollEnable)
+                return 0;
+        if((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) { // Don't poll if shorter than polling interval
+                qNextPollTime = (uint32_t)millis() + pollInterval; // Set new poll time
+                HCI_event_task(); // Poll the HCI event pipe
+                HCI_task(); // HCI state machine
+                ACL_event_task(); // Poll the ACL input pipe too
+        }
+        return 0;
+}
+
+void BTD::disconnect() {
+        for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++)
+                if(btService[i])
+                        btService[i]->disconnect();
+};
+
+void BTD::HCI_event_task() {
+        uint16_t length = BULK_MAXPKTSIZE; // Request more than 16 bytes anyway, the inTransfer routine will take care of this
+        uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_EVENT_PIPE ].epAddr, &length, hcibuf, pollInterval); // Input on endpoint 1
+
+        if(!rcode || rcode == hrNAK) { // Check for errors
+                switch(hcibuf[0]) { // Switch on event type
+                        case EV_COMMAND_COMPLETE:
+                                if(!hcibuf[5]) { // Check if command succeeded
+                                        hci_set_flag(HCI_FLAG_CMD_COMPLETE); // Set command complete flag
+                                        if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // Parameters from read local version information
+                                                hci_version = hcibuf[6]; // Used to check if it supports 2.0+EDR - see http://www.bluetooth.org/Technical/AssignedNumbers/hci.htm
+                                                hci_set_flag(HCI_FLAG_READ_VERSION);
+                                        } else if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // Parameters from read local bluetooth address
+                                                for(uint8_t i = 0; i < 6; i++)
+                                                        my_bdaddr[i] = hcibuf[6 + i];
+                                                hci_set_flag(HCI_FLAG_READ_BDADDR);
+                                        }
+                                }
+                                break;
+
+                        case EV_COMMAND_STATUS:
+                                if(hcibuf[2]) { // Show status on serial if not OK
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nHCI Command Failed: "), 0x80);
+                                        D_PrintHex<uint8_t > (hcibuf[2], 0x80);
+#endif
+                                }
+                                break;
+
+                        case EV_INQUIRY_COMPLETE:
+                                if(inquiry_counter >= 5 && (pairWithWii || pairWithHIDDevice)) {
+                                        inquiry_counter = 0;
+#ifdef DEBUG_USB_HOST
+                                        if(pairWithWii)
+                                                Notify(PSTR("\r\nCouldn't find Wiimote"), 0x80);
+                                        else
+                                                Notify(PSTR("\r\nCouldn't find HID device"), 0x80);
+#endif
+                                        connectToWii = false;
+                                        pairWithWii = false;
+                                        connectToHIDDevice = false;
+                                        pairWithHIDDevice = false;
+                                        hci_state = HCI_SCANNING_STATE;
+                                }
+                                inquiry_counter++;
+                                break;
+
+                        case EV_INQUIRY_RESULT:
+                                if(hcibuf[2]) { // Check that there is more than zero responses
+#ifdef EXTRADEBUG
+                                        Notify(PSTR("\r\nNumber of responses: "), 0x80);
+                                        Notify(hcibuf[2], 0x80);
+#endif
+                                        for(uint8_t i = 0; i < hcibuf[2]; i++) {
+                                                uint8_t offset = 8 * hcibuf[2] + 3 * i;
+
+                                                for(uint8_t j = 0; j < 3; j++)
+                                                        classOfDevice[j] = hcibuf[j + 4 + offset];
+
+#ifdef EXTRADEBUG
+                                                Notify(PSTR("\r\nClass of device: "), 0x80);
+                                                D_PrintHex<uint8_t > (classOfDevice[2], 0x80);
+                                                Notify(PSTR(" "), 0x80);
+                                                D_PrintHex<uint8_t > (classOfDevice[1], 0x80);
+                                                Notify(PSTR(" "), 0x80);
+                                                D_PrintHex<uint8_t > (classOfDevice[0], 0x80);
+#endif
+
+                                                if(pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0x0C)) { // See http://wiibrew.org/wiki/Wiimote#SDP_information
+                                                        checkRemoteName = true; // Check remote name to distinguish between the different controllers
+
+                                                        for(uint8_t j = 0; j < 6; j++)
+                                                                disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
+
+                                                        hci_set_flag(HCI_FLAG_DEVICE_FOUND);
+                                                        break;
+                                                } else if(pairWithHIDDevice && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad - see: http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html
+#ifdef DEBUG_USB_HOST
+                                                        if(classOfDevice[0] & 0x80)
+                                                                Notify(PSTR("\r\nMouse found"), 0x80);
+                                                        if(classOfDevice[0] & 0x40)
+                                                                Notify(PSTR("\r\nKeyboard found"), 0x80);
+                                                        if(classOfDevice[0] & 0x08)
+                                                                Notify(PSTR("\r\nGamepad found"), 0x80);
+#endif
+
+                                                        for(uint8_t j = 0; j < 6; j++)
+                                                                disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];
+
+                                                        hci_set_flag(HCI_FLAG_DEVICE_FOUND);
+                                                        break;
+                                                }
+                                        }
+                                }
+                                break;
+
+                        case EV_CONNECT_COMPLETE:
+                                hci_set_flag(HCI_FLAG_CONNECT_EVENT);
+                                if(!hcibuf[2]) { // Check if connected OK
+#ifdef EXTRADEBUG
+                                        Notify(PSTR("\r\nConnection established"), 0x80);
+#endif
+                                        hci_handle = hcibuf[3] | ((hcibuf[4] & 0x0F) << 8); // Store the handle for the ACL connection
+                                        hci_set_flag(HCI_FLAG_CONNECT_COMPLETE); // Set connection complete flag
+                                } else {
+                                        hci_state = HCI_CHECK_DEVICE_SERVICE;
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nConnection Failed: "), 0x80);
+                                        D_PrintHex<uint8_t > (hcibuf[2], 0x80);
+#endif
+                                }
+                                break;
+
+                        case EV_DISCONNECT_COMPLETE:
+                                if(!hcibuf[2]) { // Check if disconnected OK
+                                        hci_set_flag(HCI_FLAG_DISCONNECT_COMPLETE); // Set disconnect command complete flag
+                                        hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE); // Clear connection complete flag
+                                }
+                                break;
+
+                        case EV_REMOTE_NAME_COMPLETE:
+                                if(!hcibuf[2]) { // Check if reading is OK
+                                        for(uint8_t i = 0; i < min(sizeof (remote_name), sizeof (hcibuf) - 9); i++) {
+                                                remote_name[i] = hcibuf[9 + i];
+                                                if(remote_name[i] == '\0') // End of string
+                                                        break;
+                                        }
+                                        // TODO: Altid sæt '\0' i remote name!
+                                        hci_set_flag(HCI_FLAG_REMOTE_NAME_COMPLETE);
+                                }
+                                break;
+
+                        case EV_INCOMING_CONNECT:
+                                for(uint8_t i = 0; i < 6; i++)
+                                        disc_bdaddr[i] = hcibuf[i + 2];
+
+                                for(uint8_t i = 0; i < 3; i++)
+                                        classOfDevice[i] = hcibuf[i + 8];
+
+                                if((classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad
+#ifdef DEBUG_USB_HOST
+                                        if(classOfDevice[0] & 0x80)
+                                                Notify(PSTR("\r\nMouse is connecting"), 0x80);
+                                        if(classOfDevice[0] & 0x40)
+                                                Notify(PSTR("\r\nKeyboard is connecting"), 0x80);
+                                        if(classOfDevice[0] & 0x08)
+                                                Notify(PSTR("\r\nGamepad is connecting"), 0x80);
+#endif
+                                        incomingHIDDevice = true;
+                                }
+
+#ifdef EXTRADEBUG
+                                Notify(PSTR("\r\nClass of device: "), 0x80);
+                                D_PrintHex<uint8_t > (classOfDevice[2], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (classOfDevice[1], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (classOfDevice[0], 0x80);
+#endif
+                                hci_set_flag(HCI_FLAG_INCOMING_REQUEST);
+                                break;
+
+                        case EV_PIN_CODE_REQUEST:
+                                if(pairWithWii) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nPairing with Wiimote"), 0x80);
+#endif
+                                        hci_pin_code_request_reply();
+                                } else if(btdPin != NULL) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nBluetooth pin is set too: "), 0x80);
+                                        NotifyStr(btdPin, 0x80);
+#endif
+                                        hci_pin_code_request_reply();
+                                } else {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nNo pin was set"), 0x80);
+#endif
+                                        hci_pin_code_negative_request_reply();
+                                }
+                                break;
+
+                        case EV_LINK_KEY_REQUEST:
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nReceived Key Request"), 0x80);
+#endif
+                                hci_link_key_request_negative_reply();
+                                break;
+
+                        case EV_AUTHENTICATION_COMPLETE:
+                                if(!hcibuf[2]) { // Check if pairing was successful
+                                        if(pairWithWii && !connectToWii) {
+#ifdef DEBUG_USB_HOST
+                                                Notify(PSTR("\r\nPairing successful with Wiimote"), 0x80);
+#endif
+                                                connectToWii = true; // Used to indicate to the Wii service, that it should connect to this device
+                                        } else if(pairWithHIDDevice && !connectToHIDDevice) {
+#ifdef DEBUG_USB_HOST
+                                                Notify(PSTR("\r\nPairing successful with HID device"), 0x80);
+#endif
+                                                connectToHIDDevice = true; // Used to indicate to the BTHID service, that it should connect to this device
+                                        }
+                                } else {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nPairing Failed: "), 0x80);
+                                        D_PrintHex<uint8_t > (hcibuf[2], 0x80);
+#endif
+                                        hci_disconnect(hci_handle);
+                                        hci_state = HCI_DISCONNECT_STATE;
+                                }
+                                break;
+                                /* We will just ignore the following events */
+                        case EV_NUM_COMPLETE_PKT:
+                        case EV_ROLE_CHANGED:
+                        case EV_PAGE_SCAN_REP_MODE:
+                        case EV_LOOPBACK_COMMAND:
+                        case EV_DATA_BUFFER_OVERFLOW:
+                        case EV_CHANGE_CONNECTION_LINK:
+                        case EV_MAX_SLOTS_CHANGE:
+                        case EV_QOS_SETUP_COMPLETE:
+                        case EV_LINK_KEY_NOTIFICATION:
+                        case EV_ENCRYPTION_CHANGE:
+                        case EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE:
+                                break;
+#ifdef EXTRADEBUG
+                        default:
+                                if(hcibuf[0] != 0x00) {
+                                        Notify(PSTR("\r\nUnmanaged HCI Event: "), 0x80);
+                                        D_PrintHex<uint8_t > (hcibuf[0], 0x80);
+                                }
+                                break;
+#endif
+                } // Switch
+        }
+#ifdef EXTRADEBUG
+        else {
+                Notify(PSTR("\r\nHCI event error: "), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+        }
+#endif
+}
+
+/* Poll Bluetooth and print result */
+void BTD::HCI_task() {
+        switch(hci_state) {
+                case HCI_INIT_STATE:
+                        hci_counter++;
+                        if(hci_counter > hci_num_reset_loops) { // wait until we have looped x times to clear any old events
+                                hci_reset();
+                                hci_state = HCI_RESET_STATE;
+                                hci_counter = 0;
+                        }
+                        break;
+
+                case HCI_RESET_STATE:
+                        hci_counter++;
+                        if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {
+                                hci_counter = 0;
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHCI Reset complete"), 0x80);
+#endif
+                                hci_state = HCI_CLASS_STATE;
+                                hci_write_class_of_device();
+                        } else if(hci_counter > hci_num_reset_loops) {
+                                hci_num_reset_loops *= 10;
+                                if(hci_num_reset_loops > 2000)
+                                        hci_num_reset_loops = 2000;
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nNo response to HCI Reset"), 0x80);
+#endif
+                                hci_state = HCI_INIT_STATE;
+                                hci_counter = 0;
+                        }
+                        break;
+
+                case HCI_CLASS_STATE:
+                        if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nWrite class of device"), 0x80);
+#endif
+                                hci_state = HCI_BDADDR_STATE;
+                                hci_read_bdaddr();
+                        }
+                        break;
+
+                case HCI_BDADDR_STATE:
+                        if(hci_check_flag(HCI_FLAG_READ_BDADDR)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nLocal Bluetooth Address: "), 0x80);
+                                for(int8_t i = 5; i > 0; i--) {
+                                        D_PrintHex<uint8_t > (my_bdaddr[i], 0x80);
+                                        Notify(PSTR(":"), 0x80);
+                                }
+                                D_PrintHex<uint8_t > (my_bdaddr[0], 0x80);
+#endif
+                                hci_read_local_version_information();
+                                hci_state = HCI_LOCAL_VERSION_STATE;
+                        }
+                        break;
+
+                case HCI_LOCAL_VERSION_STATE: // The local version is used by the PS3BT class
+                        if(hci_check_flag(HCI_FLAG_READ_VERSION)) {
+                                if(btdName != NULL) {
+                                        hci_set_local_name(btdName);
+                                        hci_state = HCI_SET_NAME_STATE;
+                                } else
+                                        hci_state = HCI_CHECK_DEVICE_SERVICE;
+                        }
+                        break;
+
+                case HCI_SET_NAME_STATE:
+                        if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nThe name is set to: "), 0x80);
+                                NotifyStr(btdName, 0x80);
+#endif
+                                hci_state = HCI_CHECK_DEVICE_SERVICE;
+                        }
+                        break;
+
+                case HCI_CHECK_DEVICE_SERVICE:
+                        if(pairWithHIDDevice || pairWithWii) { // Check if it should try to connect to a Wiimote
+#ifdef DEBUG_USB_HOST
+                                if(pairWithWii)
+                                        Notify(PSTR("\r\nStarting inquiry\r\nPress 1 & 2 on the Wiimote\r\nOr press the SYNC button if you are using a Wii U Pro Controller or a Wii Balance Board"), 0x80);
+                                else
+                                        Notify(PSTR("\r\nPlease enable discovery of your device"), 0x80);
+#endif
+                                hci_inquiry();
+                                hci_state = HCI_INQUIRY_STATE;
+                        } else
+                                hci_state = HCI_SCANNING_STATE; // Don't try to connect to a Wiimote
+                        break;
+
+                case HCI_INQUIRY_STATE:
+                        if(hci_check_flag(HCI_FLAG_DEVICE_FOUND)) {
+                                hci_inquiry_cancel(); // Stop inquiry
+#ifdef DEBUG_USB_HOST
+                                if(pairWithWii)
+                                        Notify(PSTR("\r\nWiimote found"), 0x80);
+                                else
+                                        Notify(PSTR("\r\nHID device found"), 0x80);
+
+                                Notify(PSTR("\r\nNow just create the instance like so:"), 0x80);
+                                if(pairWithWii)
+                                        Notify(PSTR("\r\nWII Wii(&Btd);"), 0x80);
+                                else
+                                        Notify(PSTR("\r\nBTHID bthid(&Btd);"), 0x80);
+
+                                Notify(PSTR("\r\nAnd then press any button on the "), 0x80);
+                                if(pairWithWii)
+                                        Notify(PSTR("Wiimote"), 0x80);
+                                else
+                                        Notify(PSTR("device"), 0x80);
+#endif
+                                if(checkRemoteName) {
+                                        hci_remote_name(); // We need to know the name to distinguish between the Wiimote, the new Wiimote with Motion Plus inside, a Wii U Pro Controller and a Wii Balance Board
+                                        hci_state = HCI_REMOTE_NAME_STATE;
+                                } else
+                                        hci_state = HCI_CONNECT_DEVICE_STATE;
+                        }
+                        break;
+
+                case HCI_CONNECT_DEVICE_STATE:
+                        if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {
+#ifdef DEBUG_USB_HOST
+                                if(pairWithWii)
+                                        Notify(PSTR("\r\nConnecting to Wiimote"), 0x80);
+                                else
+                                        Notify(PSTR("\r\nConnecting to HID device"), 0x80);
+#endif
+                                checkRemoteName = false;
+                                hci_connect();
+                                hci_state = HCI_CONNECTED_DEVICE_STATE;
+                        }
+                        break;
+
+                case HCI_CONNECTED_DEVICE_STATE:
+                        if(hci_check_flag(HCI_FLAG_CONNECT_EVENT)) {
+                                if(hci_check_flag(HCI_FLAG_CONNECT_COMPLETE)) {
+#ifdef DEBUG_USB_HOST
+                                        if(pairWithWii)
+                                                Notify(PSTR("\r\nConnected to Wiimote"), 0x80);
+                                        else
+                                                Notify(PSTR("\r\nConnected to HID device"), 0x80);
+#endif
+                                        hci_authentication_request(); // This will start the pairing with the Wiimote
+                                        hci_state = HCI_SCANNING_STATE;
+                                } else {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nTrying to connect one more time..."), 0x80);
+#endif
+                                        hci_connect(); // Try to connect one more time
+                                }
+                        }
+                        break;
+
+                case HCI_SCANNING_STATE:
+                        if(!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nWait For Incoming Connection Request"), 0x80);
+#endif
+                                hci_write_scan_enable();
+                                waitingForConnection = true;
+                                hci_state = HCI_CONNECT_IN_STATE;
+                        }
+                        break;
+
+                case HCI_CONNECT_IN_STATE:
+                        if(hci_check_flag(HCI_FLAG_INCOMING_REQUEST)) {
+                                waitingForConnection = false;
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nIncoming Connection Request"), 0x80);
+#endif
+                                hci_remote_name();
+                                hci_state = HCI_REMOTE_NAME_STATE;
+                        } else if(hci_check_flag(HCI_FLAG_DISCONNECT_COMPLETE))
+                                hci_state = HCI_DISCONNECT_STATE;
+                        break;
+
+                case HCI_REMOTE_NAME_STATE:
+                        if(hci_check_flag(HCI_FLAG_REMOTE_NAME_COMPLETE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nRemote Name: "), 0x80);
+                                for(uint8_t i = 0; i < strlen(remote_name); i++)
+                                        Notifyc(remote_name[i], 0x80);
+#endif
+                                if(strncmp((const char*)remote_name, "Nintendo", 8) == 0) {
+                                        incomingWii = true;
+                                        motionPlusInside = false;
+                                        wiiUProController = false;
+                                        pairWiiUsingSync = false;
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nWiimote is connecting"), 0x80);
+#endif
+                                        if(strncmp((const char*)remote_name, "Nintendo RVL-CNT-01-TR", 22) == 0) {
+#ifdef DEBUG_USB_HOST
+                                                Notify(PSTR(" with Motion Plus Inside"), 0x80);
+#endif
+                                                motionPlusInside = true;
+                                        } else if(strncmp((const char*)remote_name, "Nintendo RVL-CNT-01-UC", 22) == 0) {
+#ifdef DEBUG_USB_HOST
+                                                Notify(PSTR(" - Wii U Pro Controller"), 0x80);
+#endif
+                                                wiiUProController = motionPlusInside = pairWiiUsingSync = true;
+                                        } else if(strncmp((const char*)remote_name, "Nintendo RVL-WBC-01", 19) == 0) {
+#ifdef DEBUG_USB_HOST
+                                                Notify(PSTR(" - Wii Balance Board"), 0x80);
+#endif
+                                                pairWiiUsingSync = true;
+                                        }
+                                }
+                                if(classOfDevice[2] == 0 && classOfDevice[1] == 0x25 && classOfDevice[0] == 0x08 && strncmp((const char*)remote_name, "Wireless Controller", 19) == 0) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nPS4 controller is connecting"), 0x80);
+#endif
+                                        incomingPS4 = true;
+                                }
+                                if(pairWithWii && checkRemoteName)
+                                        hci_state = HCI_CONNECT_DEVICE_STATE;
+                                else {
+                                        hci_accept_connection();
+                                        hci_state = HCI_CONNECTED_STATE;
+                                }
+                        }
+                        break;
+
+                case HCI_CONNECTED_STATE:
+                        if(hci_check_flag(HCI_FLAG_CONNECT_COMPLETE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nConnected to Device: "), 0x80);
+                                for(int8_t i = 5; i > 0; i--) {
+                                        D_PrintHex<uint8_t > (disc_bdaddr[i], 0x80);
+                                        Notify(PSTR(":"), 0x80);
+                                }
+                                D_PrintHex<uint8_t > (disc_bdaddr[0], 0x80);
+#endif
+                                if(incomingPS4)
+                                        connectToHIDDevice = true; // We should always connect to the PS4 controller
+
+                                // Clear these flags for a new connection
+                                l2capConnectionClaimed = false;
+                                sdpConnectionClaimed = false;
+                                rfcommConnectionClaimed = false;
+
+                                hci_event_flag = 0;
+                                hci_state = HCI_DONE_STATE;
+                        }
+                        break;
+
+                case HCI_DONE_STATE:
+                        hci_counter++;
+                        if(hci_counter > 1000) { // Wait until we have looped 1000 times to make sure that the L2CAP connection has been started
+                                hci_counter = 0;
+                                hci_state = HCI_SCANNING_STATE;
+                        }
+                        break;
+
+                case HCI_DISCONNECT_STATE:
+                        if(hci_check_flag(HCI_FLAG_DISCONNECT_COMPLETE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHCI Disconnected from Device"), 0x80);
+#endif
+                                hci_event_flag = 0; // Clear all flags
+
+                                // Reset all buffers
+                                memset(hcibuf, 0, BULK_MAXPKTSIZE);
+                                memset(l2capinbuf, 0, BULK_MAXPKTSIZE);
+
+                                connectToWii = incomingWii = pairWithWii = false;
+                                connectToHIDDevice = incomingHIDDevice = pairWithHIDDevice = checkRemoteName = false;
+                                incomingPS4 = false;
+
+                                hci_state = HCI_SCANNING_STATE;
+                        }
+                        break;
+                default:
+                        break;
+        }
+}
+
+void BTD::ACL_event_task() {
+        uint16_t length = BULK_MAXPKTSIZE;
+        uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_DATAIN_PIPE ].epAddr, &length, l2capinbuf, pollInterval); // Input on endpoint 2
+
+        if(!rcode) { // Check for errors
+                if(length > 0) { // Check if any data was read
+                        for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) {
+                                if(btService[i])
+                                        btService[i]->ACLData(l2capinbuf);
+                        }
+                }
+        }
+#ifdef EXTRADEBUG
+        else if(rcode != hrNAK) {
+                Notify(PSTR("\r\nACL data in error: "), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+        }
+#endif
+        for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++)
+                if(btService[i])
+                        btService[i]->Run();
+}
+
+/************************************************************/
+/*                    HCI Commands                        */
+
+/************************************************************/
+void BTD::HCI_Command(uint8_t* data, uint16_t nbytes) {
+        hci_clear_flag(HCI_FLAG_CMD_COMPLETE);
+        pUsb->ctrlReq(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bmREQ_HCI_OUT, 0x00, 0x00, 0x00, 0x00, nbytes, nbytes, data, NULL);
+}
+
+void BTD::hci_reset() {
+        hci_event_flag = 0; // Clear all the flags
+        hcibuf[0] = 0x03; // HCI OCF = 3
+        hcibuf[1] = 0x03 << 2; // HCI OGF = 3
+        hcibuf[2] = 0x00;
+
+        HCI_Command(hcibuf, 3);
+}
+
+void BTD::hci_write_scan_enable() {
+        hci_clear_flag(HCI_FLAG_INCOMING_REQUEST);
+        hcibuf[0] = 0x1A; // HCI OCF = 1A
+        hcibuf[1] = 0x03 << 2; // HCI OGF = 3
+        hcibuf[2] = 0x01; // parameter length = 1
+        if(btdName != NULL)
+                hcibuf[3] = 0x03; // Inquiry Scan enabled. Page Scan enabled.
+        else
+                hcibuf[3] = 0x02; // Inquiry Scan disabled. Page Scan enabled.
+
+        HCI_Command(hcibuf, 4);
+}
+
+void BTD::hci_write_scan_disable() {
+        hcibuf[0] = 0x1A; // HCI OCF = 1A
+        hcibuf[1] = 0x03 << 2; // HCI OGF = 3
+        hcibuf[2] = 0x01; // parameter length = 1
+        hcibuf[3] = 0x00; // Inquiry Scan disabled. Page Scan disabled.
+
+        HCI_Command(hcibuf, 4);
+}
+
+void BTD::hci_read_bdaddr() {
+        hci_clear_flag(HCI_FLAG_READ_BDADDR);
+        hcibuf[0] = 0x09; // HCI OCF = 9
+        hcibuf[1] = 0x04 << 2; // HCI OGF = 4
+        hcibuf[2] = 0x00;
+
+        HCI_Command(hcibuf, 3);
+}
+
+void BTD::hci_read_local_version_information() {
+        hci_clear_flag(HCI_FLAG_READ_VERSION);
+        hcibuf[0] = 0x01; // HCI OCF = 1
+        hcibuf[1] = 0x04 << 2; // HCI OGF = 4
+        hcibuf[2] = 0x00;
+
+        HCI_Command(hcibuf, 3);
+}
+
+void BTD::hci_accept_connection() {
+        hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE);
+        hcibuf[0] = 0x09; // HCI OCF = 9
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x07; // parameter length 7
+        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
+        hcibuf[4] = disc_bdaddr[1];
+        hcibuf[5] = disc_bdaddr[2];
+        hcibuf[6] = disc_bdaddr[3];
+        hcibuf[7] = disc_bdaddr[4];
+        hcibuf[8] = disc_bdaddr[5];
+        hcibuf[9] = 0x00; // Switch role to master
+
+        HCI_Command(hcibuf, 10);
+}
+
+void BTD::hci_remote_name() {
+        hci_clear_flag(HCI_FLAG_REMOTE_NAME_COMPLETE);
+        hcibuf[0] = 0x19; // HCI OCF = 19
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x0A; // parameter length = 10
+        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
+        hcibuf[4] = disc_bdaddr[1];
+        hcibuf[5] = disc_bdaddr[2];
+        hcibuf[6] = disc_bdaddr[3];
+        hcibuf[7] = disc_bdaddr[4];
+        hcibuf[8] = disc_bdaddr[5];
+        hcibuf[9] = 0x01; // Page Scan Repetition Mode
+        hcibuf[10] = 0x00; // Reserved
+        hcibuf[11] = 0x00; // Clock offset - low byte
+        hcibuf[12] = 0x00; // Clock offset - high byte
+
+        HCI_Command(hcibuf, 13);
+}
+
+void BTD::hci_set_local_name(const char* name) {
+        hcibuf[0] = 0x13; // HCI OCF = 13
+        hcibuf[1] = 0x03 << 2; // HCI OGF = 3
+        hcibuf[2] = strlen(name) + 1; // parameter length = the length of the string + end byte
+        uint8_t i;
+        for(i = 0; i < strlen(name); i++)
+                hcibuf[i + 3] = name[i];
+        hcibuf[i + 3] = 0x00; // End of string
+
+        HCI_Command(hcibuf, 4 + strlen(name));
+}
+
+void BTD::hci_inquiry() {
+        hci_clear_flag(HCI_FLAG_DEVICE_FOUND);
+        hcibuf[0] = 0x01;
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x05; // Parameter Total Length = 5
+        hcibuf[3] = 0x33; // LAP: Genera/Unlimited Inquiry Access Code (GIAC = 0x9E8B33) - see https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
+        hcibuf[4] = 0x8B;
+        hcibuf[5] = 0x9E;
+        hcibuf[6] = 0x30; // Inquiry time = 61.44 sec (maximum)
+        hcibuf[7] = 0x0A; // 10 number of responses
+
+        HCI_Command(hcibuf, 8);
+}
+
+void BTD::hci_inquiry_cancel() {
+        hcibuf[0] = 0x02;
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x00; // Parameter Total Length = 0
+
+        HCI_Command(hcibuf, 3);
+}
+
+void BTD::hci_connect() {
+        hci_connect(disc_bdaddr); // Use last discovered device
+}
+
+void BTD::hci_connect(uint8_t *bdaddr) {
+        hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE | HCI_FLAG_CONNECT_EVENT);
+        hcibuf[0] = 0x05;
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x0D; // parameter Total Length = 13
+        hcibuf[3] = bdaddr[0]; // 6 octet bdaddr (LSB)
+        hcibuf[4] = bdaddr[1];
+        hcibuf[5] = bdaddr[2];
+        hcibuf[6] = bdaddr[3];
+        hcibuf[7] = bdaddr[4];
+        hcibuf[8] = bdaddr[5];
+        hcibuf[9] = 0x18; // DM1 or DH1 may be used
+        hcibuf[10] = 0xCC; // DM3, DH3, DM5, DH5 may be used
+        hcibuf[11] = 0x01; // Page repetition mode R1
+        hcibuf[12] = 0x00; // Reserved
+        hcibuf[13] = 0x00; // Clock offset
+        hcibuf[14] = 0x00; // Invalid clock offset
+        hcibuf[15] = 0x00; // Do not allow role switch
+
+        HCI_Command(hcibuf, 16);
+}
+
+void BTD::hci_pin_code_request_reply() {
+        hcibuf[0] = 0x0D; // HCI OCF = 0D
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x17; // parameter length 23
+        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
+        hcibuf[4] = disc_bdaddr[1];
+        hcibuf[5] = disc_bdaddr[2];
+        hcibuf[6] = disc_bdaddr[3];
+        hcibuf[7] = disc_bdaddr[4];
+        hcibuf[8] = disc_bdaddr[5];
+        if(pairWithWii) {
+                hcibuf[9] = 6; // Pin length is the length of the Bluetooth address
+                if(pairWiiUsingSync) {
+#ifdef DEBUG_USB_HOST
+                        Notify(PSTR("\r\nPairing with Wii controller via SYNC"), 0x80);
+#endif
+                        for(uint8_t i = 0; i < 6; i++)
+                                hcibuf[10 + i] = my_bdaddr[i]; // The pin is the Bluetooth dongles Bluetooth address backwards
+                } else {
+                        for(uint8_t i = 0; i < 6; i++)
+                                hcibuf[10 + i] = disc_bdaddr[i]; // The pin is the Wiimote's Bluetooth address backwards
+                }
+                for(uint8_t i = 16; i < 26; i++)
+                        hcibuf[i] = 0x00; // The rest should be 0
+        } else {
+                hcibuf[9] = strlen(btdPin); // Length of pin
+                uint8_t i;
+                for(i = 0; i < strlen(btdPin); i++) // The maximum size of the pin is 16
+                        hcibuf[i + 10] = btdPin[i];
+                for(; i < 16; i++)
+                        hcibuf[i + 10] = 0x00; // The rest should be 0
+        }
+
+        HCI_Command(hcibuf, 26);
+}
+
+void BTD::hci_pin_code_negative_request_reply() {
+        hcibuf[0] = 0x0E; // HCI OCF = 0E
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x06; // parameter length 6
+        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
+        hcibuf[4] = disc_bdaddr[1];
+        hcibuf[5] = disc_bdaddr[2];
+        hcibuf[6] = disc_bdaddr[3];
+        hcibuf[7] = disc_bdaddr[4];
+        hcibuf[8] = disc_bdaddr[5];
+
+        HCI_Command(hcibuf, 9);
+}
+
+void BTD::hci_link_key_request_negative_reply() {
+        hcibuf[0] = 0x0C; // HCI OCF = 0C
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x06; // parameter length 6
+        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr
+        hcibuf[4] = disc_bdaddr[1];
+        hcibuf[5] = disc_bdaddr[2];
+        hcibuf[6] = disc_bdaddr[3];
+        hcibuf[7] = disc_bdaddr[4];
+        hcibuf[8] = disc_bdaddr[5];
+
+        HCI_Command(hcibuf, 9);
+}
+
+void BTD::hci_authentication_request() {
+        hcibuf[0] = 0x11; // HCI OCF = 11
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x02; // parameter length = 2
+        hcibuf[3] = (uint8_t)(hci_handle & 0xFF); //connection handle - low byte
+        hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F); //connection handle - high byte
+
+        HCI_Command(hcibuf, 5);
+}
+
+void BTD::hci_disconnect(uint16_t handle) { // This is called by the different services
+        hci_clear_flag(HCI_FLAG_DISCONNECT_COMPLETE);
+        hcibuf[0] = 0x06; // HCI OCF = 6
+        hcibuf[1] = 0x01 << 2; // HCI OGF = 1
+        hcibuf[2] = 0x03; // parameter length = 3
+        hcibuf[3] = (uint8_t)(handle & 0xFF); //connection handle - low byte
+        hcibuf[4] = (uint8_t)((handle >> 8) & 0x0F); //connection handle - high byte
+        hcibuf[5] = 0x13; // reason
+
+        HCI_Command(hcibuf, 6);
+}
+
+void BTD::hci_write_class_of_device() { // See http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html
+        hcibuf[0] = 0x24; // HCI OCF = 24
+        hcibuf[1] = 0x03 << 2; // HCI OGF = 3
+        hcibuf[2] = 0x03; // parameter length = 3
+        hcibuf[3] = 0x04; // Robot
+        hcibuf[4] = 0x08; // Toy
+        hcibuf[5] = 0x00;
+
+        HCI_Command(hcibuf, 6);
+}
+/*******************************************************************
+ *                                                                 *
+ *                        HCI ACL Data Packet                      *
+ *                                                                 *
+ *   buf[0]          buf[1]          buf[2]          buf[3]
+ *   0       4       8    11 12      16              24            31 MSB
+ *  .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
+ *  |      HCI Handle       |PB |BC |       Data Total Length       |   HCI ACL Data Packet
+ *  .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
+ *
+ *   buf[4]          buf[5]          buf[6]          buf[7]
+ *   0               8               16                            31 MSB
+ *  .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
+ *  |            Length             |          Channel ID           |   Basic L2CAP header
+ *  .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
+ *
+ *   buf[8]          buf[9]          buf[10]         buf[11]
+ *   0               8               16                            31 MSB
+ *  .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.
+ *  |     Code      |  Identifier   |            Length             |   Control frame (C-frame)
+ *  .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.   (signaling packet format)
+ */
+/************************************************************/
+/*                    L2CAP Commands                        */
+
+/************************************************************/
+void BTD::L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow, uint8_t channelHigh) {
+        uint8_t buf[8 + nbytes];
+        buf[0] = (uint8_t)(handle & 0xff); // HCI handle with PB,BC flag
+        buf[1] = (uint8_t)(((handle >> 8) & 0x0f) | 0x20);
+        buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length
+        buf[3] = (uint8_t)((4 + nbytes) >> 8);
+        buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length
+        buf[5] = (uint8_t)(nbytes >> 8);
+        buf[6] = channelLow;
+        buf[7] = channelHigh;
+
+        for(uint16_t i = 0; i < nbytes; i++) // L2CAP C-frame
+                buf[8 + i] = data[i];
+
+        uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf);
+        if(rcode) {
+                delay(100); // This small delay prevents it from overflowing if it fails
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nError sending L2CAP message: 0x"), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+                Notify(PSTR(" - Channel ID: "), 0x80);
+                D_PrintHex<uint8_t > (channelHigh, 0x80);
+                Notify(PSTR(" "), 0x80);
+                D_PrintHex<uint8_t > (channelLow, 0x80);
+#endif
+        }
+}
+
+void BTD::l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm) {
+        l2capoutbuf[0] = L2CAP_CMD_CONNECTION_REQUEST; // Code
+        l2capoutbuf[1] = rxid; // Identifier
+        l2capoutbuf[2] = 0x04; // Length
+        l2capoutbuf[3] = 0x00;
+        l2capoutbuf[4] = (uint8_t)(psm & 0xff); // PSM
+        l2capoutbuf[5] = (uint8_t)(psm >> 8);
+        l2capoutbuf[6] = scid[0]; // Source CID
+        l2capoutbuf[7] = scid[1];
+
+        L2CAP_Command(handle, l2capoutbuf, 8);
+}
+
+void BTD::l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result) {
+        l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE; // Code
+        l2capoutbuf[1] = rxid; // Identifier
+        l2capoutbuf[2] = 0x08; // Length
+        l2capoutbuf[3] = 0x00;
+        l2capoutbuf[4] = dcid[0]; // Destination CID
+        l2capoutbuf[5] = dcid[1];
+        l2capoutbuf[6] = scid[0]; // Source CID
+        l2capoutbuf[7] = scid[1];
+        l2capoutbuf[8] = result; // Result: Pending or Success
+        l2capoutbuf[9] = 0x00;
+        l2capoutbuf[10] = 0x00; // No further information
+        l2capoutbuf[11] = 0x00;
+
+        L2CAP_Command(handle, l2capoutbuf, 12);
+}
+
+void BTD::l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid) {
+        l2capoutbuf[0] = L2CAP_CMD_CONFIG_REQUEST; // Code
+        l2capoutbuf[1] = rxid; // Identifier
+        l2capoutbuf[2] = 0x08; // Length
+        l2capoutbuf[3] = 0x00;
+        l2capoutbuf[4] = dcid[0]; // Destination CID
+        l2capoutbuf[5] = dcid[1];
+        l2capoutbuf[6] = 0x00; // Flags
+        l2capoutbuf[7] = 0x00;
+        l2capoutbuf[8] = 0x01; // Config Opt: type = MTU (Maximum Transmission Unit) - Hint
+        l2capoutbuf[9] = 0x02; // Config Opt: length
+        l2capoutbuf[10] = 0xFF; // MTU
+        l2capoutbuf[11] = 0xFF;
+
+        L2CAP_Command(handle, l2capoutbuf, 12);
+}
+
+void BTD::l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid) {
+        l2capoutbuf[0] = L2CAP_CMD_CONFIG_RESPONSE; // Code
+        l2capoutbuf[1] = rxid; // Identifier
+        l2capoutbuf[2] = 0x0A; // Length
+        l2capoutbuf[3] = 0x00;
+        l2capoutbuf[4] = scid[0]; // Source CID
+        l2capoutbuf[5] = scid[1];
+        l2capoutbuf[6] = 0x00; // Flag
+        l2capoutbuf[7] = 0x00;
+        l2capoutbuf[8] = 0x00; // Result
+        l2capoutbuf[9] = 0x00;
+        l2capoutbuf[10] = 0x01; // Config
+        l2capoutbuf[11] = 0x02;
+        l2capoutbuf[12] = 0xA0;
+        l2capoutbuf[13] = 0x02;
+
+        L2CAP_Command(handle, l2capoutbuf, 14);
+}
+
+void BTD::l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) {
+        l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_REQUEST; // Code
+        l2capoutbuf[1] = rxid; // Identifier
+        l2capoutbuf[2] = 0x04; // Length
+        l2capoutbuf[3] = 0x00;
+        l2capoutbuf[4] = dcid[0];
+        l2capoutbuf[5] = dcid[1];
+        l2capoutbuf[6] = scid[0];
+        l2capoutbuf[7] = scid[1];
+
+        L2CAP_Command(handle, l2capoutbuf, 8);
+}
+
+void BTD::l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) {
+        l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE; // Code
+        l2capoutbuf[1] = rxid; // Identifier
+        l2capoutbuf[2] = 0x04; // Length
+        l2capoutbuf[3] = 0x00;
+        l2capoutbuf[4] = dcid[0];
+        l2capoutbuf[5] = dcid[1];
+        l2capoutbuf[6] = scid[0];
+        l2capoutbuf[7] = scid[1];
+
+        L2CAP_Command(handle, l2capoutbuf, 8);
+}
+
+void BTD::l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh) {
+        l2capoutbuf[0] = L2CAP_CMD_INFORMATION_RESPONSE; // Code
+        l2capoutbuf[1] = rxid; // Identifier
+        l2capoutbuf[2] = 0x08; // Length
+        l2capoutbuf[3] = 0x00;
+        l2capoutbuf[4] = infoTypeLow;
+        l2capoutbuf[5] = infoTypeHigh;
+        l2capoutbuf[6] = 0x00; // Result = success
+        l2capoutbuf[7] = 0x00; // Result = success
+        l2capoutbuf[8] = 0x00;
+        l2capoutbuf[9] = 0x00;
+        l2capoutbuf[10] = 0x00;
+        l2capoutbuf[11] = 0x00;
+
+        L2CAP_Command(handle, l2capoutbuf, 12);
+}
+
+/* PS3 Commands - only set Bluetooth address is implemented in this library */
+void BTD::setBdaddr(uint8_t* bdaddr) {
+        /* Set the internal Bluetooth address */
+        uint8_t buf[8];
+        buf[0] = 0x01;
+        buf[1] = 0x00;
+
+        for(uint8_t i = 0; i < 6; i++)
+                buf[i + 2] = bdaddr[5 - i]; // Copy into buffer, has to be written reversed, so it is MSB first
+
+        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
+        pUsb->ctrlReq(bAddress, epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);
+}
+
+void BTD::setMoveBdaddr(uint8_t* bdaddr) {
+        /* Set the internal Bluetooth address */
+        uint8_t buf[11];
+        buf[0] = 0x05;
+        buf[7] = 0x10;
+        buf[8] = 0x01;
+        buf[9] = 0x02;
+        buf[10] = 0x12;
+
+        for(uint8_t i = 0; i < 6; i++)
+                buf[i + 1] = bdaddr[i];
+
+        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
+        pUsb->ctrlReq(bAddress, epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00, 11, 11, buf, NULL);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/BTD.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,625 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _btd_h_
+#define _btd_h_
+
+#include "Usb.h"
+#include "usbhid.h"
+
+//PID and VID of the Sony PS3 devices
+#define PS3_VID                 0x054C  // Sony Corporation
+#define PS3_PID                 0x0268  // PS3 Controller DualShock 3
+#define PS3NAVIGATION_PID       0x042F  // Navigation controller
+#define PS3MOVE_PID             0x03D5  // Motion controller
+
+// These dongles do not present themselves correctly, so we have to check for them manually
+#define IOGEAR_GBU521_VID       0x0A5C
+#define IOGEAR_GBU521_PID       0x21E8
+#define BELKIN_F8T065BF_VID     0x050D
+#define BELKIN_F8T065BF_PID     0x065A
+
+/* Bluetooth dongle data taken from descriptors */
+#define BULK_MAXPKTSIZE         64 // Max size for ACL data
+
+// Used in control endpoint header for HCI Commands
+#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
+
+/* Bluetooth HCI states for hci_task() */
+#define HCI_INIT_STATE                  0
+#define HCI_RESET_STATE                 1
+#define HCI_CLASS_STATE                 2
+#define HCI_BDADDR_STATE                3
+#define HCI_LOCAL_VERSION_STATE         4
+#define HCI_SET_NAME_STATE              5
+#define HCI_CHECK_DEVICE_SERVICE        6
+
+#define HCI_INQUIRY_STATE               7 // These three states are only used if it should pair and connect to a device
+#define HCI_CONNECT_DEVICE_STATE        8
+#define HCI_CONNECTED_DEVICE_STATE      9
+
+#define HCI_SCANNING_STATE              10
+#define HCI_CONNECT_IN_STATE            11
+#define HCI_REMOTE_NAME_STATE           12
+#define HCI_CONNECTED_STATE             13
+#define HCI_DISABLE_SCAN_STATE          14
+#define HCI_DONE_STATE                  15
+#define HCI_DISCONNECT_STATE            16
+
+/* HCI event flags*/
+#define HCI_FLAG_CMD_COMPLETE           (1UL << 0)
+#define HCI_FLAG_CONNECT_COMPLETE       (1UL << 1)
+#define HCI_FLAG_DISCONNECT_COMPLETE    (1UL << 2)
+#define HCI_FLAG_REMOTE_NAME_COMPLETE   (1UL << 3)
+#define HCI_FLAG_INCOMING_REQUEST       (1UL << 4)
+#define HCI_FLAG_READ_BDADDR            (1UL << 5)
+#define HCI_FLAG_READ_VERSION           (1UL << 6)
+#define HCI_FLAG_DEVICE_FOUND           (1UL << 7)
+#define HCI_FLAG_CONNECT_EVENT          (1UL << 8)
+
+/* Macros for HCI event flag tests */
+#define hci_check_flag(flag) (hci_event_flag & (flag))
+#define hci_set_flag(flag) (hci_event_flag |= (flag))
+#define hci_clear_flag(flag) (hci_event_flag &= ~(flag))
+
+/* HCI Events managed */
+#define EV_INQUIRY_COMPLETE                             0x01
+#define EV_INQUIRY_RESULT                               0x02
+#define EV_CONNECT_COMPLETE                             0x03
+#define EV_INCOMING_CONNECT                             0x04
+#define EV_DISCONNECT_COMPLETE                          0x05
+#define EV_AUTHENTICATION_COMPLETE                      0x06
+#define EV_REMOTE_NAME_COMPLETE                         0x07
+#define EV_ENCRYPTION_CHANGE                            0x08
+#define EV_CHANGE_CONNECTION_LINK                       0x09
+#define EV_ROLE_CHANGED                                 0x12
+#define EV_NUM_COMPLETE_PKT                             0x13
+#define EV_PIN_CODE_REQUEST                             0x16
+#define EV_LINK_KEY_REQUEST                             0x17
+#define EV_LINK_KEY_NOTIFICATION                        0x18
+#define EV_DATA_BUFFER_OVERFLOW                         0x1A
+#define EV_MAX_SLOTS_CHANGE                             0x1B
+#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE     0x0C
+#define EV_QOS_SETUP_COMPLETE                           0x0D
+#define EV_COMMAND_COMPLETE                             0x0E
+#define EV_COMMAND_STATUS                               0x0F
+#define EV_LOOPBACK_COMMAND                             0x19
+#define EV_PAGE_SCAN_REP_MODE                           0x20
+
+/* Bluetooth states for the different Bluetooth drivers */
+#define L2CAP_WAIT                      0
+#define L2CAP_DONE                      1
+
+/* Used for HID Control channel */
+#define L2CAP_CONTROL_CONNECT_REQUEST   2
+#define L2CAP_CONTROL_CONFIG_REQUEST    3
+#define L2CAP_CONTROL_SUCCESS           4
+#define L2CAP_CONTROL_DISCONNECT        5
+
+/* Used for HID Interrupt channel */
+#define L2CAP_INTERRUPT_SETUP           6
+#define L2CAP_INTERRUPT_CONNECT_REQUEST 7
+#define L2CAP_INTERRUPT_CONFIG_REQUEST  8
+#define L2CAP_INTERRUPT_DISCONNECT      9
+
+/* Used for SDP channel */
+#define L2CAP_SDP_WAIT                  10
+#define L2CAP_SDP_SUCCESS               11
+
+/* Used for RFCOMM channel */
+#define L2CAP_RFCOMM_WAIT               12
+#define L2CAP_RFCOMM_SUCCESS            13
+
+#define L2CAP_DISCONNECT_RESPONSE       14 // Used for both SDP and RFCOMM channel
+
+/* Bluetooth states used by some drivers */
+#define TURN_ON_LED                     17
+#define PS3_ENABLE_SIXAXIS              18
+#define WII_CHECK_MOTION_PLUS_STATE     19
+#define WII_CHECK_EXTENSION_STATE       20
+#define WII_INIT_MOTION_PLUS_STATE      21
+
+/* L2CAP event flags for HID Control channel */
+#define L2CAP_FLAG_CONNECTION_CONTROL_REQUEST           (1UL << 0)
+#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS               (1UL << 1)
+#define L2CAP_FLAG_CONTROL_CONNECTED                    (1UL << 2)
+#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE          (1UL << 3)
+
+/* L2CAP event flags for HID Interrupt channel */
+#define L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST         (1UL << 4)
+#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS             (1UL << 5)
+#define L2CAP_FLAG_INTERRUPT_CONNECTED                  (1UL << 6)
+#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE        (1UL << 7)
+
+/* L2CAP event flags for SDP channel */
+#define L2CAP_FLAG_CONNECTION_SDP_REQUEST               (1UL << 8)
+#define L2CAP_FLAG_CONFIG_SDP_SUCCESS                   (1UL << 9)
+#define L2CAP_FLAG_DISCONNECT_SDP_REQUEST               (1UL << 10)
+
+/* L2CAP event flags for RFCOMM channel */
+#define L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST            (1UL << 11)
+#define L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS                (1UL << 12)
+#define L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST            (1UL << 13)
+
+#define L2CAP_FLAG_DISCONNECT_RESPONSE                  (1UL << 14)
+
+/* Macros for L2CAP event flag tests */
+#define l2cap_check_flag(flag) (l2cap_event_flag & (flag))
+#define l2cap_set_flag(flag) (l2cap_event_flag |= (flag))
+#define l2cap_clear_flag(flag) (l2cap_event_flag &= ~(flag))
+
+/* L2CAP signaling commands */
+#define L2CAP_CMD_COMMAND_REJECT        0x01
+#define L2CAP_CMD_CONNECTION_REQUEST    0x02
+#define L2CAP_CMD_CONNECTION_RESPONSE   0x03
+#define L2CAP_CMD_CONFIG_REQUEST        0x04
+#define L2CAP_CMD_CONFIG_RESPONSE       0x05
+#define L2CAP_CMD_DISCONNECT_REQUEST    0x06
+#define L2CAP_CMD_DISCONNECT_RESPONSE   0x07
+#define L2CAP_CMD_INFORMATION_REQUEST   0x0A
+#define L2CAP_CMD_INFORMATION_RESPONSE  0x0B
+
+// Used For Connection Response - Remember to Include High Byte
+#define PENDING     0x01
+#define SUCCESSFUL  0x00
+
+/* Bluetooth L2CAP PSM - see http://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm */
+#define SDP_PSM         0x01 // Service Discovery Protocol PSM Value
+#define RFCOMM_PSM      0x03 // RFCOMM PSM Value
+#define HID_CTRL_PSM    0x11 // HID_Control PSM Value
+#define HID_INTR_PSM    0x13 // HID_Interrupt PSM Value
+
+// Used to determine if it is a Bluetooth dongle
+#define WI_SUBCLASS_RF      0x01 // RF Controller
+#define WI_PROTOCOL_BT      0x01 // Bluetooth Programming Interface
+
+#define BTD_MAX_ENDPOINTS   4
+#define BTD_NUM_SERVICES    4 // Max number of Bluetooth services - if you need more than 4 simply increase this number
+
+#define PAIR    1
+
+class BluetoothService;
+
+/**
+ * The Bluetooth Dongle class will take care of all the USB communication
+ * and then pass the data to the BluetoothService classes.
+ */
+class BTD : public USBDeviceConfig, public UsbConfigXtracter {
+public:
+        /**
+         * Constructor for the BTD class.
+         * @param  p   Pointer to USB class instance.
+         */
+        BTD(USB *p);
+
+        /** @name USBDeviceConfig implementation */
+        /**
+         * Address assignment and basic initialization is done here.
+         * @param  parent   Hub number.
+         * @param  port     Port number on the hub.
+         * @param  lowspeed Speed of the device.
+         * @return          0 on success.
+         */
+        uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);
+        /**
+         * Initialize the Bluetooth dongle.
+         * @param  parent   Hub number.
+         * @param  port     Port number on the hub.
+         * @param  lowspeed Speed of the device.
+         * @return          0 on success.
+         */
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        /**
+         * Release the USB device.
+         * @return 0 on success.
+         */
+        uint8_t Release();
+        /**
+         * Poll the USB Input endpoints and run the state machines.
+         * @return 0 on success.
+         */
+        uint8_t Poll();
+
+        /**
+         * Get the device address.
+         * @return The device address.
+         */
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        /**
+         * Used to check if the dongle has been initialized.
+         * @return True if it's ready.
+         */
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  klass The device's USB class.
+         * @return       Returns true if the device's USB class matches this driver.
+         */
+        virtual bool DEVCLASSOK(uint8_t klass) {
+                return (klass == USB_CLASS_WIRELESS_CTRL);
+        };
+
+        /**
+         * Used by the USB core to check what this driver support.
+         * Used to set the Bluetooth address into the PS3 controllers.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                if((vid == IOGEAR_GBU521_VID && pid == IOGEAR_GBU521_PID) || (vid == BELKIN_F8T065BF_VID && pid == BELKIN_F8T065BF_PID))
+                        return true;
+                if(my_bdaddr[0] != 0x00 || my_bdaddr[1] != 0x00 || my_bdaddr[2] != 0x00 || my_bdaddr[3] != 0x00 || my_bdaddr[4] != 0x00 || my_bdaddr[5] != 0x00) { // Check if Bluetooth address is set
+                        if(vid == PS3_VID && (pid == PS3_PID || pid == PS3NAVIGATION_PID || pid == PS3MOVE_PID))
+                                return true;
+                }
+                return false;
+        };
+        /**@}*/
+
+        /** @name UsbConfigXtracter implementation */
+        /**
+         * UsbConfigXtracter implementation, used to extract endpoint information.
+         * @param conf  Configuration value.
+         * @param iface Interface number.
+         * @param alt   Alternate setting.
+         * @param proto Interface Protocol.
+         * @param ep    Endpoint Descriptor.
+         */
+        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+        /**@}*/
+
+        /** Disconnects both the L2CAP Channel and the HCI Connection for all Bluetooth services. */
+        void disconnect();
+
+        /**
+         * Register Bluetooth dongle members/services.
+         * @param  pService Pointer to BluetoothService class instance.
+         * @return          The service ID on success or -1 on fail.
+         */
+        int8_t registerBluetoothService(BluetoothService *pService) {
+                for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) {
+                        if(!btService[i]) {
+                                btService[i] = pService;
+                                return i; // Return ID
+                        }
+                }
+                return -1; // Error registering BluetoothService
+        };
+
+        /** @name HCI Commands */
+        /**
+         * Used to send a HCI Command.
+         * @param data   Data to send.
+         * @param nbytes Number of bytes to send.
+         */
+        void HCI_Command(uint8_t* data, uint16_t nbytes);
+        /** Reset the Bluetooth dongle. */
+        void hci_reset();
+        /** Read the Bluetooth address of the dongle. */
+        void hci_read_bdaddr();
+        /** Read the HCI Version of the Bluetooth dongle. */
+        void hci_read_local_version_information();
+        /**
+         * Set the local name of the Bluetooth dongle.
+         * @param name Desired name.
+         */
+        void hci_set_local_name(const char* name);
+        /** Enable visibility to other Bluetooth devices. */
+        void hci_write_scan_enable();
+        /** Disable visibility to other Bluetooth devices. */
+        void hci_write_scan_disable();
+        /** Read the remote devices name. */
+        void hci_remote_name();
+        /** Accept the connection with the Bluetooth device. */
+        void hci_accept_connection();
+        /**
+         * Disconnect the HCI connection.
+         * @param handle The HCI Handle for the connection.
+         */
+        void hci_disconnect(uint16_t handle);
+        /**
+         * Respond with the pin for the connection.
+         * The pin is automatically set for the Wii library,
+         * but can be customized for the SPP library.
+         */
+        void hci_pin_code_request_reply();
+        /** Respons when no pin was set. */
+        void hci_pin_code_negative_request_reply();
+        /**
+         * Command is used to reply to a Link Key Request event from the BR/EDR Controller
+         * if the Host does not have a stored Link Key for the connection.
+         */
+        void hci_link_key_request_negative_reply();
+        /** Used to try to authenticate with the remote device. */
+        void hci_authentication_request();
+        /** Start a HCI inquiry. */
+        void hci_inquiry();
+        /** Cancel a HCI inquiry. */
+        void hci_inquiry_cancel();
+        /** Connect to last device communicated with. */
+        void hci_connect();
+        /**
+         * Connect to device.
+         * @param bdaddr Bluetooth address of the device.
+         */
+        void hci_connect(uint8_t *bdaddr);
+        /** Used to a set the class of the device. */
+        void hci_write_class_of_device();
+        /**@}*/
+
+        /** @name L2CAP Commands */
+        /**
+         * Used to send L2CAP Commands.
+         * @param handle      HCI Handle.
+         * @param data        Data to send.
+         * @param nbytes      Number of bytes to send.
+         * @param channelLow,channelHigh  Low and high byte of channel to send to.
+         * If argument is omitted then the Standard L2CAP header: Channel ID (0x01) for ACL-U will be used.
+         */
+        void L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow = 0x01, uint8_t channelHigh = 0x00);
+        /**
+         * L2CAP Connection Request.
+         * @param handle HCI handle.
+         * @param rxid   Identifier.
+         * @param scid   Source Channel Identifier.
+         * @param psm    Protocol/Service Multiplexer - see: https://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm.
+         */
+        void l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm);
+        /**
+         * L2CAP Connection Response.
+         * @param handle HCI handle.
+         * @param rxid   Identifier.
+         * @param dcid   Destination Channel Identifier.
+         * @param scid   Source Channel Identifier.
+         * @param result Result - First send ::PENDING and then ::SUCCESSFUL.
+         */
+        void l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result);
+        /**
+         * L2CAP Config Request.
+         * @param handle HCI Handle.
+         * @param rxid   Identifier.
+         * @param dcid   Destination Channel Identifier.
+         */
+        void l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid);
+        /**
+         * L2CAP Config Response.
+         * @param handle HCI Handle.
+         * @param rxid   Identifier.
+         * @param scid   Source Channel Identifier.
+         */
+        void l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid);
+        /**
+         * L2CAP Disconnection Request.
+         * @param handle HCI Handle.
+         * @param rxid   Identifier.
+         * @param dcid   Device Channel Identifier.
+         * @param scid   Source Channel Identifier.
+         */
+        void l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid);
+        /**
+         * L2CAP Disconnection Response.
+         * @param handle HCI Handle.
+         * @param rxid   Identifier.
+         * @param dcid   Device Channel Identifier.
+         * @param scid   Source Channel Identifier.
+         */
+        void l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid);
+        /**
+         * L2CAP Information Response.
+         * @param handle       HCI Handle.
+         * @param rxid         Identifier.
+         * @param infoTypeLow,infoTypeHigh  Infotype.
+         */
+        void l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh);
+        /**@}*/
+
+        /** Use this to see if it is waiting for a incoming connection. */
+        bool waitingForConnection;
+        /** This is used by the service to know when to store the device information. */
+        bool l2capConnectionClaimed;
+        /** This is used by the SPP library to claim the current SDP incoming request. */
+        bool sdpConnectionClaimed;
+        /** This is used by the SPP library to claim the current RFCOMM incoming request. */
+        bool rfcommConnectionClaimed;
+
+        /** The name you wish to make the dongle show up as. It is set automatically by the SPP library. */
+        const char* btdName;
+        /** The pin you wish to make the dongle use for authentication. It is set automatically by the SPP and BTHID library. */
+        const char* btdPin;
+
+        /** The bluetooth dongles Bluetooth address. */
+        uint8_t my_bdaddr[6];
+        /** HCI handle for the last connection. */
+        uint16_t hci_handle;
+        /** Last incoming devices Bluetooth address. */
+        uint8_t disc_bdaddr[6];
+        /** First 30 chars of last remote name. */
+        char remote_name[30];
+        /**
+         * The supported HCI Version read from the Bluetooth dongle.
+         * Used by the PS3BT library to check the HCI Version of the Bluetooth dongle,
+         * it should be at least 3 to work properly with the library.
+         */
+        uint8_t hci_version;
+
+        /** Call this function to pair with a Wiimote */
+        void pairWithWiimote() {
+                pairWithWii = true;
+                hci_state = HCI_CHECK_DEVICE_SERVICE;
+        };
+        /** Used to only send the ACL data to the Wiimote. */
+        bool connectToWii;
+        /** True if a Wiimote is connecting. */
+        bool incomingWii;
+        /** True when it should pair with a Wiimote. */
+        bool pairWithWii;
+        /** True if it's the new Wiimote with the Motion Plus Inside or a Wii U Pro Controller. */
+        bool motionPlusInside;
+        /** True if it's a Wii U Pro Controller. */
+        bool wiiUProController;
+
+        /** Call this function to pair with a HID device */
+        void pairWithHID() {
+                waitingForConnection = false;
+                pairWithHIDDevice = true;
+                hci_state = HCI_CHECK_DEVICE_SERVICE;
+        };
+        /** Used to only send the ACL data to the HID device. */
+        bool connectToHIDDevice;
+        /** True if a HID device is connecting. */
+        bool incomingHIDDevice;
+        /** True when it should pair with a device like a mouse or keyboard. */
+        bool pairWithHIDDevice;
+
+        /**
+         * Read the poll interval taken from the endpoint descriptors.
+         * @return The poll interval in ms.
+         */
+        uint8_t readPollInterval() {
+                return pollInterval;
+        };
+
+protected:
+        /** Pointer to USB class instance. */
+        USB *pUsb;
+        /** Device address. */
+        uint8_t bAddress;
+        /** Endpoint info structure. */
+        EpInfo epInfo[BTD_MAX_ENDPOINTS];
+
+        /** Configuration number. */
+        uint8_t bConfNum;
+        /** Total number of endpoints in the configuration. */
+        uint8_t bNumEP;
+        /** Next poll time based on poll interval taken from the USB descriptor. */
+        uint32_t qNextPollTime;
+
+        /** Bluetooth dongle control endpoint. */
+        static const uint8_t BTD_CONTROL_PIPE;
+        /** HCI event endpoint index. */
+        static const uint8_t BTD_EVENT_PIPE;
+        /** ACL In endpoint index. */
+        static const uint8_t BTD_DATAIN_PIPE;
+        /** ACL Out endpoint index. */
+        static const uint8_t BTD_DATAOUT_PIPE;
+
+        /**
+         * Used to print the USB Endpoint Descriptor.
+         * @param ep_ptr Pointer to USB Endpoint Descriptor.
+         */
+        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
+
+private:
+        void Initialize(); // Set all variables, endpoint structs etc. to default values
+        BluetoothService *btService[BTD_NUM_SERVICES];
+
+        uint16_t PID, VID; // PID and VID of device connected
+
+        uint8_t pollInterval;
+        bool bPollEnable;
+
+        bool pairWiiUsingSync; // True if pairing was done using the Wii SYNC button.
+        bool checkRemoteName; // Used to check remote device's name before connecting.
+        bool incomingPS4; // True if a PS4 controller is connecting
+        uint8_t classOfDevice[3]; // Class of device of last device
+
+        /* Variables used by high level HCI task */
+        uint8_t hci_state; // Current state of Bluetooth HCI connection
+        uint16_t hci_counter; // Counter used for Bluetooth HCI reset loops
+        uint16_t hci_num_reset_loops; // This value indicate how many times it should read before trying to reset
+        uint16_t hci_event_flag; // HCI flags of received Bluetooth events
+        uint8_t inquiry_counter;
+
+        uint8_t hcibuf[BULK_MAXPKTSIZE]; // General purpose buffer for HCI data
+        uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for L2CAP in data
+        uint8_t l2capoutbuf[14]; // General purpose buffer for L2CAP out data
+
+        /* State machines */
+        void HCI_event_task(); // Poll the HCI event pipe
+        void HCI_task(); // HCI state machine
+        void ACL_event_task(); // ACL input pipe
+
+        /* Used to set the Bluetooth Address internally to the PS3 Controllers */
+        void setBdaddr(uint8_t* BDADDR);
+        void setMoveBdaddr(uint8_t* BDADDR);
+};
+
+/** All Bluetooth services should inherit this class. */
+class BluetoothService {
+public:
+        BluetoothService(BTD *p) : pBtd(p) {
+                if(pBtd)
+                        pBtd->registerBluetoothService(this); // Register it as a Bluetooth service
+        };
+        /**
+         * Used to pass acldata to the Bluetooth service.
+         * @param ACLData Pointer to the incoming acldata.
+         */
+        virtual void ACLData(uint8_t* ACLData) = 0;
+        /** Used to run the different state machines in the Bluetooth service. */
+        virtual void Run() = 0;
+        /** Used to reset the Bluetooth service. */
+        virtual void Reset() = 0;
+        /** Used to disconnect both the L2CAP Channel and the HCI Connection for the Bluetooth service. */
+        virtual void disconnect() = 0;
+
+        /**
+         * Used to call your own function when the device is successfully initialized.
+         * @param funcOnInit Function to call.
+         */
+        void attachOnInit(void (*funcOnInit)(void)) {
+                pFuncOnInit = funcOnInit; // TODO: This really belong in a class of it's own as it is repeated several times
+        };
+
+protected:
+        /**
+         * Called when a device is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        virtual void onInit() = 0;
+
+        /** Used to check if the incoming L2CAP data matches the HCI Handle */
+        bool checkHciHandle(uint8_t *buf, uint16_t handle) {
+                return (buf[0] == (handle & 0xFF)) && (buf[1] == ((handle >> 8) | 0x20));
+        }
+
+        /** Pointer to function called in onInit(). */
+        void (*pFuncOnInit)(void);
+
+        /** Pointer to BTD instance. */
+        BTD *pBtd;
+
+        /** The HCI Handle for the connection. */
+        uint16_t hci_handle;
+
+        /** L2CAP flags of received Bluetooth events. */
+        uint32_t l2cap_event_flag;
+
+        /** Identifier for L2CAP commands. */
+        uint8_t identifier;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/BTHID.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,400 @@
+/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "BTHID.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report send by the HID device
+
+BTHID::BTHID(BTD *p, bool pair, const char *pin) :
+BluetoothService(p), // Pointer to USB class instance - mandatory
+protocolMode(USB_HID_BOOT_PROTOCOL) {
+        for(uint8_t i = 0; i < NUM_PARSERS; i++)
+                pRptParser[i] = NULL;
+
+        pBtd->pairWithHIDDevice = pair;
+        pBtd->btdPin = pin;
+
+        /* Set device cid for the control and intterrupt channelse - LSB */
+        control_dcid[0] = 0x70; // 0x0070
+        control_dcid[1] = 0x00;
+        interrupt_dcid[0] = 0x71; // 0x0071
+        interrupt_dcid[1] = 0x00;
+
+        Reset();
+}
+
+void BTHID::Reset() {
+        connected = false;
+        activeConnection = false;
+        l2cap_event_flag = 0; // Reset flags
+        l2cap_state = L2CAP_WAIT;
+        ResetBTHID();
+}
+
+void BTHID::disconnect() { // Use this void to disconnect the device
+        // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection
+        pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid);
+        Reset();
+        l2cap_state = L2CAP_INTERRUPT_DISCONNECT;
+}
+
+void BTHID::ACLData(uint8_t* l2capinbuf) {
+        if(!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) {
+                if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
+                        if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
+                                pBtd->incomingHIDDevice = false;
+                                pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service
+                                activeConnection = true;
+                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
+                                l2cap_state = L2CAP_WAIT;
+                        }
+                }
+        }
+
+        if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok
+                if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U
+                        if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+#endif
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) {
+                                if(((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success
+                                        if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Control Connection Complete"), 0x80);
+                                                identifier = l2capinbuf[9];
+                                                control_scid[0] = l2capinbuf[12];
+                                                control_scid[1] = l2capinbuf[13];
+                                                l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED);
+                                        } else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80);
+                                                identifier = l2capinbuf[9];
+                                                interrupt_scid[0] = l2capinbuf[12];
+                                                interrupt_scid[1] = l2capinbuf[13];
+                                                l2cap_set_flag(L2CAP_FLAG_INTERRUPT_CONNECTED);
+                                        }
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
+#ifdef EXTRADEBUG
+                                Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                Notify(PSTR(" SCID: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+                                Notify(PSTR(" Identifier: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
+#endif
+                                if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
+                                        identifier = l2capinbuf[9];
+                                        control_scid[0] = l2capinbuf[14];
+                                        control_scid[1] = l2capinbuf[15];
+                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST);
+                                } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {
+                                        identifier = l2capinbuf[9];
+                                        interrupt_scid[0] = l2capinbuf[14];
+                                        interrupt_scid[1] = l2capinbuf[15];
+                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
+                                if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
+                                        if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80);
+                                                identifier = l2capinbuf[9];
+                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);
+                                        } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80);
+                                                identifier = l2capinbuf[9];
+                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);
+                                        }
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
+                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+                                        //Notify(PSTR("\r\nHID Control Configuration Request"), 0x80);
+                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);
+                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+                                        //Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80);
+                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
+                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80);
+#endif
+                                        identifier = l2capinbuf[9];
+                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);
+                                        Reset();
+                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80);
+#endif
+                                        identifier = l2capinbuf[9];
+                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);
+                                        Reset();
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
+                                if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE);
+                                } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);
+                                }
+                        }
+#ifdef EXTRADEBUG
+                        else {
+                                identifier = l2capinbuf[9];
+                                Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
+                        }
+#endif
+                } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt
+#ifdef PRINTREPORT
+                        Notify(PSTR("\r\nL2CAP Interrupt: "), 0x80);
+                        for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
+                                D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                        }
+#endif
+                        if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
+                                uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]);
+                                ParseBTHIDData((uint8_t)(length - 1), &l2capinbuf[9]);
+
+                                switch(l2capinbuf[9]) {
+                                        case 0x01: // Keyboard or Joystick events
+                                                if(pRptParser[KEYBOARD_PARSER_ID])
+                                                        pRptParser[KEYBOARD_PARSER_ID]->Parse(reinterpret_cast<USBHID *>(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance
+                                                break;
+
+                                        case 0x02: // Mouse events
+                                                if(pRptParser[MOUSE_PARSER_ID])
+                                                        pRptParser[MOUSE_PARSER_ID]->Parse(reinterpret_cast<USBHID *>(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance
+                                                break;
+#ifdef EXTRADEBUG
+                                        default:
+                                                Notify(PSTR("\r\nUnknown Report type: "), 0x80);
+                                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
+                                                break;
+#endif
+                                }
+                        }
+                } else if(l2capinbuf[6] == control_dcid[0] && l2capinbuf[7] == control_dcid[1]) { // l2cap_control
+#ifdef PRINTREPORT
+                        Notify(PSTR("\r\nL2CAP Control: "), 0x80);
+                        for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
+                                D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                        }
+#endif
+                }
+#ifdef EXTRADEBUG
+                else {
+                        Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80);
+                        D_PrintHex<uint8_t > (l2capinbuf[7], 0x80);
+                        Notify(PSTR(" "), 0x80);
+                        D_PrintHex<uint8_t > (l2capinbuf[6], 0x80);
+
+                        Notify(PSTR("\r\nData: "), 0x80);
+                        Notify(PSTR("\r\n"), 0x80);
+                        for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {
+                                D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                        }
+                }
+#endif
+                L2CAP_task();
+        }
+}
+
+void BTHID::L2CAP_task() {
+        switch(l2cap_state) {
+                        /* These states are used if the HID device is the host */
+                case L2CAP_CONTROL_SUCCESS:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80);
+#endif
+                                setProtocol(); // Set protocol before establishing HID interrupt channel
+                                l2cap_state = L2CAP_INTERRUPT_SETUP;
+                        }
+                        break;
+
+                case L2CAP_INTERRUPT_SETUP:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80);
+#endif
+                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);
+                                delay(1);
+                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);
+                                identifier++;
+                                delay(1);
+                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
+
+                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
+                        }
+                        break;
+
+                        /* These states are used if the Arduino is the host */
+                case L2CAP_CONTROL_CONNECT_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONTROL_CONNECTED)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSend HID Control Config Request"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
+                                l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST;
+                        }
+                        break;
+
+                case L2CAP_CONTROL_CONFIG_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {
+                                setProtocol(); // Set protocol before establishing HID interrupt channel
+                                delay(1); // Short delay between commands - just to be sure
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSend HID Interrupt Connection Request"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM);
+                                l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST;
+                        }
+                        break;
+
+                case L2CAP_INTERRUPT_CONNECT_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_INTERRUPT_CONNECTED)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSend HID Interrupt Config Request"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
+                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
+                        }
+                        break;
+
+                case L2CAP_INTERRUPT_CONFIG_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Channels Established"), 0x80);
+#endif
+                                pBtd->connectToHIDDevice = false;
+                                pBtd->pairWithHIDDevice = false;
+                                connected = true;
+                                onInit();
+                                l2cap_state = L2CAP_DONE;
+                        }
+                        break;
+
+                case L2CAP_DONE:
+                        break;
+
+                case L2CAP_INTERRUPT_DISCONNECT:
+                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);
+                                l2cap_state = L2CAP_CONTROL_DISCONNECT;
+                        }
+                        break;
+
+                case L2CAP_CONTROL_DISCONNECT:
+                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected Control Channel"), 0x80);
+#endif
+                                pBtd->hci_disconnect(hci_handle);
+                                hci_handle = -1; // Reset handle
+                                l2cap_event_flag = 0; // Reset flags
+                                l2cap_state = L2CAP_WAIT;
+                        }
+                        break;
+        }
+}
+
+void BTHID::Run() {
+        switch(l2cap_state) {
+                case L2CAP_WAIT:
+                        if(pBtd->connectToHIDDevice && !pBtd->l2capConnectionClaimed && !connected && !activeConnection) {
+                                pBtd->l2capConnectionClaimed = true;
+                                activeConnection = true;
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSend HID Control Connection Request"), 0x80);
+#endif
+                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
+                                l2cap_event_flag = 0; // Reset flags
+                                identifier = 0;
+                                pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM);
+                                l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST;
+                        } else if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80);
+#endif
+                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);
+                                delay(1);
+                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);
+                                identifier++;
+                                delay(1);
+                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
+                                l2cap_state = L2CAP_CONTROL_SUCCESS;
+                        }
+                        break;
+        }
+}
+
+/************************************************************/
+/*                    HID Commands                          */
+
+/************************************************************/
+void BTHID::setProtocol() {
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nSet protocol mode: "), 0x80);
+        D_PrintHex<uint8_t > (protocolMode, 0x80);
+#endif
+        if (protocolMode != USB_HID_BOOT_PROTOCOL && protocolMode != HID_RPT_PROTOCOL) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nNot a valid protocol mode. Using Boot protocol instead."), 0x80);
+#endif
+                protocolMode = USB_HID_BOOT_PROTOCOL; // Use Boot Protocol by default
+        }
+        uint8_t command = 0x70 | protocolMode; // Set Protocol, see Bluetooth HID specs page 33
+        pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]);
+}
+
+void BTHID::setLeds(uint8_t data) {
+        uint8_t buf[3];
+        buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+        buf[1] = 0x01; // Report ID
+        buf[2] = data;
+        pBtd->L2CAP_Command(hci_handle, buf, 3, interrupt_scid[0], interrupt_scid[1]);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/BTHID.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,161 @@
+/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _bthid_h_
+#define _bthid_h_
+
+#include "BTD.h"
+#include "hidboot.h"
+
+#define KEYBOARD_PARSER_ID      0
+#define MOUSE_PARSER_ID         1
+#define NUM_PARSERS             2
+
+/** This BluetoothService class implements support for Bluetooth HID devices. */
+class BTHID : public BluetoothService {
+public:
+        /**
+         * Constructor for the BTHID class.
+         * @param  p   Pointer to the BTD class instance.
+         * @param  pair   Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true.
+         * @param  pin   Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
+         */
+        BTHID(BTD *p, bool pair = false, const char *pin = "0000");
+
+        /** @name BluetoothService implementation */
+        /** Used this to disconnect the devices. */
+        void disconnect();
+        /**@}*/
+
+        /**
+         * Get HIDReportParser.
+         * @param  id ID of parser.
+         * @return    Returns the corresponding HIDReportParser. Returns NULL if id is not valid.
+         */
+        HIDReportParser *GetReportParser(uint8_t id) {
+                if (id >= NUM_PARSERS)
+                        return NULL;
+                return pRptParser[id];
+        };
+
+        /**
+         * Set HIDReportParser to be used.
+         * @param  id  Id of parser.
+         * @param  prs Pointer to HIDReportParser.
+         * @return     Returns true if the HIDReportParser is set. False otherwise.
+         */
+        bool SetReportParser(uint8_t id, HIDReportParser *prs) {
+                if (id >= NUM_PARSERS)
+                        return false;
+                pRptParser[id] = prs;
+                return true;
+        };
+
+        /**
+         * Set HID protocol mode.
+         * @param mode HID protocol to use. Either USB_HID_BOOT_PROTOCOL or HID_RPT_PROTOCOL.
+         */
+        void setProtocolMode(uint8_t mode) {
+                protocolMode = mode;
+        };
+
+        /**@{*/
+        /**
+         * Used to set the leds on a keyboard.
+         * @param data See ::KBDLEDS in hidboot.h
+         */
+        void setLeds(struct KBDLEDS data) {
+                setLeds(*((uint8_t*)&data));
+        };
+        void setLeds(uint8_t data);
+        /**@}*/
+
+        /** True if a device is connected */
+        bool connected;
+
+        /** Call this to start the pairing sequence with a device */
+        void pair(void) {
+                if(pBtd)
+                        pBtd->pairWithHID();
+        };
+
+protected:
+        /** @name BluetoothService implementation */
+        /**
+         * Used to pass acldata to the services.
+         * @param ACLData Incoming acldata.
+         */
+        void ACLData(uint8_t* ACLData);
+        /** Used to run part of the state machine. */
+        void Run();
+        /** Use this to reset the service. */
+        void Reset();
+        /**
+         * Called when a device is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        void onInit() {
+                if(pFuncOnInit)
+                        pFuncOnInit(); // Call the user function
+                OnInitBTHID();
+        };
+        /**@}*/
+
+        /** @name Overridable functions */
+        /**
+         * Used to parse Bluetooth HID data to any class that inherits this class.
+         * @param len The length of the incoming data.
+         * @param buf Pointer to the data buffer.
+         */
+        virtual void ParseBTHIDData(uint8_t len __attribute__((unused)), uint8_t *buf __attribute__((unused))) {
+                return;
+        };
+        /** Called when a device is connected */
+        virtual void OnInitBTHID() {
+                return;
+        };
+        /** Used to reset any buffers in the class that inherits this */
+        virtual void ResetBTHID() {
+                return;
+        }
+        /**@}*/
+
+        /** L2CAP source CID for HID_Control */
+        uint8_t control_scid[2];
+
+        /** L2CAP source CID for HID_Interrupt */
+        uint8_t interrupt_scid[2];
+
+private:
+        HIDReportParser *pRptParser[NUM_PARSERS]; // Pointer to HIDReportParsers.
+
+        /** Set report protocol. */
+        void setProtocol();
+        uint8_t protocolMode;
+
+        void L2CAP_task(); // L2CAP state machine
+
+        bool activeConnection; // Used to indicate if it already has established a connection
+
+        /* Variables used for L2CAP communication */
+        uint8_t control_dcid[2]; // L2CAP device CID for HID_Control - Always 0x0070
+        uint8_t interrupt_dcid[2]; // L2CAP device CID for HID_Interrupt - Always 0x0071
+        uint8_t l2cap_state;
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS3BT.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,638 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "PS3BT.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers
+
+PS3BT::PS3BT(BTD *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0) :
+BluetoothService(p) // Pointer to USB class instance - mandatory
+{
+        pBtd->my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead
+        pBtd->my_bdaddr[4] = btadr4;
+        pBtd->my_bdaddr[3] = btadr3;
+        pBtd->my_bdaddr[2] = btadr2;
+        pBtd->my_bdaddr[1] = btadr1;
+        pBtd->my_bdaddr[0] = btadr0;
+
+        HIDBuffer[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02)
+        HIDBuffer[1] = 0x01; // Report ID
+
+        // Needed for PS3 Move Controller commands to work via bluetooth
+        HIDMoveBuffer[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+        HIDMoveBuffer[1] = 0x02; // Report ID
+
+        /* Set device cid for the control and intterrupt channelse - LSB */
+        control_dcid[0] = 0x40; // 0x0040
+        control_dcid[1] = 0x00;
+        interrupt_dcid[0] = 0x41; // 0x0041
+        interrupt_dcid[1] = 0x00;
+
+        Reset();
+}
+
+bool PS3BT::getButtonPress(ButtonEnum b) {
+        return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]));
+}
+
+bool PS3BT::getButtonClick(ButtonEnum b) {
+        uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]);
+        bool click = (ButtonClickState & button);
+        ButtonClickState &= ~button; // Clear "click" event
+        return click;
+}
+
+uint8_t PS3BT::getAnalogButton(ButtonEnum a) {
+        return (uint8_t)(l2capinbuf[pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])]);
+}
+
+uint8_t PS3BT::getAnalogHat(AnalogHatEnum a) {
+        return (uint8_t)(l2capinbuf[(uint8_t)a + 15]);
+}
+
+int16_t PS3BT::getSensor(SensorEnum a) {
+        if(PS3Connected) {
+                if(a == aX || a == aY || a == aZ || a == gZ)
+                        return ((l2capinbuf[(uint16_t)a] << 8) | l2capinbuf[(uint16_t)a + 1]);
+                else
+                        return 0;
+        } else if(PS3MoveConnected) {
+                if(a == mXmove || a == mYmove) // These are all 12-bits long
+                        return (((l2capinbuf[(uint16_t)a] & 0x0F) << 8) | (l2capinbuf[(uint16_t)a + 1]));
+                else if(a == mZmove || a == tempMove) // The tempearature is also 12 bits long
+                        return ((l2capinbuf[(uint16_t)a] << 4) | ((l2capinbuf[(uint16_t)a + 1] & 0xF0) >> 4));
+                else // aXmove, aYmove, aZmove, gXmove, gYmove and gZmove
+                        return (l2capinbuf[(uint16_t)a] | (l2capinbuf[(uint16_t)a + 1] << 8));
+        } else
+                return 0;
+}
+
+float PS3BT::getAngle(AngleEnum a) {
+        float accXval, accYval, accZval;
+
+        if(PS3Connected) {
+                // Data for the Kionix KXPC4 used in the DualShock 3
+                const float zeroG = 511.5f; // 1.65/3.3*1023 (1.65V)
+                accXval = -((float)getSensor(aX) - zeroG);
+                accYval = -((float)getSensor(aY) - zeroG);
+                accZval = -((float)getSensor(aZ) - zeroG);
+        } else if(PS3MoveConnected) {
+                // It's a Kionix KXSC4 inside the Motion controller
+                const uint16_t zeroG = 0x8000;
+                accXval = -(int16_t)(getSensor(aXmove) - zeroG);
+                accYval = (int16_t)(getSensor(aYmove) - zeroG);
+                accZval = (int16_t)(getSensor(aZmove) - zeroG);
+        } else
+                return 0;
+
+        // Convert to 360 degrees resolution
+        // atan2 outputs the value of -π to π (radians)
+        // We are then converting it to 0 to 2π and then to degrees
+        if(a == Pitch)
+                return (atan2f(accYval, accZval) + PI) * RAD_TO_DEG;
+        else
+                return (atan2f(accXval, accZval) + PI) * RAD_TO_DEG;
+}
+
+float PS3BT::get9DOFValues(SensorEnum a) { // Thanks to Manfred Piendl
+        if(!PS3MoveConnected)
+                return 0;
+        int16_t value = getSensor(a);
+        if(a == mXmove || a == mYmove || a == mZmove) {
+                if(value > 2047)
+                        value -= 0x1000;
+                return (float)value / 3.2f; // unit: muT = 10^(-6) Tesla
+        } else if(a == aXmove || a == aYmove || a == aZmove) {
+                if(value < 0)
+                        value += 0x8000;
+                else
+                        value -= 0x8000;
+                return (float)value / 442.0f; // unit: m/(s^2)
+        } else if(a == gXmove || a == gYmove || a == gZmove) {
+                if(value < 0)
+                        value += 0x8000;
+                else
+                        value -= 0x8000;
+                if(a == gXmove)
+                        return (float)value / 11.6f; // unit: deg/s
+                else if(a == gYmove)
+                        return (float)value / 11.2f; // unit: deg/s
+                else // gZmove
+                        return (float)value / 9.6f; // unit: deg/s
+        } else
+                return 0;
+}
+
+String PS3BT::getTemperature() {
+        if(PS3MoveConnected) {
+                int16_t input = getSensor(tempMove);
+
+                String output = String(input / 100);
+                output += ".";
+                if(input % 100 < 10)
+                        output += "0";
+                output += String(input % 100);
+
+                return output;
+        } else
+                return "Error";
+}
+
+bool PS3BT::getStatus(StatusEnum c) {
+        return (l2capinbuf[(uint16_t)c >> 8] == ((uint8_t)c & 0xff));
+}
+
+void PS3BT::printStatusString() {
+        char statusOutput[102]; // Max string length plus null character
+        if(PS3Connected || PS3NavigationConnected) {
+                strcpy_P(statusOutput, PSTR("\r\nConnectionStatus: "));
+
+                if(getStatus(Plugged)) strcat_P(statusOutput, PSTR("Plugged"));
+                else if(getStatus(Unplugged)) strcat_P(statusOutput, PSTR("Unplugged"));
+                else strcat_P(statusOutput, PSTR("Error"));
+
+                strcat_P(statusOutput, PSTR(" - PowerRating: "));
+
+                if(getStatus(Charging)) strcat_P(statusOutput, PSTR("Charging"));
+                else if(getStatus(NotCharging)) strcat_P(statusOutput, PSTR("Not Charging"));
+                else if(getStatus(Shutdown)) strcat_P(statusOutput, PSTR("Shutdown"));
+                else if(getStatus(Dying)) strcat_P(statusOutput, PSTR("Dying"));
+                else if(getStatus(Low)) strcat_P(statusOutput, PSTR("Low"));
+                else if(getStatus(High)) strcat_P(statusOutput, PSTR("High"));
+                else if(getStatus(Full)) strcat_P(statusOutput, PSTR("Full"));
+                else strcat_P(statusOutput, PSTR("Error"));
+
+                strcat_P(statusOutput, PSTR(" - WirelessStatus: "));
+
+                if(getStatus(CableRumble)) strcat_P(statusOutput, PSTR("Cable - Rumble is on"));
+                else if(getStatus(Cable)) strcat_P(statusOutput, PSTR("Cable - Rumble is off"));
+                else if(getStatus(BluetoothRumble)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is on"));
+                else if(getStatus(Bluetooth)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is off"));
+                else strcat_P(statusOutput, PSTR("Error"));
+        } else if(PS3MoveConnected) {
+                strcpy_P(statusOutput, PSTR("\r\nPowerRating: "));
+
+                if(getStatus(MoveCharging)) strcat_P(statusOutput, PSTR("Charging"));
+                else if(getStatus(MoveNotCharging)) strcat_P(statusOutput, PSTR("Not Charging"));
+                else if(getStatus(MoveShutdown)) strcat_P(statusOutput, PSTR("Shutdown"));
+                else if(getStatus(MoveDying)) strcat_P(statusOutput, PSTR("Dying"));
+                else if(getStatus(MoveLow)) strcat_P(statusOutput, PSTR("Low"));
+                else if(getStatus(MoveHigh)) strcat_P(statusOutput, PSTR("High"));
+                else if(getStatus(MoveFull)) strcat_P(statusOutput, PSTR("Full"));
+                else strcat_P(statusOutput, PSTR("Error"));
+        } else
+                strcpy_P(statusOutput, PSTR("\r\nError"));
+
+        //USB_HOST_SERIAL.write(statusOutput);
+}
+
+void PS3BT::Reset() {
+        PS3Connected = false;
+        PS3MoveConnected = false;
+        PS3NavigationConnected = false;
+        activeConnection = false;
+        l2cap_event_flag = 0; // Reset flags
+        l2cap_state = L2CAP_WAIT;
+
+        // Needed for PS3 Dualshock Controller commands to work via Bluetooth
+        for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)
+                HIDBuffer[i + 2] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // First two bytes reserved for report type and ID
+}
+
+void PS3BT::disconnect() { // Use this void to disconnect any of the controllers
+        // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection
+        pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid);
+        Reset();
+        l2cap_state = L2CAP_INTERRUPT_DISCONNECT;
+}
+
+void PS3BT::ACLData(uint8_t* ACLData) {
+        if(!pBtd->l2capConnectionClaimed && !PS3Connected && !PS3MoveConnected && !PS3NavigationConnected && !activeConnection && !pBtd->connectToWii && !pBtd->incomingWii && !pBtd->pairWithWii) {
+                if(ACLData[8] == L2CAP_CMD_CONNECTION_REQUEST) {
+                        if((ACLData[12] | (ACLData[13] << 8)) == HID_CTRL_PSM) {
+                                pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service
+                                activeConnection = true;
+                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
+                                l2cap_state = L2CAP_WAIT;
+                                remote_name_first = pBtd->remote_name[0]; // Store the first letter in remote name for the connection
+#ifdef DEBUG_USB_HOST
+                                if(pBtd->hci_version < 3) { // Check the HCI Version of the Bluetooth dongle
+                                        Notify(PSTR("\r\nYour dongle may not support reading the analog buttons, sensors and status\r\nYour HCI Version is: "), 0x80);
+                                        Notify(pBtd->hci_version, 0x80);
+                                        Notify(PSTR("\r\nBut should be at least 3\r\nThis means that it doesn't support Bluetooth Version 2.0+EDR"), 0x80);
+                                }
+#endif
+                        }
+                }
+        }
+
+        if(checkHciHandle(ACLData, hci_handle)) { // acl_handle_ok
+                memcpy(l2capinbuf, ACLData, BULK_MAXPKTSIZE);
+                if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U
+                        if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                Notify(PSTR(" Data: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+#endif
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
+#ifdef EXTRADEBUG
+                                Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                Notify(PSTR(" SCID: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+                                Notify(PSTR(" Identifier: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
+#endif
+                                if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
+                                        identifier = l2capinbuf[9];
+                                        control_scid[0] = l2capinbuf[14];
+                                        control_scid[1] = l2capinbuf[15];
+                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST);
+                                } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {
+                                        identifier = l2capinbuf[9];
+                                        interrupt_scid[0] = l2capinbuf[14];
+                                        interrupt_scid[1] = l2capinbuf[15];
+                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
+                                if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
+                                        if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80);
+                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);
+                                        } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80);
+                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);
+                                        }
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
+                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+                                        //Notify(PSTR("\r\nHID Control Configuration Request"), 0x80);
+                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);
+                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+                                        //Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80);
+                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
+                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80);
+#endif
+                                        identifier = l2capinbuf[9];
+                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);
+                                        Reset();
+                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80);
+#endif
+                                        identifier = l2capinbuf[9];
+                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);
+                                        Reset();
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
+                                if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE);
+                                } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);
+                                }
+                        }
+#ifdef EXTRADEBUG
+                        else {
+                                Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
+                        }
+#endif
+                } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt
+                        //Notify(PSTR("\r\nL2CAP Interrupt"), 0x80);
+                        if(PS3Connected || PS3MoveConnected || PS3NavigationConnected) {
+                                /* Read Report */
+                                if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
+                                        lastMessageTime = (uint32_t)millis(); // Store the last message time
+
+                                        if(PS3Connected || PS3NavigationConnected)
+                                                ButtonState = (uint32_t)(l2capinbuf[11] | ((uint16_t)l2capinbuf[12] << 8) | ((uint32_t)l2capinbuf[13] << 16));
+                                        else if(PS3MoveConnected)
+                                                ButtonState = (uint32_t)(l2capinbuf[10] | ((uint16_t)l2capinbuf[11] << 8) | ((uint32_t)l2capinbuf[12] << 16));
+
+                                        //Notify(PSTR("\r\nButtonState", 0x80);
+                                        //PrintHex<uint32_t>(ButtonState, 0x80);
+
+                                        if(ButtonState != OldButtonState) {
+                                                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
+                                                OldButtonState = ButtonState;
+                                        }
+
+#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers
+                                        for(uint8_t i = 10; i < 58; i++) {
+                                                D_PrintHex<uint8_t > (l2capinbuf[i], 0x80);
+                                                Notify(PSTR(" "), 0x80);
+                                        }
+                                        Notify(PSTR("\r\n"), 0x80);
+#endif
+                                }
+                        }
+                }
+                L2CAP_task();
+        }
+}
+
+void PS3BT::L2CAP_task() {
+        switch(l2cap_state) {
+                case L2CAP_WAIT:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80);
+#endif
+                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);
+                                delay(1);
+                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);
+                                identifier++;
+                                delay(1);
+                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
+                                l2cap_state = L2CAP_CONTROL_SUCCESS;
+                        }
+                        break;
+
+                case L2CAP_CONTROL_SUCCESS:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80);
+#endif
+                                l2cap_state = L2CAP_INTERRUPT_SETUP;
+                        }
+                        break;
+
+                case L2CAP_INTERRUPT_SETUP:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80);
+#endif
+                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);
+                                delay(1);
+                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);
+                                identifier++;
+                                delay(1);
+                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
+
+                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
+                        }
+                        break;
+
+                case L2CAP_INTERRUPT_CONFIG_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Interrupt Successfully Configured"), 0x80);
+#endif
+                                if(remote_name_first == 'M') { // First letter in Motion Controller ('M')
+                                        memset(l2capinbuf, 0, BULK_MAXPKTSIZE); // Reset l2cap in buffer as it sometimes read it as a button has been pressed
+                                        l2cap_state = TURN_ON_LED;
+                                } else
+                                        l2cap_state = PS3_ENABLE_SIXAXIS;
+                                timer = (uint32_t)millis();
+                        }
+                        break;
+
+                        /* These states are handled in Run() */
+
+                case L2CAP_INTERRUPT_DISCONNECT:
+                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);
+                                l2cap_state = L2CAP_CONTROL_DISCONNECT;
+                        }
+                        break;
+
+                case L2CAP_CONTROL_DISCONNECT:
+                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected Control Channel"), 0x80);
+#endif
+                                pBtd->hci_disconnect(hci_handle);
+                                hci_handle = -1; // Reset handle
+                                l2cap_event_flag = 0; // Reset flags
+                                l2cap_state = L2CAP_WAIT;
+                        }
+                        break;
+        }
+}
+
+void PS3BT::Run() {
+        switch(l2cap_state) {
+                case PS3_ENABLE_SIXAXIS:
+                        if((int32_t)((uint32_t)millis() - timer) > 1000) { // loop 1 second before sending the command
+                                memset(l2capinbuf, 0, BULK_MAXPKTSIZE); // Reset l2cap in buffer as it sometimes read it as a button has been pressed
+                                for(uint8_t i = 15; i < 19; i++)
+                                        l2capinbuf[i] = 0x7F; // Set the analog joystick values to center position
+                                enable_sixaxis();
+                                l2cap_state = TURN_ON_LED;
+                                timer = (uint32_t)millis();
+                        }
+                        break;
+
+                case TURN_ON_LED:
+                        if((int32_t)((uint32_t)millis() - timer) > 1000) { // loop 1 second before sending the command
+                                if(remote_name_first == 'P') { // First letter in PLAYSTATION(R)3 Controller ('P')
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nDualshock 3 Controller Enabled\r\n"), 0x80);
+#endif
+                                        PS3Connected = true;
+                                } else if(remote_name_first == 'N') { // First letter in Navigation Controller ('N')
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nNavigation Controller Enabled\r\n"), 0x80);
+#endif
+                                        PS3NavigationConnected = true;
+                                } else if(remote_name_first == 'M') { // First letter in Motion Controller ('M')
+                                        timer = (uint32_t)millis();
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nMotion Controller Enabled\r\n"), 0x80);
+#endif
+                                        PS3MoveConnected = true;
+                                }
+                                ButtonState = 0; // Clear all values
+                                OldButtonState = 0;
+                                ButtonClickState = 0;
+
+                                onInit(); // Turn on the LED on the controller
+                                l2cap_state = L2CAP_DONE;
+                        }
+                        break;
+
+                case L2CAP_DONE:
+                        if(PS3MoveConnected) { // The Bulb and rumble values, has to be send at approximately every 5th second for it to stay on
+                                if((int32_t)((uint32_t)millis() - timer) > 4000) { // Send at least every 4th second
+                                        HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on
+                                        timer = (uint32_t)millis();
+                                }
+                        }
+                        break;
+        }
+}
+
+/************************************************************/
+/*                    HID Commands                          */
+/************************************************************/
+
+// Playstation Sixaxis Dualshock and Navigation Controller commands
+
+void PS3BT::HID_Command(uint8_t* data, uint8_t nbytes) {
+        if((int32_t)((uint32_t)millis() - timerHID) <= 150) // Check if is has been more than 150ms since last command
+                delay((uint32_t)(150 - ((uint32_t)millis() - timerHID))); // There have to be a delay between commands
+        pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]); // Both the Navigation and Dualshock controller sends data via the control channel
+        timerHID = (uint32_t)millis();
+}
+
+void PS3BT::setAllOff() {
+        HIDBuffer[3] = 0x00; // Rumble bytes
+        HIDBuffer[4] = 0x00;
+        HIDBuffer[5] = 0x00;
+        HIDBuffer[6] = 0x00;
+
+        HIDBuffer[11] = 0x00; // LED byte
+
+        HID_Command(HIDBuffer, HID_BUFFERSIZE);
+}
+
+void PS3BT::setRumbleOff() {
+        uint8_t rumbleBuf[HID_BUFFERSIZE];
+        memcpy(rumbleBuf, HIDBuffer, HID_BUFFERSIZE);
+        rumbleBuf[3] = 0x00;
+        rumbleBuf[4] = 0x00;
+        rumbleBuf[5] = 0x00;
+        rumbleBuf[6] = 0x00;
+        HID_Command(rumbleBuf, HID_BUFFERSIZE);
+}
+
+void PS3BT::setRumbleOn(RumbleEnum mode) {
+        uint8_t power[2] = {0xff, 0x00}; // Defaults to RumbleLow
+        if(mode == RumbleHigh) {
+                power[0] = 0x00;
+                power[1] = 0xff;
+        }
+        setRumbleOn(0xfe, power[0], 0xfe, power[1]);
+}
+
+void PS3BT::setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower) {
+        uint8_t rumbleBuf[HID_BUFFERSIZE];
+        memcpy(rumbleBuf, HIDBuffer, HID_BUFFERSIZE);
+        rumbleBuf[3] = rightDuration;
+        rumbleBuf[4] = rightPower;
+        rumbleBuf[5] = leftDuration;
+        rumbleBuf[6] = leftPower;
+        HID_Command(rumbleBuf, HID_BUFFERSIZE);
+}
+
+void PS3BT::setLedRaw(uint8_t value) {
+        HIDBuffer[11] = value << 1;
+        HID_Command(HIDBuffer, HID_BUFFERSIZE);
+}
+
+void PS3BT::setLedOff(LEDEnum a) {
+        HIDBuffer[11] &= ~((uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1));
+        HID_Command(HIDBuffer, HID_BUFFERSIZE);
+}
+
+void PS3BT::setLedOn(LEDEnum a) {
+        if(a == OFF)
+                setLedRaw(0);
+        else {
+                HIDBuffer[11] |= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);
+                HID_Command(HIDBuffer, HID_BUFFERSIZE);
+        }
+}
+
+void PS3BT::setLedToggle(LEDEnum a) {
+        HIDBuffer[11] ^= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);
+        HID_Command(HIDBuffer, HID_BUFFERSIZE);
+}
+
+void PS3BT::enable_sixaxis() { // Command used to enable the Dualshock 3 and Navigation controller to send data via Bluetooth
+        uint8_t cmd_buf[6];
+        cmd_buf[0] = 0x53; // HID BT Set_report (0x50) | Report Type (Feature 0x03)
+        cmd_buf[1] = 0xF4; // Report ID
+        cmd_buf[2] = 0x42; // Special PS3 Controller enable commands
+        cmd_buf[3] = 0x03;
+        cmd_buf[4] = 0x00;
+        cmd_buf[5] = 0x00;
+
+        HID_Command(cmd_buf, 6);
+}
+
+// Playstation Move Controller commands
+
+void PS3BT::HIDMove_Command(uint8_t* data, uint8_t nbytes) {
+        if((int32_t)((uint32_t)millis() - timerHID) <= 150)// Check if is has been less than 150ms since last command
+                delay((uint32_t)(150 - ((uint32_t)millis() - timerHID))); // There have to be a delay between commands
+        pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]); // The Move controller sends it's data via the intterrupt channel
+        timerHID = (uint32_t)millis();
+}
+
+void PS3BT::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { // Use this to set the Color using RGB values
+        // Set the Bulb's values into the write buffer
+        HIDMoveBuffer[3] = r;
+        HIDMoveBuffer[4] = g;
+        HIDMoveBuffer[5] = b;
+
+        HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE);
+}
+
+void PS3BT::moveSetBulb(ColorsEnum color) { // Use this to set the Color using the predefined colors in enum
+        moveSetBulb((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));
+}
+
+void PS3BT::moveSetRumble(uint8_t rumble) {
+#ifdef DEBUG_USB_HOST
+        if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100)
+                Notify(PSTR("\r\nThe rumble value has to at least 64, or approximately 25%"), 0x80);
+#endif
+        // Set the rumble value into the write buffer
+        HIDMoveBuffer[7] = rumble;
+
+        HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE);
+}
+
+void PS3BT::onInit() {
+        if(pFuncOnInit)
+                pFuncOnInit(); // Call the user function
+        else {
+                if(PS3MoveConnected)
+                        moveSetBulb(Red);
+                else // Dualshock 3 or Navigation controller
+                        setLedOn(static_cast<LEDEnum>(CONTROLLER_LED1));
+        }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS3BT.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,241 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _ps3bt_h_
+#define _ps3bt_h_
+
+#include "BTD.h"
+#include "PS3Enums.h"
+
+#define HID_BUFFERSIZE 50 // Size of the buffer for the Playstation Motion Controller
+
+/**
+ * This BluetoothService class implements support for all the official PS3 Controllers:
+ * Dualshock 3, Navigation or a Motion controller via Bluetooth.
+ *
+ * Information about the protocol can be found at the wiki: https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information.
+ */
+class PS3BT : public BluetoothService {
+public:
+        /**
+         * Constructor for the PS3BT class.
+         * @param  pBtd   Pointer to BTD class instance.
+         * @param  btadr5,btadr4,btadr3,btadr2,btadr1,btadr0
+         * Pass your dongles Bluetooth address into the constructor,
+         * This will set BTD#my_bdaddr, so you don't have to plug in the dongle before pairing with your controller.
+         */
+        PS3BT(BTD *pBtd, uint8_t btadr5 = 0, uint8_t btadr4 = 0, uint8_t btadr3 = 0, uint8_t btadr2 = 0, uint8_t btadr1 = 0, uint8_t btadr0 = 0);
+
+        /** @name BluetoothService implementation */
+        /** Used this to disconnect any of the controllers. */
+        void disconnect();
+        /**@}*/
+
+        /** @name PS3 Controller functions */
+        /**
+         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(ButtonEnum b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
+         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
+         * @param  b          ::ButtonEnum to read.
+         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
+         */
+        bool getButtonPress(ButtonEnum b);
+        bool getButtonClick(ButtonEnum b);
+        /**@}*/
+        /** @name PS3 Controller functions */
+        /**
+         * Used to get the analog value from button presses.
+         * @param  a The ::ButtonEnum to read.
+         * The supported buttons are:
+         * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,
+         * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.
+         * @return   Analog value in the range of 0-255.
+         */
+        uint8_t getAnalogButton(ButtonEnum a);
+        /**
+         * Used to read the analog joystick.
+         * @param  a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
+         * @return   Return the analog value in the range of 0-255.
+         */
+        uint8_t getAnalogHat(AnalogHatEnum a);
+        /**
+         * Used to read the sensors inside the Dualshock 3 and Move controller.
+         * @param  a
+         * The Dualshock 3 has a 3-axis accelerometer and a 1-axis gyro inside.
+         * The Move controller has a 3-axis accelerometer, a 3-axis gyro, a 3-axis magnetometer
+         * and a temperature sensor inside.
+         * @return   Return the raw sensor value.
+         */
+        int16_t getSensor(SensorEnum a);
+        /**
+         * Use this to get ::Pitch and ::Roll calculated using the accelerometer.
+         * @param  a Either ::Pitch or ::Roll.
+         * @return   Return the angle in the range of 0-360.
+         */
+        float getAngle(AngleEnum a);
+        /**
+         * Read the sensors inside the Move controller.
+         * @param  a ::aXmove, ::aYmove, ::aZmove, ::gXmove, ::gYmove, ::gZmove, ::mXmove, ::mYmove, and ::mXmove.
+         * @return   The value in SI units.
+         */
+        float get9DOFValues(SensorEnum a);
+        /**
+         * Get the status from the controller.
+         * @param  c The ::StatusEnum you want to read.
+         * @return   True if correct and false if not.
+         */
+        bool getStatus(StatusEnum c);
+        /** Read all the available statuses from the controller and prints it as a nice formated string. */
+        void printStatusString();
+        /**
+         * Read the temperature from the Move controller.
+         * @return The temperature in degrees Celsius.
+         */
+        String getTemperature();
+
+        /** Used to set all LEDs and rumble off. */
+        void setAllOff();
+        /** Turn off rumble. */
+        void setRumbleOff();
+        /**
+         * Turn on rumble.
+         * @param mode Either ::RumbleHigh or ::RumbleLow.
+         */
+        void setRumbleOn(RumbleEnum mode);
+        /**
+         * Turn on rumble using custom duration and power.
+         * @param rightDuration The duration of the right/low rumble effect.
+         * @param rightPower The intensity of the right/low rumble effect.
+         * @param leftDuration The duration of the left/high rumble effect.
+         * @param leftPower The intensity of the left/high rumble effect.
+         */
+        void setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower);
+
+        /**
+         * Set LED value without using ::LEDEnum.
+         * @param value See: ::LEDEnum.
+         */
+        void setLedRaw(uint8_t value);
+
+        /** Turn all LEDs off. */
+        void setLedOff() {
+                setLedRaw(0);
+        };
+        /**
+         * Turn the specific LED off.
+         * @param a The ::LEDEnum to turn off.
+         */
+        void setLedOff(LEDEnum a);
+        /**
+         * Turn the specific LED on.
+         * @param a The ::LEDEnum to turn on.
+         */
+        void setLedOn(LEDEnum a);
+        /**
+         * Toggle the specific LED.
+         * @param a The ::LEDEnum to toggle.
+         */
+        void setLedToggle(LEDEnum a);
+
+        /**
+         * Use this to set the Color using RGB values.
+         * @param r,g,b RGB value.
+         */
+        void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);
+        /**
+         * Use this to set the color using the predefined colors in ::ColorsEnum.
+         * @param color The desired color.
+         */
+        void moveSetBulb(ColorsEnum color);
+        /**
+         * Set the rumble value inside the Move controller.
+         * @param rumble The desired value in the range from 64-255.
+         */
+        void moveSetRumble(uint8_t rumble);
+
+        /** Used to get the millis() of the last message */
+        uint32_t getLastMessageTime() {
+                return lastMessageTime;
+        };
+        /**@}*/
+
+        /** Variable used to indicate if the normal Playstation controller is successfully connected. */
+        bool PS3Connected;
+        /** Variable used to indicate if the Move controller is successfully connected. */
+        bool PS3MoveConnected;
+        /** Variable used to indicate if the Navigation controller is successfully connected. */
+        bool PS3NavigationConnected;
+
+protected:
+        /** @name BluetoothService implementation */
+        /**
+         * Used to pass acldata to the services.
+         * @param ACLData Incoming acldata.
+         */
+        void ACLData(uint8_t* ACLData);
+        /** Used to run part of the state machine. */
+        void Run();
+        /** Use this to reset the service. */
+        void Reset();
+        /**
+         * Called when the controller is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        void onInit();
+        /**@}*/
+
+private:
+
+        void L2CAP_task(); // L2CAP state machine
+
+        /* Variables filled from HCI event management */
+        char remote_name_first; // First letter in remote name
+        bool activeConnection; // Used to indicate if it's already has established a connection
+
+        /* Variables used by high level L2CAP task */
+        uint8_t l2cap_state;
+
+        uint32_t lastMessageTime; // Variable used to store the millis value of the last message.
+
+        uint32_t ButtonState;
+        uint32_t OldButtonState;
+        uint32_t ButtonClickState;
+
+        uint32_t timer; // Timer used to limit time between messages and also used to continuously set PS3 Move controller Bulb and rumble values
+        uint32_t timerHID; // Timer used see if there has to be a delay before a new HID command
+
+        uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for L2CAP in data
+        uint8_t HIDBuffer[HID_BUFFERSIZE]; // Used to store HID commands
+        uint8_t HIDMoveBuffer[HID_BUFFERSIZE]; // Used to store HID commands for the Move controller
+
+        /* L2CAP Channels */
+        uint8_t control_scid[2]; // L2CAP source CID for HID_Control
+        uint8_t control_dcid[2]; // 0x0040
+        uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt
+        uint8_t interrupt_dcid[2]; // 0x0041
+
+        /* HID Commands */
+        void HID_Command(uint8_t* data, uint8_t nbytes);
+        void HIDMove_Command(uint8_t* data, uint8_t nbytes);
+        void enable_sixaxis(); // Command used to enable the Dualshock 3 and Navigation controller to send data via Bluetooth
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS3Enums.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,142 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _ps3enums_h
+#define _ps3enums_h
+
+#include "controllerEnums.h"
+
+/** Size of the output report buffer for the Dualshock and Navigation controllers */
+#define PS3_REPORT_BUFFER_SIZE  48
+
+/** Report buffer for all PS3 commands */
+const uint8_t PS3_REPORT_BUFFER[PS3_REPORT_BUFFER_SIZE] PROGMEM = {
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        0xff, 0x27, 0x10, 0x00, 0x32,
+        0xff, 0x27, 0x10, 0x00, 0x32,
+        0xff, 0x27, 0x10, 0x00, 0x32,
+        0xff, 0x27, 0x10, 0x00, 0x32,
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/** Size of the output report buffer for the Move Controller */
+#define MOVE_REPORT_BUFFER_SIZE 7
+
+/** Used to set the LEDs on the controllers */
+const uint8_t PS3_LEDS[] PROGMEM = {
+        0x00, // OFF
+        0x01, // LED1
+        0x02, // LED2
+        0x04, // LED3
+        0x08, // LED4
+
+        0x09, // LED5
+        0x0A, // LED6
+        0x0C, // LED7
+        0x0D, // LED8
+        0x0E, // LED9
+        0x0F, // LED10
+};
+
+/**
+ * Buttons on the controllers.
+ * <B>Note:</B> that the location is shifted 9 when it's connected via USB.
+ */
+const uint32_t PS3_BUTTONS[] PROGMEM = {
+        0x10, // UP
+        0x20, // RIGHT
+        0x40, // DOWN
+        0x80, // LEFT
+
+        0x01, // SELECT
+        0x08, // START
+        0x02, // L3
+        0x04, // R3
+
+        0x0100, // L2
+        0x0200, // R2
+        0x0400, // L1
+        0x0800, // R1
+
+        0x1000, // TRIANGLE
+        0x2000, // CIRCLE
+        0x4000, // CROSS
+        0x8000, // SQUARE
+
+        0x010000, // PS
+        0x080000, // MOVE - covers 12 bits - we only need to read the top 8
+        0x100000, // T - covers 12 bits - we only need to read the top 8
+};
+
+/**
+ * Analog buttons on the controllers.
+ * <B>Note:</B> that the location is shifted 9 when it's connected via USB.
+ */
+const uint8_t PS3_ANALOG_BUTTONS[] PROGMEM = {
+        23, // UP_ANALOG
+        24, // RIGHT_ANALOG
+        25, // DOWN_ANALOG
+        26, // LEFT_ANALOG
+        0, 0, 0, 0, // Skip SELECT, L3, R3 and START
+
+        27, // L2_ANALOG
+        28, // R2_ANALOG
+        29, // L1_ANALOG
+        30, // R1_ANALOG
+        31, // TRIANGLE_ANALOG
+        32, // CIRCLE_ANALOG
+        33, // CROSS_ANALOG
+        34, // SQUARE_ANALOG
+        0, 0, // Skip PS and MOVE
+
+        // Playstation Move Controller
+        15, // T_ANALOG - Both at byte 14 (last reading) and byte 15 (current reading)
+};
+
+enum StatusEnum {
+        // Note that the location is shifted 9 when it's connected via USB
+        // Byte location | bit location
+        Plugged = (38 << 8) | 0x02,
+        Unplugged = (38 << 8) | 0x03,
+
+        Charging = (39 << 8) | 0xEE,
+        NotCharging = (39 << 8) | 0xF1,
+        Shutdown = (39 << 8) | 0x01,
+        Dying = (39 << 8) | 0x02,
+        Low = (39 << 8) | 0x03,
+        High = (39 << 8) | 0x04,
+        Full = (39 << 8) | 0x05,
+
+        MoveCharging = (21 << 8) | 0xEE,
+        MoveNotCharging = (21 << 8) | 0xF1,
+        MoveShutdown = (21 << 8) | 0x01,
+        MoveDying = (21 << 8) | 0x02,
+        MoveLow = (21 << 8) | 0x03,
+        MoveHigh = (21 << 8) | 0x04,
+        MoveFull = (21 << 8) | 0x05,
+
+        CableRumble = (40 << 8) | 0x10, // Operating by USB and rumble is turned on
+        Cable = (40 << 8) | 0x12, // Operating by USB and rumble is turned off
+        BluetoothRumble = (40 << 8) | 0x14, // Operating by Bluetooth and rumble is turned on
+        Bluetooth = (40 << 8) | 0x16, // Operating by Bluetooth and rumble is turned off
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS3USB.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,580 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "stdio.h"
+#include "PS3USB.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers
+
+PS3USB::PS3USB(USB *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0) :
+pUsb(p), // pointer to USB class instance - mandatory
+bAddress(0), // device address - mandatory
+bPollEnable(false) // don't start polling before dongle is connected
+{
+        for(uint8_t i = 0; i < PS3_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+
+        if(pUsb) // register in USB subsystem
+                pUsb->RegisterDeviceClass(this); //set devConfig[] entry
+
+        my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead
+        my_bdaddr[4] = btadr4;
+        my_bdaddr[3] = btadr3;
+        my_bdaddr[2] = btadr2;
+        my_bdaddr[1] = btadr1;
+        my_bdaddr[0] = btadr0;
+        
+        printf("Constractor\n");
+}
+
+uint8_t PS3USB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint16_t PID;
+        uint16_t VID;
+
+        // get memory address of USB device address pool
+        AddressPool &addrPool = pUsb->GetAddressPool();
+        printf("\r\nPS3USB Init");
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nPS3USB Init"), 0x80);
+        std:printf("\r\nPS3USB Init");
+#endif
+        // check if address has already been assigned to an instance
+        if(bAddress) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress in use"), 0x80);
+#endif
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        if(!p->epinfo) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nepinfo is null"), 0x80);
+#endif
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        VID = udd->idVendor;
+        PID = udd->idProduct;
+
+        if(VID != PS3_VID || (PID != PS3_PID && PID != PS3NAVIGATION_PID && PID != PS3MOVE_PID))
+                goto FailUnknownDevice;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nsetAddr: "), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+#endif
+                return rcode;
+        }
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nAddr: "), 0x80);
+        D_PrintHex<uint8_t > (bAddress, 0x80);
+#endif
+        //delay(300); // Spec says you should wait at least 200ms
+
+        p->lowspeed = false;
+
+        //get pointer to assigned address record
+        p = addrPool.GetUsbDevicePtr(bAddress);
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        // Assign epInfo to epinfo pointer - only EP0 is known
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+
+        /* The application will work in reduced host mode, so we can save program and data
+           memory space. After verifying the PID and VID we will use known values for the
+           configuration values for device, interface, endpoints and HID for the PS3 Controllers */
+
+        /* Initialize data structures for endpoints of device */
+        epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint
+        epInfo[ PS3_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ PS3_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ PS3_OUTPUT_PIPE ].bmSndToggle = 0;
+        epInfo[ PS3_OUTPUT_PIPE ].bmRcvToggle = 0;
+        epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint
+        epInfo[ PS3_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ PS3_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ PS3_INPUT_PIPE ].bmSndToggle = 0;
+        epInfo[ PS3_INPUT_PIPE ].bmRcvToggle = 0;
+
+        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        delay(200); //Give time for address change
+
+        rcode = pUsb->setConf(bAddress, epInfo[ PS3_CONTROL_PIPE ].epAddr, 1);
+        if(rcode)
+                goto FailSetConfDescr;
+
+        if(PID == PS3_PID || PID == PS3NAVIGATION_PID) {
+                if(PID == PS3_PID) {
+#ifdef DEBUG_USB_HOST
+                        Notify(PSTR("\r\nDualshock 3 Controller Connected"), 0x80);
+#endif
+                        PS3Connected = true;
+                } else { // must be a navigation controller
+#ifdef DEBUG_USB_HOST
+                        Notify(PSTR("\r\nNavigation Controller Connected"), 0x80);
+#endif
+                        PS3NavigationConnected = true;
+                }
+                enable_sixaxis(); // The PS3 controller needs a special command before it starts sending data
+
+                // Needed for PS3 Dualshock and Navigation commands to work
+                for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)
+                        writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]);
+
+                for(uint8_t i = 6; i < 10; i++)
+                        readBuf[i] = 0x7F; // Set the analog joystick values to center position
+        } else { // must be a Motion controller
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nMotion Controller Connected"), 0x80);
+#endif
+                PS3MoveConnected = true;
+                writeBuf[0] = 0x02; // Set report ID, this is needed for Move commands to work
+        }
+        if(my_bdaddr[0] != 0x00 || my_bdaddr[1] != 0x00 || my_bdaddr[2] != 0x00 || my_bdaddr[3] != 0x00 || my_bdaddr[4] != 0x00 || my_bdaddr[5] != 0x00) {
+                if(PS3MoveConnected)
+                        setMoveBdaddr(my_bdaddr); // Set internal Bluetooth address
+                else
+                        setBdaddr(my_bdaddr); // Set internal Bluetooth address
+
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nBluetooth Address was set to: "), 0x80);
+                for(int8_t i = 5; i > 0; i--) {
+                        D_PrintHex<uint8_t > (my_bdaddr[i], 0x80);
+                        Notify(PSTR(":"), 0x80);
+                }
+                D_PrintHex<uint8_t > (my_bdaddr[0], 0x80);
+#endif
+        }
+        onInit();
+
+        bPollEnable = true;
+        Notify(PSTR("\r\n"), 0x80);
+        timer = (uint32_t)millis();
+        return 0; // Successful configuration
+
+        /* Diagnostic messages */
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+#endif
+        goto Fail;
+
+FailUnknownDevice:
+#ifdef DEBUG_USB_HOST
+        NotifyFailUnknownDevice(VID, PID);
+#endif
+        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+Fail:
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nPS3 Init Failed, error code: "), 0x80);
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+/* Performs a cleanup after failed Init() attempt */
+uint8_t PS3USB::Release() {
+        PS3Connected = false;
+        PS3MoveConnected = false;
+        PS3NavigationConnected = false;
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+        bAddress = 0;
+        bPollEnable = false;
+        return 0;
+}
+
+uint8_t PS3USB::Poll() {
+        if(!bPollEnable)
+                return 0;
+
+        if(PS3Connected || PS3NavigationConnected) {
+                uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
+                pUsb->inTransfer(bAddress, epInfo[ PS3_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
+                if((int32_t)((uint32_t)millis() - timer) > 100) { // Loop 100ms before processing data
+                        readReport();
+#ifdef PRINTREPORT
+                        printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers
+#endif
+                }
+        } else if(PS3MoveConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB
+                if((int32_t)((uint32_t)millis() - timer) > 4000) { // Send at least every 4th second
+                        Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on
+                        timer = (uint32_t)millis();
+                }
+        }
+        return 0;
+}
+
+void PS3USB::readReport() {
+        ButtonState = (uint32_t)(readBuf[2] | ((uint16_t)readBuf[3] << 8) | ((uint32_t)readBuf[4] << 16));
+
+        //Notify(PSTR("\r\nButtonState", 0x80);
+        //PrintHex<uint32_t>(ButtonState, 0x80);
+
+        if(ButtonState != OldButtonState) {
+                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
+                OldButtonState = ButtonState;
+        }
+}
+
+void PS3USB::printReport() { // Uncomment "#define PRINTREPORT" to print the report send by the PS3 Controllers
+#ifdef PRINTREPORT
+        for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++) {
+                D_PrintHex<uint8_t > (readBuf[i], 0x80);
+                Notify(PSTR(" "), 0x80);
+        }
+        Notify(PSTR("\r\n"), 0x80);
+#endif
+}
+
+bool PS3USB::getButtonPress(ButtonEnum b) {
+        return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]));
+}
+
+bool PS3USB::getButtonClick(ButtonEnum b) {
+        uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]);
+        bool click = (ButtonClickState & button);
+        ButtonClickState &= ~button; // Clear "click" event
+        return click;
+}
+
+uint8_t PS3USB::getAnalogButton(ButtonEnum a) {
+        return (uint8_t)(readBuf[(pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])) - 9]);
+}
+
+uint8_t PS3USB::getAnalogHat(AnalogHatEnum a) {
+        return (uint8_t)(readBuf[((uint8_t)a + 6)]);
+}
+
+uint16_t PS3USB::getSensor(SensorEnum a) {
+        return ((readBuf[((uint16_t)a) - 9] << 8) | readBuf[((uint16_t)a + 1) - 9]);
+}
+
+float PS3USB::getAngle(AngleEnum a) {
+        if(PS3Connected) {
+                float accXval, accYval, accZval;
+
+                // Data for the Kionix KXPC4 used in the DualShock 3
+                const float zeroG = 511.5f; // 1.65/3.3*1023 (1,65V)
+                accXval = -((float)getSensor(aX) - zeroG);
+                accYval = -((float)getSensor(aY) - zeroG);
+                accZval = -((float)getSensor(aZ) - zeroG);
+
+                // Convert to 360 degrees resolution
+                // atan2 outputs the value of -π to π (radians)
+                // We are then converting it to 0 to 2π and then to degrees
+                if(a == Pitch)
+                        return (atan2f(accYval, accZval) + PI) * RAD_TO_DEG;
+                else
+                        return (atan2f(accXval, accZval) + PI) * RAD_TO_DEG;
+        } else
+                return 0;
+}
+
+bool PS3USB::getStatus(StatusEnum c) {
+        return (readBuf[((uint16_t)c >> 8) - 9] == ((uint8_t)c & 0xff));
+}
+
+void PS3USB::printStatusString() {
+        char statusOutput[102]; // Max string length plus null character
+        if(PS3Connected || PS3NavigationConnected) {
+                strcpy_P(statusOutput, PSTR("\r\nConnectionStatus: "));
+
+                if(getStatus(Plugged)) strcat_P(statusOutput, PSTR("Plugged"));
+                else if(getStatus(Unplugged)) strcat_P(statusOutput, PSTR("Unplugged"));
+                else strcat_P(statusOutput, PSTR("Error"));
+
+                strcat_P(statusOutput, PSTR(" - PowerRating: "));
+
+                if(getStatus(Charging)) strcat_P(statusOutput, PSTR("Charging"));
+                else if(getStatus(NotCharging)) strcat_P(statusOutput, PSTR("Not Charging"));
+                else if(getStatus(Shutdown)) strcat_P(statusOutput, PSTR("Shutdown"));
+                else if(getStatus(Dying)) strcat_P(statusOutput, PSTR("Dying"));
+                else if(getStatus(Low)) strcat_P(statusOutput, PSTR("Low"));
+                else if(getStatus(High)) strcat_P(statusOutput, PSTR("High"));
+                else if(getStatus(Full)) strcat_P(statusOutput, PSTR("Full"));
+                else strcat_P(statusOutput, PSTR("Error"));
+
+                strcat_P(statusOutput, PSTR(" - WirelessStatus: "));
+
+                if(getStatus(CableRumble)) strcat_P(statusOutput, PSTR("Cable - Rumble is on"));
+                else if(getStatus(Cable)) strcat_P(statusOutput, PSTR("Cable - Rumble is off"));
+                else if(getStatus(BluetoothRumble)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is on"));
+                else if(getStatus(Bluetooth)) strcat_P(statusOutput, PSTR("Bluetooth - Rumble is off"));
+                else strcat_P(statusOutput, PSTR("Error"));
+        } else
+                strcpy_P(statusOutput, PSTR("\r\nError"));
+
+        //////USB_HOST_SERIAL.write(statusOutput);
+}
+
+/* Playstation Sixaxis Dualshock and Navigation Controller commands */
+void PS3USB::PS3_Command(uint8_t *data, uint16_t nbytes) {
+        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x01), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)
+        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x01, 0x02, 0x00, nbytes, nbytes, data, NULL);
+}
+
+void PS3USB::setAllOff() {
+        for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)
+                writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // Reset buffer
+
+        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
+}
+
+void PS3USB::setRumbleOff() {
+        uint8_t rumbleBuf[EP_MAXPKTSIZE];
+        memcpy(rumbleBuf, writeBuf, EP_MAXPKTSIZE);
+        rumbleBuf[1] = 0x00;
+        rumbleBuf[2] = 0x00; // Low mode off
+        rumbleBuf[3] = 0x00;
+        rumbleBuf[4] = 0x00; // High mode off
+        PS3_Command(rumbleBuf, PS3_REPORT_BUFFER_SIZE);
+}
+
+void PS3USB::setRumbleOn(RumbleEnum mode) {
+        if((mode & 0x30) > 0x00) {
+                uint8_t power[2] = {0xff, 0x00}; // Defaults to RumbleLow
+                if(mode == RumbleHigh) {
+                        power[0] = 0x00;
+                        power[1] = 0xff;
+                }
+                setRumbleOn(0xfe, power[0], 0xfe, power[1]);
+        }
+}
+
+void PS3USB::setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower) {
+        uint8_t rumbleBuf[EP_MAXPKTSIZE];
+        memcpy(rumbleBuf, writeBuf, EP_MAXPKTSIZE);
+        rumbleBuf[1] = rightDuration;
+        rumbleBuf[2] = rightPower;
+        rumbleBuf[3] = leftDuration;
+        rumbleBuf[4] = leftPower;
+        PS3_Command(rumbleBuf, PS3_REPORT_BUFFER_SIZE);
+}
+
+void PS3USB::setLedRaw(uint8_t value) {
+        writeBuf[9] = value << 1;
+        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
+}
+
+void PS3USB::setLedOff(LEDEnum a) {
+        writeBuf[9] &= ~((uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1));
+        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
+}
+
+void PS3USB::setLedOn(LEDEnum a) {
+        if(a == OFF)
+                setLedRaw(0);
+        else {
+                writeBuf[9] |= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);
+                PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
+        }
+}
+
+void PS3USB::setLedToggle(LEDEnum a) {
+        writeBuf[9] ^= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);
+        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);
+}
+
+void PS3USB::setBdaddr(uint8_t *bdaddr) {
+        /* Set the internal Bluetooth address */
+        uint8_t buf[8];
+        buf[0] = 0x01;
+        buf[1] = 0x00;
+
+        for(uint8_t i = 0; i < 6; i++)
+                buf[i + 2] = bdaddr[5 - i]; // Copy into buffer, has to be written reversed, so it is MSB first
+
+        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
+        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);
+}
+
+void PS3USB::getBdaddr(uint8_t *bdaddr) {
+        uint8_t buf[8];
+
+        // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
+        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);
+
+        for(uint8_t i = 0; i < 6; i++)
+                bdaddr[5 - i] = buf[i + 2]; // Copy into buffer reversed, so it is LSB first
+}
+
+void PS3USB::enable_sixaxis() { // Command used to enable the Dualshock 3 and Navigation controller to send data via USB
+        uint8_t cmd_buf[4];
+        cmd_buf[0] = 0x42; // Special PS3 Controller enable commands
+        cmd_buf[1] = 0x0c;
+        cmd_buf[2] = 0x00;
+        cmd_buf[3] = 0x00;
+
+        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF4), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data)
+        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF4, 0x03, 0x00, 4, 4, cmd_buf, NULL);
+}
+
+/* Playstation Move Controller commands */
+void PS3USB::Move_Command(uint8_t *data, uint16_t nbytes) {
+        pUsb->outTransfer(bAddress, epInfo[ PS3_OUTPUT_PIPE ].epAddr, nbytes, data);
+}
+
+void PS3USB::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { // Use this to set the Color using RGB values
+        // Set the Bulb's values into the write buffer
+        writeBuf[2] = r;
+        writeBuf[3] = g;
+        writeBuf[4] = b;
+
+        Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE);
+}
+
+void PS3USB::moveSetBulb(ColorsEnum color) { // Use this to set the Color using the predefined colors in "enums.h"
+        moveSetBulb((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));
+}
+
+void PS3USB::moveSetRumble(uint8_t rumble) {
+#ifdef DEBUG_USB_HOST
+        if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100)
+                Notify(PSTR("\r\nThe rumble value has to at least 64, or approximately 25%"), 0x80);
+#endif
+        writeBuf[6] = rumble; // Set the rumble value into the write buffer
+
+        Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE);
+}
+
+void PS3USB::setMoveBdaddr(uint8_t *bdaddr) {
+        /* Set the internal Bluetooth address */
+        uint8_t buf[11];
+        buf[0] = 0x05;
+        buf[7] = 0x10;
+        buf[8] = 0x01;
+        buf[9] = 0x02;
+        buf[10] = 0x12;
+
+        for(uint8_t i = 0; i < 6; i++)
+                buf[i + 1] = bdaddr[i];
+
+        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
+        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00, 11, 11, buf, NULL);
+}
+
+void PS3USB::getMoveBdaddr(uint8_t *bdaddr) {
+        uint8_t buf[16];
+
+        // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0x04), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
+        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0x04, 0x03, 0x00, 16, 16, buf, NULL);
+
+        for(uint8_t i = 0; i < 6; i++)
+                bdaddr[i] = buf[10 + i];
+}
+
+void PS3USB::getMoveCalibration(uint8_t *data) {
+        uint8_t buf[49];
+
+        for(uint8_t i = 0; i < 3; i++) {
+                // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0x10), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data
+                pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0x10, 0x03, 0x00, 49, 49, buf, NULL);
+
+                for(uint8_t j = 0; j < 49; j++)
+                        data[49 * i + j] = buf[j];
+        }
+}
+
+void PS3USB::onInit() {
+        if(pFuncOnInit)
+                pFuncOnInit(); // Call the user function
+        else {
+                if(PS3MoveConnected)
+                        moveSetBulb(Red);
+                else // Dualshock 3 or Navigation controller
+                        setLedOn(static_cast<LEDEnum>(CONTROLLER_LED1));
+        }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS3USB.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,304 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _ps3usb_h_
+#define _ps3usb_h_
+
+#include "Usb.h"
+#include "usbhid.h"
+#include "PS3Enums.h"
+
+/* PS3 data taken from descriptors */
+#define EP_MAXPKTSIZE           64 // max size for data via USB
+
+/* Names we give to the 3 ps3 pipes - this is only used for setting the bluetooth address into the ps3 controllers */
+#define PS3_CONTROL_PIPE        0
+#define PS3_OUTPUT_PIPE         1
+#define PS3_INPUT_PIPE          2
+
+//PID and VID of the different devices
+#define PS3_VID                 0x054C  // Sony Corporation
+#define PS3_PID                 0x0268  // PS3 Controller DualShock 3
+#define PS3NAVIGATION_PID       0x042F  // Navigation controller
+#define PS3MOVE_PID             0x03D5  // Motion controller
+
+#define PS3_MAX_ENDPOINTS       3
+
+/**
+ * This class implements support for all the official PS3 Controllers:
+ * Dualshock 3, Navigation or a Motion controller via USB.
+ *
+ * One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB on the Move controller.
+ *
+ * Information about the protocol can be found at the wiki: https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information.
+ */
+class PS3USB : public USBDeviceConfig {
+public:
+        /**
+         * Constructor for the PS3USB class.
+         * @param  pUsb   Pointer to USB class instance.
+         * @param  btadr5,btadr4,btadr3,btadr2,btadr1,btadr0
+         * Pass your dongles Bluetooth address into the constructor,
+         * so you are able to pair the controller with a Bluetooth dongle.
+         */
+        PS3USB(USB *pUsb, uint8_t btadr5 = 0, uint8_t btadr4 = 0, uint8_t btadr3 = 0, uint8_t btadr2 = 0, uint8_t btadr1 = 0, uint8_t btadr0 = 0);
+
+        /** @name USBDeviceConfig implementation */
+        /**
+         * Initialize the PS3 Controller.
+         * @param  parent   Hub number.
+         * @param  port     Port number on the hub.
+         * @param  lowspeed Speed of the device.
+         * @return          0 on success.
+         */
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        /**
+         * Release the USB device.
+         * @return 0 on success.
+         */
+        uint8_t Release();
+        /**
+         * Poll the USB Input endpoins and run the state machines.
+         * @return 0 on success.
+         */
+        uint8_t Poll();
+
+        /**
+         * Get the device address.
+         * @return The device address.
+         */
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        /**
+         * Used to check if the controller has been initialized.
+         * @return True if it's ready.
+         */
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return (vid == PS3_VID && (pid == PS3_PID || pid == PS3NAVIGATION_PID || pid == PS3MOVE_PID));
+        };
+        /**@}*/
+
+        /**
+         * Used to set the Bluetooth address inside the Dualshock 3 and Navigation controller.
+         * Set using LSB first.
+         * @param bdaddr Your dongles Bluetooth address.
+         */
+        void setBdaddr(uint8_t *bdaddr);
+        /**
+         * Used to get the Bluetooth address inside the Dualshock 3 and Navigation controller.
+         * Will return LSB first.
+         * @param bdaddr Your dongles Bluetooth address.
+         */
+        void getBdaddr(uint8_t *bdaddr);
+
+        /**
+         * Used to set the Bluetooth address inside the Move controller.
+         * Set using LSB first.
+         * @param bdaddr Your dongles Bluetooth address.
+         */
+        void setMoveBdaddr(uint8_t *bdaddr);
+        /**
+         * Used to get the Bluetooth address inside the Move controller.
+         * Will return LSB first.
+         * @param bdaddr Your dongles Bluetooth address.
+         */
+        void getMoveBdaddr(uint8_t *bdaddr);
+        /**
+         * Used to get the calibration data inside the Move controller.
+         * @param data Buffer to store data in. Must be at least 147 bytes
+         */
+        void getMoveCalibration(uint8_t *data);
+
+        /** @name PS3 Controller functions */
+        /**
+         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(ButtonEnum b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
+         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
+         * @param  b          ::ButtonEnum to read.
+         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
+         */
+        bool getButtonPress(ButtonEnum b);
+        bool getButtonClick(ButtonEnum b);
+        /**@}*/
+        /** @name PS3 Controller functions */
+        /**
+         * Used to get the analog value from button presses.
+         * @param  a The ::ButtonEnum to read.
+         * The supported buttons are:
+         * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,
+         * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.
+         * @return   Analog value in the range of 0-255.
+         */
+        uint8_t getAnalogButton(ButtonEnum a);
+        /**
+         * Used to read the analog joystick.
+         * @param  a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
+         * @return   Return the analog value in the range of 0-255.
+         */
+        uint8_t getAnalogHat(AnalogHatEnum a);
+        /**
+         * Used to read the sensors inside the Dualshock 3 controller.
+         * @param  a
+         * The Dualshock 3 has a 3-axis accelerometer and a 1-axis gyro inside.
+         * @return   Return the raw sensor value.
+         */
+        uint16_t getSensor(SensorEnum a);
+        /**
+         * Use this to get ::Pitch and ::Roll calculated using the accelerometer.
+         * @param  a Either ::Pitch or ::Roll.
+         * @return   Return the angle in the range of 0-360.
+         */
+        float getAngle(AngleEnum a);
+        /**
+         * Get the ::StatusEnum from the controller.
+         * @param  c The ::StatusEnum you want to read.
+         * @return   True if correct and false if not.
+         */
+        bool getStatus(StatusEnum c);
+        /** Read all the available statuses from the controller and prints it as a nice formated string. */
+        void printStatusString();
+
+        /** Used to set all LEDs and rumble off. */
+        void setAllOff();
+        /** Turn off rumble. */
+        void setRumbleOff();
+        /**
+         * Turn on rumble.
+         * @param mode Either ::RumbleHigh or ::RumbleLow.
+         */
+        void setRumbleOn(RumbleEnum mode);
+        /**
+         * Turn on rumble using custom duration and power.
+         * @param rightDuration The duration of the right/low rumble effect.
+         * @param rightPower The intensity of the right/low rumble effect.
+         * @param leftDuration The duration of the left/high rumble effect.
+         * @param leftPower The intensity of the left/high rumble effect.
+         */
+        void setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower);
+
+        /**
+         * Set LED value without using the ::LEDEnum.
+         * @param value See: ::LEDEnum.
+         */
+        void setLedRaw(uint8_t value);
+
+        /** Turn all LEDs off. */
+        void setLedOff() {
+                setLedRaw(0);
+        }
+        /**
+         * Turn the specific ::LEDEnum off.
+         * @param a The ::LEDEnum to turn off.
+         */
+        void setLedOff(LEDEnum a);
+        /**
+         * Turn the specific ::LEDEnum on.
+         * @param a The ::LEDEnum to turn on.
+         */
+        void setLedOn(LEDEnum a);
+        /**
+         * Toggle the specific ::LEDEnum.
+         * @param a The ::LEDEnum to toggle.
+         */
+        void setLedToggle(LEDEnum a);
+
+        /**
+         * Use this to set the Color using RGB values.
+         * @param r,g,b RGB value.
+         */
+        void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);
+        /**
+         * Use this to set the color using the predefined colors in ::ColorsEnum.
+         * @param color The desired color.
+         */
+        void moveSetBulb(ColorsEnum color);
+        /**
+         * Set the rumble value inside the Move controller.
+         * @param rumble The desired value in the range from 64-255.
+         */
+        void moveSetRumble(uint8_t rumble);
+
+        /**
+         * Used to call your own function when the controller is successfully initialized.
+         * @param funcOnInit Function to call.
+         */
+        void attachOnInit(void (*funcOnInit)(void)) {
+                pFuncOnInit = funcOnInit;
+        };
+        /**@}*/
+
+        /** Variable used to indicate if the normal playstation controller is successfully connected. */
+        bool PS3Connected;
+        /** Variable used to indicate if the move controller is successfully connected. */
+        bool PS3MoveConnected;
+        /** Variable used to indicate if the navigation controller is successfully connected. */
+        bool PS3NavigationConnected;
+
+protected:
+        /** Pointer to USB class instance. */
+        USB *pUsb;
+        /** Device address. */
+        uint8_t bAddress;
+        /** Endpoint info structure. */
+        EpInfo epInfo[PS3_MAX_ENDPOINTS];
+
+private:
+        /**
+         * Called when the controller is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        void onInit();
+        void (*pFuncOnInit)(void); // Pointer to function called in onInit()
+
+        bool bPollEnable;
+
+        uint32_t timer; // used to continuously set PS3 Move controller Bulb and rumble values
+
+        uint32_t ButtonState;
+        uint32_t OldButtonState;
+        uint32_t ButtonClickState;
+
+        uint8_t my_bdaddr[6]; // Change to your dongles Bluetooth address in the constructor
+        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data
+        uint8_t writeBuf[EP_MAXPKTSIZE]; // General purpose buffer for output data
+
+        void readReport(); // read incoming data
+        void printReport(); // print incoming date - Uncomment for debugging
+
+        /* Private commands */
+        void PS3_Command(uint8_t *data, uint16_t nbytes);
+        void enable_sixaxis(); // Command used to enable the Dualshock 3 and Navigation controller to send data via USB
+        void Move_Command(uint8_t *data, uint16_t nbytes);
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS4BT.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,122 @@
+/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _ps4bt_h_
+#define _ps4bt_h_
+
+#include "BTHID.h"
+#include "PS4Parser.h"
+
+/**
+ * This class implements support for the PS4 controller via Bluetooth.
+ * It uses the BTHID class for all the Bluetooth communication.
+ */
+class PS4BT : public BTHID, public PS4Parser {
+public:
+        /**
+         * Constructor for the PS4BT class.
+         * @param  p     Pointer to the BTD class instance.
+         * @param  pair  Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true.
+         * @param  pin   Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
+         */
+        PS4BT(BTD *p, bool pair = false, const char *pin = "0000") :
+        BTHID(p, pair, pin) {
+                PS4Parser::Reset();
+        };
+
+        /**
+         * Used to check if a PS4 controller is connected.
+         * @return Returns true if it is connected.
+         */
+        bool connected() {
+                return BTHID::connected;
+        };
+
+protected:
+        /** @name BTHID implementation */
+        /**
+         * Used to parse Bluetooth HID data.
+         * @param len The length of the incoming data.
+         * @param buf Pointer to the data buffer.
+         */
+        virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) {
+                PS4Parser::Parse(len, buf);
+        };
+
+        /**
+         * Called when a device is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        virtual void OnInitBTHID() {
+                PS4Parser::Reset();
+                enable_sixaxis(); // Make the controller send out the entire output report
+                if (pFuncOnInit)
+                        pFuncOnInit(); // Call the user function
+                else
+                        setLed(Blue);
+        };
+
+        /** Used to reset the different buffers to there default values */
+        virtual void ResetBTHID() {
+                PS4Parser::Reset();
+        };
+        /**@}*/
+
+        /** @name PS4Parser implementation */
+        virtual void sendOutputReport(PS4Output *output) { // Source: https://github.com/chrippa/ds4drv
+                uint8_t buf[79];
+                memset(buf, 0, sizeof(buf));
+
+                buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02)
+                buf[1] = 0x11; // Report ID
+                buf[2] = 0x80;
+                buf[4]= 0xFF;
+
+                buf[7] = output->smallRumble; // Small Rumble
+                buf[8] = output->bigRumble; // Big rumble
+
+                buf[9] = output->r; // Red
+                buf[10] = output->g; // Green
+                buf[11] = output->b; // Blue
+
+                buf[12] = output->flashOn; // Time to flash bright (255 = 2.5 seconds)
+                buf[13] = output->flashOff; // Time to flash dark (255 = 2.5 seconds)
+
+                output->reportChanged = false;
+
+                // The PS4 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed
+
+                HID_Command(buf, sizeof(buf));
+        };
+        /**@}*/
+
+private:
+        void enable_sixaxis() { // Command used to make the PS4 controller send out the entire output report
+                uint8_t buf[2];
+                buf[0] = 0x43; // HID BT Get_report (0x40) | Report Type (Feature 0x03)
+                buf[1] = 0x02; // Report ID
+
+                HID_Command(buf, 2);
+        };
+
+        void HID_Command(uint8_t *data, uint8_t nbytes) {
+                pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]);
+        };
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS4Parser.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,154 @@
+/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "PS4Parser.h"
+
+enum DPADEnum {
+        DPAD_UP = 0x0,
+        DPAD_UP_RIGHT = 0x1,
+        DPAD_RIGHT = 0x2,
+        DPAD_RIGHT_DOWN = 0x3,
+        DPAD_DOWN = 0x4,
+        DPAD_DOWN_LEFT = 0x5,
+        DPAD_LEFT = 0x6,
+        DPAD_LEFT_UP = 0x7,
+        DPAD_OFF = 0x8,
+};
+
+// To enable serial debugging see "settings.h"
+//#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller
+
+bool PS4Parser::checkDpad(ButtonEnum b) {
+        switch (b) {
+                case UP:
+                        return ps4Data.btn.dpad == DPAD_LEFT_UP || ps4Data.btn.dpad == DPAD_UP || ps4Data.btn.dpad == DPAD_UP_RIGHT;
+                case RIGHT:
+                        return ps4Data.btn.dpad == DPAD_UP_RIGHT || ps4Data.btn.dpad == DPAD_RIGHT || ps4Data.btn.dpad == DPAD_RIGHT_DOWN;
+                case DOWN:
+                        return ps4Data.btn.dpad == DPAD_RIGHT_DOWN || ps4Data.btn.dpad == DPAD_DOWN || ps4Data.btn.dpad == DPAD_DOWN_LEFT;
+                case LEFT:
+                        return ps4Data.btn.dpad == DPAD_DOWN_LEFT || ps4Data.btn.dpad == DPAD_LEFT || ps4Data.btn.dpad == DPAD_LEFT_UP;
+                default:
+                        return false;
+        }
+}
+
+bool PS4Parser::getButtonPress(ButtonEnum b) {
+        if (b <= LEFT) // Dpad
+                return checkDpad(b);
+        else
+                return ps4Data.btn.val & (1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b]));
+}
+
+bool PS4Parser::getButtonClick(ButtonEnum b) {
+        uint32_t mask = 1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b]);
+        bool click = buttonClickState.val & mask;
+        buttonClickState.val &= ~mask; // Clear "click" event
+        return click;
+}
+
+uint8_t PS4Parser::getAnalogButton(ButtonEnum b) {
+        if (b == L2) // These are the only analog buttons on the controller
+                return ps4Data.trigger[0];
+        else if (b == R2)
+                return ps4Data.trigger[1];
+        return 0;
+}
+
+uint8_t PS4Parser::getAnalogHat(AnalogHatEnum a) {
+        return ps4Data.hatValue[(uint8_t)a];
+}
+
+void PS4Parser::Parse(uint8_t len, uint8_t *buf) {
+        if (len > 1 && buf)  {
+#ifdef PRINTREPORT
+                Notify(PSTR("\r\n"), 0x80);
+                for (uint8_t i = 0; i < len; i++) {
+                        D_PrintHex<uint8_t > (buf[i], 0x80);
+                        Notify(PSTR(" "), 0x80);
+                }
+#endif
+
+                if (buf[0] == 0x01) // Check report ID
+                        memcpy(&ps4Data, buf + 1, min((uint8_t)(len - 1), MFK_CASTUINT8T sizeof(ps4Data)));
+                else if (buf[0] == 0x11) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data
+                        if (len < 4) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nReport is too short: "), 0x80);
+                                D_PrintHex<uint8_t > (len, 0x80);
+#endif
+                                return;
+                        }
+                        memcpy(&ps4Data, buf + 3, min((uint8_t)(len - 3), MFK_CASTUINT8T sizeof(ps4Data)));
+                } else {
+#ifdef DEBUG_USB_HOST
+                        Notify(PSTR("\r\nUnknown report id: "), 0x80);
+                        D_PrintHex<uint8_t > (buf[0], 0x80);
+#endif
+                        return;
+                }
+
+                if (ps4Data.btn.val != oldButtonState.val) { // Check if anything has changed
+                        buttonClickState.val = ps4Data.btn.val & ~oldButtonState.val; // Update click state variable
+                        oldButtonState.val = ps4Data.btn.val;
+
+                        // The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself
+                        uint8_t newDpad = 0;
+                        if (checkDpad(UP))
+                                newDpad |= 1 << UP;
+                        if (checkDpad(RIGHT))
+                                newDpad |= 1 << RIGHT;
+                        if (checkDpad(DOWN))
+                                newDpad |= 1 << DOWN;
+                        if (checkDpad(LEFT))
+                                newDpad |= 1 << LEFT;
+                        if (newDpad != oldDpad) {
+                                buttonClickState.dpad = newDpad & ~oldDpad; // Override values
+                                oldDpad = newDpad;
+                        }
+                }
+        }
+
+        if (ps4Output.reportChanged)
+                sendOutputReport(&ps4Output); // Send output report
+}
+
+void PS4Parser::Reset() {
+        uint8_t i;
+        for (i = 0; i < sizeof(ps4Data.hatValue); i++)
+                ps4Data.hatValue[i] = 127; // Center value
+        ps4Data.btn.val = 0;
+        oldButtonState.val = 0;
+        for (i = 0; i < sizeof(ps4Data.trigger); i++)
+                ps4Data.trigger[i] = 0;
+        for (i = 0; i < sizeof(ps4Data.xy)/sizeof(ps4Data.xy[0]); i++) {
+                for (uint8_t j = 0; j < sizeof(ps4Data.xy[0].finger)/sizeof(ps4Data.xy[0].finger[0]); j++)
+                        ps4Data.xy[i].finger[j].touching = 1; // The bit is cleared if the finger is touching the touchpad
+        }
+
+        ps4Data.btn.dpad = DPAD_OFF;
+        oldButtonState.dpad = DPAD_OFF;
+        buttonClickState.dpad = 0;
+        oldDpad = 0;
+
+        ps4Output.bigRumble = ps4Output.smallRumble = 0;
+        ps4Output.r = ps4Output.g = ps4Output.b = 0;
+        ps4Output.flashOn = ps4Output.flashOff = 0;
+        ps4Output.reportChanged = false;
+};
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS4Parser.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,373 @@
+/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _ps4parser_h_
+#define _ps4parser_h_
+
+#include "Usb.h"
+#include "controllerEnums.h"
+
+/** Buttons on the controller */
+const uint8_t PS4_BUTTONS[] PROGMEM = {
+        UP, // UP
+        RIGHT, // RIGHT
+        DOWN, // DOWN
+        LEFT, // LEFT
+
+        0x0C, // SHARE
+        0x0D, // OPTIONS
+        0x0E, // L3
+        0x0F, // R3
+
+        0x0A, // L2
+        0x0B, // R2
+        0x08, // L1
+        0x09, // R1
+
+        0x07, // TRIANGLE
+        0x06, // CIRCLE
+        0x05, // CROSS
+        0x04, // SQUARE
+
+        0x10, // PS
+        0x11, // TOUCHPAD
+};
+
+union PS4Buttons {
+        struct {
+                uint8_t dpad : 4;
+                uint8_t square : 1;
+                uint8_t cross : 1;
+                uint8_t circle : 1;
+                uint8_t triangle : 1;
+
+                uint8_t l1 : 1;
+                uint8_t r1 : 1;
+                uint8_t l2 : 1;
+                uint8_t r2 : 1;
+                uint8_t share : 1;
+                uint8_t options : 1;
+                uint8_t l3 : 1;
+                uint8_t r3 : 1;
+
+                uint8_t ps : 1;
+                uint8_t touchpad : 1;
+                uint8_t reportCounter : 6;
+        } __attribute__((packed));
+        uint32_t val : 24;
+} __attribute__((packed));
+
+struct touchpadXY {
+        uint8_t dummy; // I can not figure out what this data is for, it seems to change randomly, maybe a timestamp?
+        struct {
+                uint8_t counter : 7; // Increments every time a finger is touching the touchpad
+                uint8_t touching : 1; // The top bit is cleared if the finger is touching the touchpad
+                uint16_t x : 12;
+                uint16_t y : 12;
+        } __attribute__((packed)) finger[2]; // 0 = first finger, 1 = second finger
+} __attribute__((packed));
+
+struct PS4Status {
+        uint8_t battery : 4;
+        uint8_t usb : 1;
+        uint8_t audio : 1;
+        uint8_t mic : 1;
+        uint8_t unknown : 1; // Extension port?
+} __attribute__((packed));
+
+struct PS4Data {
+        /* Button and joystick values */
+        uint8_t hatValue[4];
+        PS4Buttons btn;
+        uint8_t trigger[2];
+
+        /* Gyro and accelerometer values */
+        uint8_t dummy[3]; // First two looks random, while the third one might be some kind of status - it increments once in a while
+        int16_t gyroY, gyroZ, gyroX;
+        int16_t accX, accZ, accY;
+
+        uint8_t dummy2[5];
+        PS4Status status;
+        uint8_t dummy3[3];
+
+        /* The rest is data for the touchpad */
+        touchpadXY xy[3]; // It looks like it sends out three coordinates each time, this might be because the microcontroller inside the PS4 controller is much faster than the Bluetooth connection.
+                          // The last data is read from the last position in the array while the oldest measurement is from the first position.
+                          // The first position will also keep it's value after the finger is released, while the other two will set them to zero.
+                          // Note that if you read fast enough from the device, then only the first one will contain any data.
+
+        // The last three bytes are always: 0x00, 0x80, 0x00
+} __attribute__((packed));
+
+struct PS4Output {
+        uint8_t bigRumble, smallRumble; // Rumble
+        uint8_t r, g, b; // RGB
+        uint8_t flashOn, flashOff; // Time to flash bright/dark (255 = 2.5 seconds)
+        bool reportChanged; // The data is send when data is received from the controller
+} __attribute__((packed));
+
+/** This class parses all the data sent by the PS4 controller */
+class PS4Parser {
+public:
+        /** Constructor for the PS4Parser class. */
+        PS4Parser() {
+                Reset();
+        };
+
+        /** @name PS4 Controller functions */
+        /**
+         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(ButtonEnum b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
+         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
+         * @param  b          ::ButtonEnum to read.
+         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
+         */
+        bool getButtonPress(ButtonEnum b);
+        bool getButtonClick(ButtonEnum b);
+        /**@}*/
+        /** @name PS4 Controller functions */
+        /**
+         * Used to get the analog value from button presses.
+         * @param  b The ::ButtonEnum to read.
+         * The supported buttons are:
+         * ::L2 and ::R2.
+         * @return   Analog value in the range of 0-255.
+         */
+        uint8_t getAnalogButton(ButtonEnum b);
+
+        /**
+         * Used to read the analog joystick.
+         * @param  a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.
+         * @return   Return the analog value in the range of 0-255.
+         */
+        uint8_t getAnalogHat(AnalogHatEnum a);
+
+        /**
+         * Get the x-coordinate of the touchpad. Position 0 is in the top left.
+         * @param  finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
+         * @param  xyId   The controller sends out three packets with the same structure.
+         *                The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
+         *                For that reason it will be set to 0 if the argument is omitted.
+         * @return        Returns the x-coordinate of the finger.
+         */
+        uint16_t getX(uint8_t finger = 0, uint8_t xyId = 0) {
+                return ps4Data.xy[xyId].finger[finger].x;
+        };
+
+        /**
+         * Get the y-coordinate of the touchpad. Position 0 is in the top left.
+         * @param  finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
+         * @param  xyId   The controller sends out three packets with the same structure.
+         *                The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
+         *                For that reason it will be set to 0 if the argument is omitted.
+         * @return        Returns the y-coordinate of the finger.
+         */
+        uint16_t getY(uint8_t finger = 0, uint8_t xyId = 0) {
+                return ps4Data.xy[xyId].finger[finger].y;
+        };
+
+        /**
+         * Returns whenever the user is toucing the touchpad.
+         * @param  finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
+         * @param  xyId   The controller sends out three packets with the same structure.
+         *                The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
+         *                For that reason it will be set to 0 if the argument is omitted.
+         * @return        Returns true if the specific finger is touching the touchpad.
+         */
+        bool isTouching(uint8_t finger = 0, uint8_t xyId = 0) {
+                return !(ps4Data.xy[xyId].finger[finger].touching); // The bit is cleared when a finger is touching the touchpad
+        };
+
+        /**
+         * This counter increments every time a finger touches the touchpad.
+         * @param  finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.
+         * @param  xyId   The controller sends out three packets with the same structure.
+         *                The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.
+         *                For that reason it will be set to 0 if the argument is omitted.
+         * @return        Return the value of the counter, note that it is only a 7-bit value.
+         */
+        uint8_t getTouchCounter(uint8_t finger = 0, uint8_t xyId = 0) {
+                return ps4Data.xy[xyId].finger[finger].counter;
+        };
+
+        /**
+         * Get the angle of the controller calculated using the accelerometer.
+         * @param  a Either ::Pitch or ::Roll.
+         * @return   Return the angle in the range of 0-360.
+         */
+        float getAngle(AngleEnum a) {
+                if (a == Pitch)
+                        return (atan2f(ps4Data.accY, ps4Data.accZ) + PI) * RAD_TO_DEG;
+                else
+                        return (atan2f(ps4Data.accX, ps4Data.accZ) + PI) * RAD_TO_DEG;
+        };
+
+        /**
+         * Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS4 controller.
+         * @param  s The sensor to read.
+         * @return   Returns the raw sensor reading.
+         */
+        int16_t getSensor(SensorEnum s) {
+                switch(s) {
+                        case gX:
+                                return ps4Data.gyroX;
+                        case gY:
+                                return ps4Data.gyroY;
+                        case gZ:
+                                return ps4Data.gyroZ;
+                        case aX:
+                                return ps4Data.accX;
+                        case aY:
+                                return ps4Data.accY;
+                        case aZ:
+                                return ps4Data.accZ;
+                        default:
+                                return 0;
+                }
+        };
+
+        /**
+         * Return the battery level of the PS4 controller.
+         * @return The battery level in the range 0-15.
+         */
+        uint8_t getBatteryLevel() {
+                return ps4Data.status.battery;
+        };
+
+        /**
+         * Use this to check if an USB cable is connected to the PS4 controller.
+         * @return Returns true if an USB cable is connected.
+         */
+        bool getUsbStatus() {
+                return ps4Data.status.usb;
+        };
+
+        /**
+         * Use this to check if an audio jack cable is connected to the PS4 controller.
+         * @return Returns true if an audio jack cable is connected.
+         */
+        bool getAudioStatus() {
+                return ps4Data.status.audio;
+        };
+
+        /**
+         * Use this to check if a microphone is connected to the PS4 controller.
+         * @return Returns true if a microphone is connected.
+         */
+        bool getMicStatus() {
+                return ps4Data.status.mic;
+        };
+
+        /** Turn both rumble and the LEDs off. */
+        void setAllOff() {
+                setRumbleOff();
+                setLedOff();
+        };
+
+        /** Set rumble off. */
+        void setRumbleOff() {
+                setRumbleOn(0, 0);
+        };
+
+        /**
+         * Turn on rumble.
+         * @param mode Either ::RumbleHigh or ::RumbleLow.
+         */
+        void setRumbleOn(RumbleEnum mode) {
+                if (mode == RumbleLow)
+                        setRumbleOn(0x00, 0xFF);
+                else
+                        setRumbleOn(0xFF, 0x00);
+        };
+
+        /**
+         * Turn on rumble.
+         * @param bigRumble   Value for big motor.
+         * @param smallRumble Value for small motor.
+         */
+        void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) {
+                ps4Output.bigRumble = bigRumble;
+                ps4Output.smallRumble = smallRumble;
+                ps4Output.reportChanged = true;
+        };
+
+        /** Turn all LEDs off. */
+        void setLedOff() {
+                setLed(0, 0, 0);
+        };
+
+        /**
+         * Use this to set the color using RGB values.
+         * @param r,g,b RGB value.
+         */
+        void setLed(uint8_t r, uint8_t g, uint8_t b) {
+                ps4Output.r = r;
+                ps4Output.g = g;
+                ps4Output.b = b;
+                ps4Output.reportChanged = true;
+        };
+
+        /**
+         * Use this to set the color using the predefined colors in ::ColorsEnum.
+         * @param color The desired color.
+         */
+        void setLed(ColorsEnum color) {
+                setLed((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));
+        };
+
+        /**
+         * Set the LEDs flash time.
+         * @param flashOn  Time to flash bright (255 = 2.5 seconds).
+         * @param flashOff Time to flash dark (255 = 2.5 seconds).
+         */
+        void setLedFlash(uint8_t flashOn, uint8_t flashOff) {
+                ps4Output.flashOn = flashOn;
+                ps4Output.flashOff = flashOff;
+                ps4Output.reportChanged = true;
+        };
+        /**@}*/
+
+protected:
+        /**
+         * Used to parse data sent from the PS4 controller.
+         * @param len Length of the data.
+         * @param buf Pointer to the data buffer.
+         */
+        void Parse(uint8_t len, uint8_t *buf);
+
+        /** Used to reset the different buffers to their default values */
+        void Reset();
+
+        /**
+         * Send the output to the PS4 controller. This is implemented in PS4BT.h and PS4USB.h.
+         * @param output Pointer to PS4Output buffer;
+         */
+        virtual void sendOutputReport(PS4Output *output) = 0;
+
+private:
+        bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons
+
+        PS4Data ps4Data;
+        PS4Buttons oldButtonState, buttonClickState;
+        PS4Output ps4Output;
+        uint8_t oldDpad;
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PS4USB.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,132 @@
+/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _ps4usb_h_
+#define _ps4usb_h_
+
+#include "hiduniversal.h"
+#include "PS4Parser.h"
+
+#define PS4_VID         0x054C // Sony Corporation
+#define PS4_PID         0x05C4 // PS4 Controller
+#define PS4_PID_SLIM    0x09CC // PS4 Slim Controller
+
+/**
+ * This class implements support for the PS4 controller via USB.
+ * It uses the HIDUniversal class for all the USB communication.
+ */
+class PS4USB : public HIDUniversal, public PS4Parser {
+public:
+        /**
+         * Constructor for the PS4USB class.
+         * @param  p   Pointer to the USB class instance.
+         */
+        PS4USB(USB *p) :
+        HIDUniversal(p) {
+                PS4Parser::Reset();
+        };
+
+        /**
+         * Used to check if a PS4 controller is connected.
+         * @return Returns true if it is connected.
+         */
+        bool connected() {
+                return HIDUniversal::isReady() && HIDUniversal::VID == PS4_VID && (HIDUniversal::PID == PS4_PID || HIDUniversal::PID == PS4_PID_SLIM);
+        };
+
+        /**
+         * Used to call your own function when the device is successfully initialized.
+         * @param funcOnInit Function to call.
+         */
+        void attachOnInit(void (*funcOnInit)(void)) {
+                pFuncOnInit = funcOnInit;
+        };
+
+protected:
+        /** @name HIDUniversal implementation */
+        /**
+         * Used to parse USB HID data.
+         * @param hid       Pointer to the HID class.
+         * @param is_rpt_id Only used for Hubs.
+         * @param len       The length of the incoming data.
+         * @param buf       Pointer to the data buffer.
+         */
+        virtual void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {
+                if (HIDUniversal::VID == PS4_VID && (HIDUniversal::PID == PS4_PID || HIDUniversal::PID == PS4_PID_SLIM))
+                        PS4Parser::Parse(len, buf);
+        };
+
+        /**
+         * Called when a device is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        virtual uint8_t OnInitSuccessful() {
+                if (HIDUniversal::VID == PS4_VID && (HIDUniversal::PID == PS4_PID || HIDUniversal::PID == PS4_PID_SLIM)) {
+                        PS4Parser::Reset();
+                        if (pFuncOnInit)
+                                pFuncOnInit(); // Call the user function
+                        else
+                                setLed(Blue);
+                };
+                return 0;
+        };
+        /**@}*/
+
+        /** @name PS4Parser implementation */
+        virtual void sendOutputReport(PS4Output *output) { // Source: https://github.com/chrippa/ds4drv
+                uint8_t buf[32];
+                memset(buf, 0, sizeof(buf));
+
+                buf[0] = 0x05; // Report ID
+                buf[1]= 0xFF;
+
+                buf[4] = output->smallRumble; // Small Rumble
+                buf[5] = output->bigRumble; // Big rumble
+
+                buf[6] = output->r; // Red
+                buf[7] = output->g; // Green
+                buf[8] = output->b; // Blue
+
+                buf[9] = output->flashOn; // Time to flash bright (255 = 2.5 seconds)
+                buf[10] = output->flashOff; // Time to flash dark (255 = 2.5 seconds)
+
+                output->reportChanged = false;
+
+                // The PS4 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed
+
+                pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, sizeof(buf), buf);
+        };
+        /**@}*/
+
+        /** @name USBDeviceConfig implementation */
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return (vid == PS4_VID && (pid == PS4_PID || HIDUniversal::PID == PS4_PID_SLIM));
+        };
+        /**@}*/
+
+private:
+        void (*pFuncOnInit)(void); // Pointer to function called in onInit()
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PSBuzz.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,83 @@
+/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "PSBuzz.h"
+
+// To enable serial debugging see "settings.h"
+//#define PRINTREPORT // Uncomment to print the report send by the PS Buzz Controllers
+
+void PSBuzz::ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) {
+        if (HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID && len > 2 && buf) {
+#ifdef PRINTREPORT
+                Notify(PSTR("\r\n"), 0x80);
+                for (uint8_t i = 0; i < len; i++) {
+                        D_PrintHex<uint8_t > (buf[i], 0x80);
+                        Notify(PSTR(" "), 0x80);
+                }
+#endif
+                memcpy(&psbuzzButtons, buf + 2, min((uint8_t)(len - 2), MFK_CASTUINT8T sizeof(psbuzzButtons)));
+
+                if (psbuzzButtons.val != oldButtonState.val) { // Check if anything has changed
+                        buttonClickState.val = psbuzzButtons.val & ~oldButtonState.val; // Update click state variable
+                        oldButtonState.val = psbuzzButtons.val;
+                }
+        }
+};
+
+uint8_t PSBuzz::OnInitSuccessful() {
+        if (HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID) {
+                Reset();
+                if (pFuncOnInit)
+                        pFuncOnInit(); // Call the user function
+                else
+                        setLedOnAll(); // Turn the LED on, on all four controllers
+        };
+        return 0;
+};
+
+bool PSBuzz::getButtonPress(ButtonEnum b, uint8_t controller) {
+        return psbuzzButtons.val & (1UL << (b + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller
+};
+
+bool PSBuzz::getButtonClick(ButtonEnum b, uint8_t controller) {
+        uint32_t mask = (1UL << (b + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller
+        bool click = buttonClickState.val & mask;
+        buttonClickState.val &= ~mask; // Clear "click" event
+        return click;
+};
+
+// Source: http://www.developerfusion.com/article/84338/making-usb-c-friendly/ and https://github.com/torvalds/linux/blob/master/drivers/hid/hid-sony.c
+void PSBuzz::setLedRaw(bool value, uint8_t controller) {
+        ledState[controller] = value; // Save value for next time it is called
+
+        uint8_t buf[7];
+        buf[0] = 0x00;
+        buf[1] = ledState[0] ? 0xFF : 0x00;
+        buf[2] = ledState[1] ? 0xFF : 0x00;
+        buf[3] = ledState[2] ? 0xFF : 0x00;
+        buf[4] = ledState[3] ? 0xFF : 0x00;
+        buf[5] = 0x00;
+        buf[6] = 0x00;
+
+        PSBuzz_Command(buf, sizeof(buf));
+};
+
+void PSBuzz::PSBuzz_Command(uint8_t *data, uint16_t nbytes) {
+        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)
+        pUsb->ctrlReq(bAddress, epInfo[0].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/PSBuzz.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,186 @@
+/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _psbuzz_h_
+#define _psbuzz_h_
+
+#include "hiduniversal.h"
+#include "controllerEnums.h"
+
+#define PSBUZZ_VID 0x054C // Sony Corporation
+#define PSBUZZ_PID 0x1000 // PS Buzz Controller
+
+/** Struct used to easily read the different buttons on the controllers */
+union PSBUZZButtons {
+        struct {
+                uint8_t red : 1;
+                uint8_t yellow : 1;
+                uint8_t green : 1;
+                uint8_t orange : 1;
+                uint8_t blue : 1;
+        } __attribute__((packed)) btn[4];
+        uint32_t val : 20;
+} __attribute__((packed));
+
+/**
+ * This class implements support for the PS Buzz controllers via USB.
+ * It uses the HIDUniversal class for all the USB communication.
+ */
+class PSBuzz : public HIDUniversal {
+public:
+        /**
+         * Constructor for the PSBuzz class.
+         * @param  p   Pointer to the USB class instance.
+         */
+        PSBuzz(USB *p) :
+        HIDUniversal(p) {
+                Reset();
+        };
+
+        /**
+         * Used to check if a PS Buzz controller is connected.
+         * @return Returns true if it is connected.
+         */
+        bool connected() {
+                return HIDUniversal::isReady() && HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID;
+        };
+
+        /**
+         * Used to call your own function when the device is successfully initialized.
+         * @param funcOnInit Function to call.
+         */
+        void attachOnInit(void (*funcOnInit)(void)) {
+                pFuncOnInit = funcOnInit;
+        };
+
+        /** @name PS Buzzer Controller functions */
+        /**
+         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(ButtonEnum b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
+         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
+         * @param  b          ::ButtonEnum to read.
+         * @param  controller The controller to read from. Default to 0.
+         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
+         */
+        bool getButtonPress(ButtonEnum b, uint8_t controller = 0);
+        bool getButtonClick(ButtonEnum b, uint8_t controller = 0);
+        /**@}*/
+        /** @name PS Buzzer Controller functions */
+        /**
+         * Set LED value without using ::LEDEnum.
+         * @param value See: ::LEDEnum.
+         */
+        /**
+         * Set LED values directly.
+         * @param value      Used to set whenever the LED should be on or off
+         * @param controller The controller to control. Defaults to 0.
+         */
+        void setLedRaw(bool value, uint8_t controller = 0);
+
+        /** Turn all LEDs off. */
+        void setLedOffAll() {
+                for (uint8_t i = 1; i < 4; i++) // Skip first as it will be set in setLedRaw
+                        ledState[i] = false; // Just an easy way to set all four off at the same time
+                setLedRaw(false); // Turn the LED off, on all four controllers
+        };
+
+        /**
+         * Turn the LED off on a specific controller.
+         * @param controller The controller to turn off. Defaults to 0.
+         */
+        void setLedOff(uint8_t controller = 0) {
+                setLedRaw(false, controller);
+        };
+
+
+        /** Turn all LEDs on. */
+        void setLedOnAll() {
+                for (uint8_t i = 1; i < 4; i++) // Skip first as it will be set in setLedRaw
+                        ledState[i] = true; // Just an easy way to set all four off at the same time
+                setLedRaw(true); // Turn the LED on, on all four controllers
+        };
+
+        /**
+         * Turn the LED on on a specific controller.
+         * @param controller The controller to turn off. Defaults to 0.
+         */
+        void setLedOn(uint8_t controller = 0) {
+                setLedRaw(true, controller);
+        };
+
+        /**
+         * Toggle the LED on a specific controller.
+         * @param controller The controller to turn off. Defaults to 0.
+         */
+        void setLedToggle(uint8_t controller = 0) {
+                setLedRaw(!ledState[controller], controller);
+        };
+        /**@}*/
+
+protected:
+        /** @name HIDUniversal implementation */
+        /**
+         * Used to parse USB HID data.
+         * @param hid       Pointer to the HID class.
+         * @param is_rpt_id Only used for Hubs.
+         * @param len       The length of the incoming data.
+         * @param buf       Pointer to the data buffer.
+         */
+        void ParseHIDData(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
+
+        /**
+         * Called when a device is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        uint8_t OnInitSuccessful();
+        /**@}*/
+
+        /** Used to reset the different buffers to their default values */
+        void Reset() {
+                psbuzzButtons.val = 0;
+                oldButtonState.val = 0;
+                buttonClickState.val = 0;
+                for (uint8_t i = 0; i < sizeof(ledState); i++)
+                        ledState[i] = 0;
+        };
+
+        /** @name USBDeviceConfig implementation */
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return (vid == PSBUZZ_VID && pid == PSBUZZ_PID);
+        };
+        /**@}*/
+
+private:
+        void (*pFuncOnInit)(void); // Pointer to function called in onInit()
+
+        void PSBuzz_Command(uint8_t *data, uint16_t nbytes);
+
+        PSBUZZButtons psbuzzButtons, oldButtonState, buttonClickState;
+        bool ledState[4];
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/SPP.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,830 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "SPP.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report sent to the Arduino
+
+/*
+ * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
+ */
+const uint8_t rfcomm_crc_table[256] PROGMEM = {/* reversed, 8-bit, poly=0x07 */
+        0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
+        0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
+        0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
+        0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
+        0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
+        0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
+        0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
+        0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
+        0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
+        0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
+        0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
+        0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
+        0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
+        0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
+        0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
+        0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
+};
+
+SPP::SPP(BTD *p, const char* name, const char* pin) :
+BluetoothService(p) // Pointer to BTD class instance - mandatory
+{
+        pBtd->btdName = name;
+        pBtd->btdPin = pin;
+
+        /* Set device cid for the SDP and RFCOMM channelse */
+        sdp_dcid[0] = 0x50; // 0x0050
+        sdp_dcid[1] = 0x00;
+        rfcomm_dcid[0] = 0x51; // 0x0051
+        rfcomm_dcid[1] = 0x00;
+
+        Reset();
+}
+
+void SPP::Reset() {
+        connected = false;
+        RFCOMMConnected = false;
+        SDPConnected = false;
+        waitForLastCommand = false;
+        l2cap_sdp_state = L2CAP_SDP_WAIT;
+        l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
+        l2cap_event_flag = 0;
+        sppIndex = 0;
+        creditSent = false;
+}
+
+void SPP::disconnect() {
+        connected = false;
+        // First the two L2CAP channels has to be disconnected and then the HCI connection
+        if(RFCOMMConnected)
+                pBtd->l2cap_disconnection_request(hci_handle, ++identifier, rfcomm_scid, rfcomm_dcid);
+        if(RFCOMMConnected && SDPConnected)
+                delay(1); // Add delay between commands
+        if(SDPConnected)
+                pBtd->l2cap_disconnection_request(hci_handle, ++identifier, sdp_scid, sdp_dcid);
+        l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE;
+}
+
+void SPP::ACLData(uint8_t* l2capinbuf) {
+        if(!connected) {
+                if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
+                        if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM && !pBtd->sdpConnectionClaimed) {
+                                pBtd->sdpConnectionClaimed = true;
+                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
+                                l2cap_sdp_state = L2CAP_SDP_WAIT; // Reset state
+                        } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM && !pBtd->rfcommConnectionClaimed) {
+                                pBtd->rfcommConnectionClaimed = true;
+                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
+                                l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; // Reset state
+                        }
+                }
+        }
+
+        if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok
+                if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U
+                        if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                Notify(PSTR(" Data: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+#endif
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
+#ifdef EXTRADEBUG
+                                Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                Notify(PSTR(" SCID: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+                                Notify(PSTR(" Identifier: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
+#endif
+                                if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) { // It doesn't matter if it receives another reqeust, since it waits for the channel to disconnect in the L2CAP_SDP_DONE state, and the l2cap_event_flag will be cleared if so
+                                        identifier = l2capinbuf[9];
+                                        sdp_scid[0] = l2capinbuf[14];
+                                        sdp_scid[1] = l2capinbuf[15];
+                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST);
+                                } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM) { // ----- || -----
+                                        identifier = l2capinbuf[9];
+                                        rfcomm_scid[0] = l2capinbuf[14];
+                                        rfcomm_scid[1] = l2capinbuf[15];
+                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
+                                if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
+                                        if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
+                                                //Notify(PSTR("\r\nSDP Configuration Complete"), 0x80);
+                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS);
+                                        } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {
+                                                //Notify(PSTR("\r\nRFCOMM Configuration Complete"), 0x80);
+                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS);
+                                        }
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
+                                if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
+                                        //Notify(PSTR("\r\nSDP Configuration Request"), 0x80);
+                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], sdp_scid);
+                                } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {
+                                        //Notify(PSTR("\r\nRFCOMM Configuration Request"), 0x80);
+                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], rfcomm_scid);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
+                                if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Request: SDP Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST);
+                                } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Request: RFCOMM Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
+                                if(l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Response: SDP Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE);
+                                } else if(l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Response: RFCOMM Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_INFORMATION_REQUEST) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nInformation request"), 0x80);
+#endif
+                                identifier = l2capinbuf[9];
+                                pBtd->l2cap_information_response(hci_handle, identifier, l2capinbuf[12], l2capinbuf[13]);
+                        }
+#ifdef EXTRADEBUG
+                        else {
+                                Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
+                        }
+#endif
+                } else if(l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP
+                        if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) {
+                                if(((l2capinbuf[16] << 8 | l2capinbuf[17]) == SERIALPORT_UUID) || ((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000 && (l2capinbuf[18] << 8 | l2capinbuf[19]) == SERIALPORT_UUID)) { // Check if it's sending the full UUID, see: https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm, we will just check the first four bytes
+                                        if(firstMessage) {
+                                                serialPortResponse1(l2capinbuf[9], l2capinbuf[10]);
+                                                firstMessage = false;
+                                        } else {
+                                                serialPortResponse2(l2capinbuf[9], l2capinbuf[10]); // Serialport continuation state
+                                                firstMessage = true;
+                                        }
+                                } else if(((l2capinbuf[16] << 8 | l2capinbuf[17]) == L2CAP_UUID) || ((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000 && (l2capinbuf[18] << 8 | l2capinbuf[19]) == L2CAP_UUID)) {
+                                        if(firstMessage) {
+                                                l2capResponse1(l2capinbuf[9], l2capinbuf[10]);
+                                                firstMessage = false;
+                                        } else {
+                                                l2capResponse2(l2capinbuf[9], l2capinbuf[10]); // L2CAP continuation state
+                                                firstMessage = true;
+                                        }
+                                } else
+                                        serviceNotSupported(l2capinbuf[9], l2capinbuf[10]); // The service is not supported
+#ifdef EXTRADEBUG
+                                Notify(PSTR("\r\nUUID: "), 0x80);
+                                uint16_t uuid;
+                                if((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000) // Check if it's sending the UUID as a 128-bit UUID
+                                        uuid = (l2capinbuf[18] << 8 | l2capinbuf[19]);
+                                else // Short UUID
+                                        uuid = (l2capinbuf[16] << 8 | l2capinbuf[17]);
+                                D_PrintHex<uint16_t > (uuid, 0x80);
+
+                                Notify(PSTR("\r\nLength: "), 0x80);
+                                uint16_t length = l2capinbuf[11] << 8 | l2capinbuf[12];
+                                D_PrintHex<uint16_t > (length, 0x80);
+                                Notify(PSTR("\r\nData: "), 0x80);
+                                for(uint8_t i = 0; i < length; i++) {
+                                        D_PrintHex<uint8_t > (l2capinbuf[13 + i], 0x80);
+                                        Notify(PSTR(" "), 0x80);
+                                }
+#endif
+                        }
+#ifdef EXTRADEBUG
+                        else {
+                                Notify(PSTR("\r\nUnknown PDU: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
+                        }
+#endif
+                } else if(l2capinbuf[6] == rfcomm_dcid[0] && l2capinbuf[7] == rfcomm_dcid[1]) { // RFCOMM
+                        rfcommChannel = l2capinbuf[8] & 0xF8;
+                        rfcommDirection = l2capinbuf[8] & 0x04;
+                        rfcommCommandResponse = l2capinbuf[8] & 0x02;
+                        rfcommChannelType = l2capinbuf[9] & 0xEF;
+                        rfcommPfBit = l2capinbuf[9] & 0x10;
+
+                        if(rfcommChannel >> 3 != 0x00)
+                                rfcommChannelConnection = rfcommChannel;
+
+#ifdef EXTRADEBUG
+                        Notify(PSTR("\r\nRFCOMM Channel: "), 0x80);
+                        D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);
+                        Notify(PSTR(" Direction: "), 0x80);
+                        D_PrintHex<uint8_t > (rfcommDirection >> 2, 0x80);
+                        Notify(PSTR(" CommandResponse: "), 0x80);
+                        D_PrintHex<uint8_t > (rfcommCommandResponse >> 1, 0x80);
+                        Notify(PSTR(" ChannelType: "), 0x80);
+                        D_PrintHex<uint8_t > (rfcommChannelType, 0x80);
+                        Notify(PSTR(" PF_BIT: "), 0x80);
+                        D_PrintHex<uint8_t > (rfcommPfBit, 0x80);
+#endif
+                        if(rfcommChannelType == RFCOMM_DISC) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nReceived Disconnect RFCOMM Command on channel: "), 0x80);
+                                D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);
+#endif
+                                connected = false;
+                                sendRfcomm(rfcommChannel, rfcommDirection, rfcommCommandResponse, RFCOMM_UA, rfcommPfBit, rfcommbuf, 0x00); // UA Command
+                        }
+                        if(connected) {
+                                /* Read the incoming message */
+                                if(rfcommChannelType == RFCOMM_UIH && rfcommChannel == rfcommChannelConnection) {
+                                        uint8_t length = l2capinbuf[10] >> 1; // Get length
+                                        uint8_t offset = l2capinbuf[4] - length - 4; // Check if there is credit
+                                        if(checkFcs(&l2capinbuf[8], l2capinbuf[11 + length + offset])) {
+                                                uint8_t i = 0;
+                                                for(; i < length; i++) {
+                                                        if(rfcommAvailable + i >= sizeof (rfcommDataBuffer)) {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nWarning: Buffer is full!"), 0x80);
+#endif
+                                                                break;
+                                                        }
+                                                        rfcommDataBuffer[rfcommAvailable + i] = l2capinbuf[11 + i + offset];
+                                                }
+                                                rfcommAvailable += i;
+#ifdef EXTRADEBUG
+                                                Notify(PSTR("\r\nRFCOMM Data Available: "), 0x80);
+                                                Notify(rfcommAvailable, 0x80);
+                                                if(offset) {
+                                                        Notify(PSTR(" - Credit: 0x"), 0x80);
+                                                        D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);
+                                                }
+#endif
+                                        }
+#ifdef DEBUG_USB_HOST
+                                        else
+                                                Notify(PSTR("\r\nError in FCS checksum!"), 0x80);
+#endif
+#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send to the Arduino via Bluetooth
+                                        for(uint8_t i = 0; i < length; i++)
+                                                Notifyc(l2capinbuf[i + 11 + offset], 0x80);
+#endif
+                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command"), 0x80);
+#endif
+                                        rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command
+                                        rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1
+                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1
+                                        rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
+                                        rfcommbuf[4] = l2capinbuf[15]; // Priority
+                                        rfcommbuf[5] = l2capinbuf[16]; // Timer
+                                        rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB
+                                        rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB
+                                        rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm.
+                                        rfcommbuf[9] = l2capinbuf[20]; // Number of Frames
+                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A); // UIH Remote Port Negotiation Response
+                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nSend UIH Modem Status Response"), 0x80);
+#endif
+                                        rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response
+                                        rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
+                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
+                                        rfcommbuf[3] = l2capinbuf[14];
+                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04);
+                                }
+                        } else {
+                                if(rfcommChannelType == RFCOMM_SABM) { // SABM Command - this is sent twice: once for channel 0 and then for the channel to establish
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nReceived SABM Command"), 0x80);
+#endif
+                                        sendRfcomm(rfcommChannel, rfcommDirection, rfcommCommandResponse, RFCOMM_UA, rfcommPfBit, rfcommbuf, 0x00); // UA Command
+                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_PN_CMD) { // UIH Parameter Negotiation Command
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nReceived UIH Parameter Negotiation Command"), 0x80);
+#endif
+                                        rfcommbuf[0] = BT_RFCOMM_PN_RSP; // UIH Parameter Negotiation Response
+                                        rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1
+                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1
+                                        rfcommbuf[3] = 0xE0; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
+                                        rfcommbuf[4] = 0x00; // Priority
+                                        rfcommbuf[5] = 0x00; // Timer
+                                        rfcommbuf[6] = BULK_MAXPKTSIZE - 14; // Max Fram Size LSB - set to the size of received data (50)
+                                        rfcommbuf[7] = 0x00; // Max Fram Size MSB
+                                        rfcommbuf[8] = 0x00; // MaxRatransm.
+                                        rfcommbuf[9] = 0x00; // Number of Frames
+                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A);
+                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nSend UIH Modem Status Response"), 0x80);
+#endif
+                                        rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response
+                                        rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
+                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
+                                        rfcommbuf[3] = l2capinbuf[14];
+                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04);
+
+                                        delay(1);
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nSend UIH Modem Status Command"), 0x80);
+#endif
+                                        rfcommbuf[0] = BT_RFCOMM_MSC_CMD; // UIH Modem Status Command
+                                        rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1
+                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)
+                                        rfcommbuf[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES)
+
+                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04);
+                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_RSP) { // UIH Modem Status Response
+                                        if(!creditSent) {
+#ifdef DEBUG_USB_HOST
+                                                Notify(PSTR("\r\nSend UIH Command with credit"), 0x80);
+#endif
+                                                sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send credit
+                                                creditSent = true;
+                                                timer = (uint32_t)millis();
+                                                waitForLastCommand = true;
+                                        }
+                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[10] == 0x01) { // UIH Command with credit
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nReceived UIH Command with credit"), 0x80);
+#endif
+                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nReceived UIH Remote Port Negotiation Command"), 0x80);
+#endif
+                                        rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command
+                                        rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1
+                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1
+                                        rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM
+                                        rfcommbuf[4] = l2capinbuf[15]; // Priority
+                                        rfcommbuf[5] = l2capinbuf[16]; // Timer
+                                        rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB
+                                        rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB
+                                        rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm.
+                                        rfcommbuf[9] = l2capinbuf[20]; // Number of Frames
+                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A); // UIH Remote Port Negotiation Response
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nRFCOMM Connection is now established\r\n"), 0x80);
+#endif
+                                        onInit();
+                                }
+#ifdef EXTRADEBUG
+                                else if(rfcommChannelType != RFCOMM_DISC) {
+                                        Notify(PSTR("\r\nUnsupported RFCOMM Data - ChannelType: "), 0x80);
+                                        D_PrintHex<uint8_t > (rfcommChannelType, 0x80);
+                                        Notify(PSTR(" Command: "), 0x80);
+                                        D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);
+                                }
+#endif
+                        }
+                }
+#ifdef EXTRADEBUG
+                else {
+                        Notify(PSTR("\r\nUnsupported L2CAP Data - Channel ID: "), 0x80);
+                        D_PrintHex<uint8_t > (l2capinbuf[7], 0x80);
+                        Notify(PSTR(" "), 0x80);
+                        D_PrintHex<uint8_t > (l2capinbuf[6], 0x80);
+                }
+#endif
+                SDP_task();
+                RFCOMM_task();
+        }
+}
+
+void SPP::Run() {
+        if(waitForLastCommand && (int32_t)((uint32_t)millis() - timer) > 100) { // We will only wait 100ms and see if the UIH Remote Port Negotiation Command is send, as some deviced don't send it
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nRFCOMM Connection is now established - Automatic\r\n"), 0x80);
+#endif
+                onInit();
+        }
+        send(); // Send all bytes currently in the buffer
+}
+
+void SPP::onInit() {
+        creditSent = false;
+        waitForLastCommand = false;
+        connected = true; // The RFCOMM channel is now established
+        sppIndex = 0;
+        if(pFuncOnInit)
+                pFuncOnInit(); // Call the user function
+};
+
+void SPP::SDP_task() {
+        switch(l2cap_sdp_state) {
+                case L2CAP_SDP_WAIT:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST)) {
+                                l2cap_clear_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST); // Clear flag
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSDP Incoming Connection Request"), 0x80);
+#endif
+                                pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, PENDING);
+                                delay(1);
+                                pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, SUCCESSFUL);
+                                identifier++;
+                                delay(1);
+                                pBtd->l2cap_config_request(hci_handle, identifier, sdp_scid);
+                                l2cap_sdp_state = L2CAP_SDP_SUCCESS;
+                        } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST)) {
+                                l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST); // Clear flag
+                                SDPConnected = false;
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected SDP Channel"), 0x80);
+#endif
+                                pBtd->l2cap_disconnection_response(hci_handle, identifier, sdp_dcid, sdp_scid);
+                        }
+                        break;
+                case L2CAP_SDP_SUCCESS:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS)) {
+                                l2cap_clear_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); // Clear flag
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSDP Successfully Configured"), 0x80);
+#endif
+                                firstMessage = true; // Reset bool
+                                SDPConnected = true;
+                                l2cap_sdp_state = L2CAP_SDP_WAIT;
+                        }
+                        break;
+
+                case L2CAP_DISCONNECT_RESPONSE: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected
+                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RESPONSE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected L2CAP Connection"), 0x80);
+#endif
+                                pBtd->hci_disconnect(hci_handle);
+                                hci_handle = -1; // Reset handle
+                                Reset();
+                        }
+                        break;
+        }
+}
+
+void SPP::RFCOMM_task() {
+        switch(l2cap_rfcomm_state) {
+                case L2CAP_RFCOMM_WAIT:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST)) {
+                                l2cap_clear_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST); // Clear flag
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nRFCOMM Incoming Connection Request"), 0x80);
+#endif
+                                pBtd->l2cap_connection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid, PENDING);
+                                delay(1);
+                                pBtd->l2cap_connection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid, SUCCESSFUL);
+                                identifier++;
+                                delay(1);
+                                pBtd->l2cap_config_request(hci_handle, identifier, rfcomm_scid);
+                                l2cap_rfcomm_state = L2CAP_RFCOMM_SUCCESS;
+                        } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST)) {
+                                l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST); // Clear flag
+                                RFCOMMConnected = false;
+                                connected = false;
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected RFCOMM Channel"), 0x80);
+#endif
+                                pBtd->l2cap_disconnection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid);
+                        }
+                        break;
+                case L2CAP_RFCOMM_SUCCESS:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS)) {
+                                l2cap_clear_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS); // Clear flag
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nRFCOMM Successfully Configured"), 0x80);
+#endif
+                                rfcommAvailable = 0; // Reset number of bytes available
+                                bytesRead = 0; // Reset number of bytes received
+                                RFCOMMConnected = true;
+                                l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;
+                        }
+                        break;
+        }
+}
+/************************************************************/
+/*                    SDP Commands                          */
+
+/************************************************************/
+void SPP::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bluetooth specs
+        pBtd->L2CAP_Command(hci_handle, data, nbytes, sdp_scid[0], sdp_scid[1]);
+}
+
+void SPP::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs
+        l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;
+        l2capoutbuf[1] = transactionIDHigh;
+        l2capoutbuf[2] = transactionIDLow;
+        l2capoutbuf[3] = 0x00; // MSB Parameter Length
+        l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5
+        l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount
+        l2capoutbuf[6] = 0x02; // LSB AttributeListsByteCount = 2
+
+        /* Attribute ID/Value Sequence: */
+        l2capoutbuf[7] = 0x35; // Data element sequence - length in next byte
+        l2capoutbuf[8] = 0x00; // Length = 0
+        l2capoutbuf[9] = 0x00; // No continuation state
+
+        SDP_Command(l2capoutbuf, 10);
+}
+
+void SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
+        l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;
+        l2capoutbuf[1] = transactionIDHigh;
+        l2capoutbuf[2] = transactionIDLow;
+        l2capoutbuf[3] = 0x00; // MSB Parameter Length
+        l2capoutbuf[4] = 0x2B; // LSB Parameter Length = 43
+        l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount
+        l2capoutbuf[6] = 0x26; // LSB AttributeListsByteCount = 38
+
+        /* Attribute ID/Value Sequence: */
+        l2capoutbuf[7] = 0x36; // Data element sequence - length in next two bytes
+        l2capoutbuf[8] = 0x00; // MSB Length
+        l2capoutbuf[9] = 0x3C; // LSB Length = 60
+
+        l2capoutbuf[10] = 0x36; // Data element sequence - length in next two bytes
+        l2capoutbuf[11] = 0x00; // MSB Length
+        l2capoutbuf[12] = 0x39; // LSB Length = 57
+
+        l2capoutbuf[13] = 0x09; // Unsigned Integer - length 2 bytes
+        l2capoutbuf[14] = 0x00; // MSB ServiceRecordHandle
+        l2capoutbuf[15] = 0x00; // LSB ServiceRecordHandle
+        l2capoutbuf[16] = 0x0A; // Unsigned int - length 4 bytes
+        l2capoutbuf[17] = 0x00; // ServiceRecordHandle value - TODO: Is this related to HCI_Handle?
+        l2capoutbuf[18] = 0x01;
+        l2capoutbuf[19] = 0x00;
+        l2capoutbuf[20] = 0x06;
+
+        l2capoutbuf[21] = 0x09; // Unsigned Integer - length 2 bytes
+        l2capoutbuf[22] = 0x00; // MSB ServiceClassIDList
+        l2capoutbuf[23] = 0x01; // LSB ServiceClassIDList
+        l2capoutbuf[24] = 0x35; // Data element sequence - length in next byte
+        l2capoutbuf[25] = 0x03; // Length = 3
+        l2capoutbuf[26] = 0x19; // UUID (universally unique identifier) - length = 2 bytes
+        l2capoutbuf[27] = 0x11; // MSB SerialPort
+        l2capoutbuf[28] = 0x01; // LSB SerialPort
+
+        l2capoutbuf[29] = 0x09; // Unsigned Integer - length 2 bytes
+        l2capoutbuf[30] = 0x00; // MSB ProtocolDescriptorList
+        l2capoutbuf[31] = 0x04; // LSB ProtocolDescriptorList
+        l2capoutbuf[32] = 0x35; // Data element sequence - length in next byte
+        l2capoutbuf[33] = 0x0C; // Length = 12
+
+        l2capoutbuf[34] = 0x35; // Data element sequence - length in next byte
+        l2capoutbuf[35] = 0x03; // Length = 3
+        l2capoutbuf[36] = 0x19; // UUID (universally unique identifier) - length = 2 bytes
+        l2capoutbuf[37] = 0x01; // MSB L2CAP
+        l2capoutbuf[38] = 0x00; // LSB L2CAP
+
+        l2capoutbuf[39] = 0x35; // Data element sequence - length in next byte
+        l2capoutbuf[40] = 0x05; // Length = 5
+        l2capoutbuf[41] = 0x19; // UUID (universally unique identifier) - length = 2 bytes
+        l2capoutbuf[42] = 0x00; // MSB RFCOMM
+        l2capoutbuf[43] = 0x03; // LSB RFCOMM
+        l2capoutbuf[44] = 0x08; // Unsigned Integer - length 1 byte
+
+        l2capoutbuf[45] = 0x02; // ContinuationState - Two more bytes
+        l2capoutbuf[46] = 0x00; // MSB length
+        l2capoutbuf[47] = 0x19; // LSB length = 25 more bytes to come
+
+        SDP_Command(l2capoutbuf, 48);
+}
+
+void SPP::serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
+        l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;
+        l2capoutbuf[1] = transactionIDHigh;
+        l2capoutbuf[2] = transactionIDLow;
+        l2capoutbuf[3] = 0x00; // MSB Parameter Length
+        l2capoutbuf[4] = 0x1C; // LSB Parameter Length = 28
+        l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount
+        l2capoutbuf[6] = 0x19; // LSB AttributeListsByteCount = 25
+
+        /* Attribute ID/Value Sequence: */
+        l2capoutbuf[7] = 0x01; // Channel 1 - TODO: Try different values, so multiple servers can be used at once
+
+        l2capoutbuf[8] = 0x09; // Unsigned Integer - length 2 bytes
+        l2capoutbuf[9] = 0x00; // MSB LanguageBaseAttributeIDList
+        l2capoutbuf[10] = 0x06; // LSB LanguageBaseAttributeIDList
+        l2capoutbuf[11] = 0x35; // Data element sequence - length in next byte
+        l2capoutbuf[12] = 0x09; // Length = 9
+
+        // Identifier representing the natural language = en = English - see: "ISO 639:1988"
+        l2capoutbuf[13] = 0x09; // Unsigned Integer - length 2 bytes
+        l2capoutbuf[14] = 0x65; // 'e'
+        l2capoutbuf[15] = 0x6E; // 'n'
+
+        // "The second element of each triplet contains an identifier that specifies a character encoding used for the language"
+        // Encoding is set to 106 (UTF-8) - see: http://www.iana.org/assignments/character-sets/character-sets.xhtml
+        l2capoutbuf[16] = 0x09; // Unsigned Integer - length 2 bytes
+        l2capoutbuf[17] = 0x00; // MSB of character encoding
+        l2capoutbuf[18] = 0x6A; // LSB of character encoding (106)
+
+        // Attribute ID that serves as the base attribute ID for the natural language in the service record
+        // "To facilitate the retrieval of human-readable universal attributes in a principal language, the base attribute ID value for the primary language supported by a service record shall be 0x0100"
+        l2capoutbuf[19] = 0x09; // Unsigned Integer - length 2 bytes
+        l2capoutbuf[20] = 0x01;
+        l2capoutbuf[21] = 0x00;
+
+        l2capoutbuf[22] = 0x09; // Unsigned Integer - length 2 bytes
+        l2capoutbuf[23] = 0x01; // MSB ServiceDescription
+        l2capoutbuf[24] = 0x00; // LSB ServiceDescription
+
+        l2capoutbuf[25] = 0x25; // Text string - length in next byte
+        l2capoutbuf[26] = 0x05; // Name length
+        l2capoutbuf[27] = 'T';
+        l2capoutbuf[28] = 'K';
+        l2capoutbuf[29] = 'J';
+        l2capoutbuf[30] = 'S';
+        l2capoutbuf[31] = 'P';
+        l2capoutbuf[32] = 0x00; // No continuation state
+
+        SDP_Command(l2capoutbuf, 33);
+}
+
+void SPP::l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
+        serialPortResponse1(transactionIDHigh, transactionIDLow); // These has to send all the supported functions, since it only supports virtual serialport it just sends the message again
+}
+
+void SPP::l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) {
+        serialPortResponse2(transactionIDHigh, transactionIDLow); // Same data as serialPortResponse2
+}
+/************************************************************/
+/*                    RFCOMM Commands                       */
+
+/************************************************************/
+void SPP::RFCOMM_Command(uint8_t* data, uint8_t nbytes) {
+        pBtd->L2CAP_Command(hci_handle, data, nbytes, rfcomm_scid[0], rfcomm_scid[1]);
+}
+
+void SPP::sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length) {
+        l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address
+        l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control
+        l2capoutbuf[2] = length << 1 | 0x01; // Length and format (always 0x01 bytes format)
+        uint8_t i = 0;
+        for(; i < length; i++)
+                l2capoutbuf[i + 3] = data[i];
+        l2capoutbuf[i + 3] = calcFcs(l2capoutbuf);
+#ifdef EXTRADEBUG
+        Notify(PSTR(" - RFCOMM Data: "), 0x80);
+        for(i = 0; i < length + 4; i++) {
+                D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);
+                Notify(PSTR(" "), 0x80);
+        }
+#endif
+        RFCOMM_Command(l2capoutbuf, length + 4);
+}
+
+void SPP::sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit) {
+        l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address
+        l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control
+        l2capoutbuf[2] = 0x01; // Length = 0
+        l2capoutbuf[3] = credit; // Credit
+        l2capoutbuf[4] = calcFcs(l2capoutbuf);
+#ifdef EXTRADEBUG
+        Notify(PSTR(" - RFCOMM Credit Data: "), 0x80);
+        for(uint8_t i = 0; i < 5; i++) {
+                D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);
+                Notify(PSTR(" "), 0x80);
+        }
+#endif
+        RFCOMM_Command(l2capoutbuf, 5);
+}
+
+/* CRC on 2 bytes */
+uint8_t SPP::crc(uint8_t *data) {
+        return (pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xFF ^ data[0]]) ^ data[1]]));
+}
+
+/* Calculate FCS */
+uint8_t SPP::calcFcs(uint8_t *data) {
+        uint8_t temp = crc(data);
+        if((data[1] & 0xEF) == RFCOMM_UIH)
+                return (0xFF - temp); // FCS on 2 bytes
+        else
+                return (0xFF - pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]])); // FCS on 3 bytes
+}
+
+/* Check FCS */
+bool SPP::checkFcs(uint8_t *data, uint8_t fcs) {
+        uint8_t temp = crc(data);
+        if((data[1] & 0xEF) != RFCOMM_UIH)
+                temp = pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]]); // FCS on 3 bytes
+        return (pgm_read_byte(&rfcomm_crc_table[temp ^ fcs]) == 0xCF);
+}
+
+/* Serial commands */
+#if defined(ARDUINO) && ARDUINO >=100
+
+size_t SPP::write(uint8_t data) {
+        return write(&data, 1);
+}
+#else
+
+void SPP::write(uint8_t data) {
+        write(&data, 1);
+}
+#endif
+
+#if defined(ARDUINO) && ARDUINO >=100
+
+size_t SPP::write(const uint8_t *data, size_t size) {
+#else
+
+void SPP::write(const uint8_t *data, size_t size) {
+#endif
+        for(uint8_t i = 0; i < size; i++) {
+                if(sppIndex >= sizeof (sppOutputBuffer) / sizeof (sppOutputBuffer[0]))
+                        send(); // Send the current data in the buffer
+                sppOutputBuffer[sppIndex++] = data[i]; // All the bytes are put into a buffer and then send using the send() function
+        }
+#if defined(ARDUINO) && ARDUINO >=100
+        return size;
+#endif
+}
+
+void SPP::send() {
+        if(!connected || !sppIndex)
+                return;
+        uint8_t length; // This is the length of the string we are sending
+        uint8_t offset = 0; // This is used to keep track of where we are in the string
+
+        l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress; // RFCOMM Address
+        l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control
+
+        while(sppIndex) { // We will run this while loop until this variable is 0
+                if(sppIndex > (sizeof (l2capoutbuf) - 4)) // Check if the string is larger than the outgoing buffer
+                        length = sizeof (l2capoutbuf) - 4;
+                else
+                        length = sppIndex;
+
+                l2capoutbuf[2] = length << 1 | 1; // Length
+                uint8_t i = 0;
+                for(; i < length; i++)
+                        l2capoutbuf[i + 3] = sppOutputBuffer[i + offset];
+                l2capoutbuf[i + 3] = calcFcs(l2capoutbuf); // Calculate checksum
+
+                RFCOMM_Command(l2capoutbuf, length + 4);
+
+                sppIndex -= length;
+                offset += length; // Increment the offset
+        }
+}
+
+int SPP::available(void) {
+        return rfcommAvailable;
+};
+
+void SPP::discard(void) {
+        rfcommAvailable = 0;
+}
+
+int SPP::peek(void) {
+        if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer
+                return -1;
+        return rfcommDataBuffer[0];
+}
+
+int SPP::read(void) {
+        if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer
+                return -1;
+        uint8_t output = rfcommDataBuffer[0];
+        for(uint8_t i = 1; i < rfcommAvailable; i++)
+                rfcommDataBuffer[i - 1] = rfcommDataBuffer[i]; // Shift the buffer one left
+        rfcommAvailable--;
+        bytesRead++;
+        if(bytesRead > (sizeof (rfcommDataBuffer) - 5)) { // We will send the command just before it runs out of credit
+                bytesRead = 0;
+                sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send more credit
+#ifdef EXTRADEBUG
+                Notify(PSTR("\r\nSent "), 0x80);
+                Notify((uint8_t)sizeof (rfcommDataBuffer), 0x80);
+                Notify(PSTR(" more credit"), 0x80);
+#endif
+        }
+        return output;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/SPP.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,230 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _spp_h_
+#define _spp_h_
+
+#include "BTD.h"
+
+/* Used for SDP */
+#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU    0x06 // See the RFCOMM specs
+#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU   0x07 // See the RFCOMM specs
+#define SERIALPORT_UUID     0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm
+#define L2CAP_UUID          0x0100
+
+/* Used for RFCOMM */
+#define RFCOMM_SABM     0x2F
+#define RFCOMM_UA       0x63
+#define RFCOMM_UIH      0xEF
+//#define RFCOMM_DM       0x0F
+#define RFCOMM_DISC     0x43
+
+#define extendAddress   0x01 // Always 1
+
+// Multiplexer message types
+#define BT_RFCOMM_PN_CMD     0x83
+#define BT_RFCOMM_PN_RSP     0x81
+#define BT_RFCOMM_MSC_CMD    0xE3
+#define BT_RFCOMM_MSC_RSP    0xE1
+#define BT_RFCOMM_RPN_CMD    0x93
+#define BT_RFCOMM_RPN_RSP    0x91
+/*
+#define BT_RFCOMM_TEST_CMD   0x23
+#define BT_RFCOMM_TEST_RSP   0x21
+#define BT_RFCOMM_FCON_CMD   0xA3
+#define BT_RFCOMM_FCON_RSP   0xA1
+#define BT_RFCOMM_FCOFF_CMD  0x63
+#define BT_RFCOMM_FCOFF_RSP  0x61
+#define BT_RFCOMM_RLS_CMD    0x53
+#define BT_RFCOMM_RLS_RSP    0x51
+#define BT_RFCOMM_NSC_RSP    0x11
+ */
+
+/**
+ * This BluetoothService class implements the Serial Port Protocol (SPP).
+ * It inherits the Arduino Stream class. This allows it to use all the standard Arduino print and stream functions.
+ */
+class SPP : public BluetoothService, public Stream {
+public:
+        /**
+         * Constructor for the SPP class.
+         * @param  p   Pointer to BTD class instance.
+         * @param  name   Set the name to BTD#btdName. If argument is omitted, then "Arduino" will be used.
+         * @param  pin   Write the pin to BTD#btdPin. If argument is omitted, then "0000" will be used.
+         */
+        SPP(BTD *p, const char *name = "Arduino", const char *pin = "0000");
+
+        /** @name BluetoothService implementation */
+        /** Used this to disconnect the virtual serial port. */
+        void disconnect();
+        /**@}*/
+
+        /**
+         * Used to provide Boolean tests for the class.
+         * @return Return true if SPP communication is connected.
+         */
+        operator bool() {
+                return connected;
+        }
+        /** Variable used to indicate if the connection is established. */
+        bool connected;
+
+        /** @name Serial port profile (SPP) Print functions */
+        /**
+         * Get number of bytes waiting to be read.
+         * @return Return the number of bytes ready to be read.
+         */
+        int available(void);
+
+        /** Send out all bytes in the buffer. */
+        void flush(void) {
+                send();
+        };
+        /**
+         * Used to read the next value in the buffer without advancing to the next one.
+         * @return Return the byte. Will return -1 if no bytes are available.
+         */
+        int peek(void);
+        /**
+         * Used to read the buffer.
+         * @return Return the byte. Will return -1 if no bytes are available.
+         */
+        int read(void);
+
+#if defined(ARDUINO) && ARDUINO >=100
+        /**
+         * Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called.
+         * @param  data The byte to write.
+         * @return      Return the number of bytes written.
+         */
+        size_t write(uint8_t data);
+        /**
+         * Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called.
+         * @param  data The data array to send.
+         * @param  size Size of the data.
+         * @return      Return the number of bytes written.
+         */
+        size_t write(const uint8_t* data, size_t size);
+        /** Pull in write(const char *str) from Print */
+        /*
+#if !defined(RBL_NRF51822)
+        using Print::write;
+#endif*/
+
+#else
+        /**
+         * Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called.
+         * @param  data The byte to write.
+         */
+        void write(uint8_t data);
+        /**
+         * Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called.
+         * @param data The data array to send.
+         * @param size Size of the data.
+         */
+        void write(const uint8_t* data, size_t size);
+#endif
+
+        /** Discard all the bytes in the buffer. */
+        void discard(void);
+        /**
+         * This will send all the bytes in the buffer.
+         * This is called whenever Usb.Task() is called,
+         * but can also be called via this function.
+         */
+        void send(void);
+        /**@}*/
+
+protected:
+        /** @name BluetoothService implementation */
+        /**
+         * Used to pass acldata to the services.
+         * @param ACLData Incoming acldata.
+         */
+        void ACLData(uint8_t* ACLData);
+        /** Used to establish the connection automatically. */
+        void Run();
+        /** Use this to reset the service. */
+        void Reset();
+        /**
+         * Called when a device is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        void onInit();
+        /**@}*/
+
+private:
+        /* Set true when a channel is created */
+        bool SDPConnected;
+        bool RFCOMMConnected;
+
+        /* Variables used by L2CAP state machines */
+        uint8_t l2cap_sdp_state;
+        uint8_t l2cap_rfcomm_state;
+
+        uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data
+        uint8_t rfcommbuf[10]; // Buffer for RFCOMM Commands
+
+        /* L2CAP Channels */
+        uint8_t sdp_scid[2]; // L2CAP source CID for SDP
+        uint8_t sdp_dcid[2]; // 0x0050
+        uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM
+        uint8_t rfcomm_dcid[2]; // 0x0051
+
+        /* RFCOMM Variables */
+        uint8_t rfcommChannel;
+        uint8_t rfcommChannelConnection; // This is the channel the SPP channel will be running at
+        uint8_t rfcommDirection;
+        uint8_t rfcommCommandResponse;
+        uint8_t rfcommChannelType;
+        uint8_t rfcommPfBit;
+
+        uint32_t timer;
+        bool waitForLastCommand;
+        bool creditSent;
+
+        uint8_t rfcommDataBuffer[100]; // Create a 100 sized buffer for incoming data
+        uint8_t sppOutputBuffer[100]; // Create a 100 sized buffer for outgoing SPP data
+        uint8_t sppIndex;
+        uint8_t rfcommAvailable;
+
+        bool firstMessage; // Used to see if it's the first SDP request received
+        uint8_t bytesRead; // Counter to see when it's time to send more credit
+
+        /* State machines */
+        void SDP_task(); // SDP state machine
+        void RFCOMM_task(); // RFCOMM state machine
+
+        /* SDP Commands */
+        void SDP_Command(uint8_t *data, uint8_t nbytes);
+        void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow);
+        void serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);
+        void serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);
+        void l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);
+        void l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);
+
+        /* RFCOMM Commands */
+        void RFCOMM_Command(uint8_t *data, uint8_t nbytes);
+        void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t *data, uint8_t length);
+        void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit);
+        uint8_t calcFcs(uint8_t *data);
+        bool checkFcs(uint8_t *data, uint8_t fcs);
+        uint8_t crc(uint8_t *data);
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/UHS2_gpio.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,75 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+
+UHS2_GPIO implements "wiring" style GPIO access. Implemented by Brian Walton brian@riban.co.uk
+ */
+
+#include "UHS2_gpio.h"
+
+/** @brief  Implement an instance of a UHS2_GPIO object
+*   @param  pUSB Pointer to a UHS2 USB object
+*/
+UHS2_GPIO::UHS2_GPIO(USB *pUsb) : m_pUsb(pUsb)
+{
+}
+
+/** @brief  Set a GPIO output value
+*   @param  pin GPIO output pin on USB Host Shield to set
+*   @param  val Value to set the pin to (zero value will clear output, non-zero value will assert output)
+*/
+void UHS2_GPIO::digitalWrite(uint8_t pin, uint8_t val) {
+        if(pin > 7)
+                return;
+        uint8_t nValue = m_pUsb->gpioRdOutput();
+        uint8_t nMask = 1 << pin;
+        nValue &= (~nMask);
+        if(val)
+                nValue |= (nMask);
+        m_pUsb->gpioWr(nValue);
+}
+
+/** @brief  Read the value from a GPIO input pin
+*   @param  pin GPIO input pin on USB Host Shield to read
+*   @retval int Value of GPIO input (-1 on fail)
+*/
+int UHS2_GPIO::digitalRead(uint8_t pin) {
+        if(pin > 7)
+                return -1;
+        uint8_t nMask = 1 << pin;
+        uint8_t nValue = m_pUsb->gpioRd();
+        return ((nValue & nMask)?1:0);
+}
+
+/** @brief  Read the value from a GPIO output pin
+*   @param  pin GPIO output pin on USB Host Shield to read
+*   @retval int Value of GPIO output (-1 on fail)
+*   @note   Value of MAX3421E output register, i.e. what the device has been set to, not the physical value on the pin
+*/
+int UHS2_GPIO::digitalReadOutput(uint8_t pin) {
+        if(pin > 7)
+                return -1;
+        uint8_t nMask = 1 << pin;
+        uint8_t nValue = m_pUsb->gpioRdOutput();
+        return ((nValue & nMask)?1:0);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/UHS2_gpio.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,45 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+
+UHS2_GPIO implements "wiring" style GPIO access. Implemented by Brian Walton brian@riban.co.uk
+ */
+
+#if !defined(__USB2_GPIO_H__)
+#define __USB2_GPIO_H__
+
+#include "Usb.h"
+
+class UHS2_GPIO {
+public:
+        UHS2_GPIO(USB *pUsb);
+
+        void digitalWrite(uint8_t pin, uint8_t val);
+        int digitalRead(uint8_t pin);
+        int digitalReadOutput(uint8_t pin);
+
+private:
+        USB* m_pUsb;
+};
+
+#endif // __USB2_GPIO_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/Usb.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,932 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+/* USB functions */
+
+// warning
+// #define _usb_h_
+// #define MBED_H
+
+#include "Usb.h"
+
+static uint8_t usb_error = 0;
+static uint8_t usb_task_state;
+
+/* constructor */
+USB::USB(PinName mosi, PinName miso, PinName sclk, PinName ssel, PinName intr) : 
+    MAX3421E(mosi, miso, sclk, ssel, intr),
+    bmHubPre(0)
+{
+    arduinoTimer.start();
+    usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; //set up state machine
+    init();
+}
+
+
+/* Initialize data structures */
+void USB::init()
+{
+    //devConfigIndex = 0;
+    bmHubPre = 0;
+}
+
+uint8_t USB::getUsbTaskState(void)
+{
+    return (usb_task_state);
+}
+
+void USB::setUsbTaskState(uint8_t state)
+{
+    usb_task_state = state;
+}
+
+EpInfo *USB::getEpInfoEntry(uint8_t addr, uint8_t ep)
+{
+    UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
+
+    if (!p || !p->epinfo)
+        return NULL;
+
+    EpInfo *pep = p->epinfo;
+
+    for (uint8_t i = 0; i < p->epcount; i++)
+    {
+        if ((pep)->epAddr == ep)
+            return pep;
+
+        pep++;
+    }
+    return NULL;
+}
+
+/* set device table entry */
+
+/* each device is different and has different number of endpoints. This function plugs endpoint record structure, defined in application, to devtable */
+uint8_t USB::setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr)
+{
+    if (!eprecord_ptr)
+        return USB_ERROR_INVALID_ARGUMENT;
+
+    UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
+
+    if (!p)
+        return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+    p->address.devAddress = addr;
+    p->epinfo = eprecord_ptr;
+    p->epcount = epcount;
+
+    return 0;
+}
+
+uint8_t USB::SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit)
+{
+    UsbDevice *p = addrPool.GetUsbDevicePtr(addr);
+
+    if (!p)
+        return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+    if (!p->epinfo)
+        return USB_ERROR_EPINFO_IS_NULL;
+
+    *ppep = getEpInfoEntry(addr, ep);
+
+    if (!*ppep)
+        return USB_ERROR_EP_NOT_FOUND_IN_TBL;
+
+    *nak_limit = (0x0001UL << (((*ppep)->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower));
+    (*nak_limit)--;
+    /*
+          USBTRACE2("\r\nAddress: ", addr);
+          USBTRACE2(" EP: ", ep);
+          USBTRACE2(" NAK Power: ",(*ppep)->bmNakPower);
+          USBTRACE2(" NAK Limit: ", nak_limit);
+          USBTRACE("\r\n");
+         */
+    regWr(rPERADDR, addr); //set peripheral address
+
+    uint8_t mode = regRd(rMODE);
+
+    //Serial.print("\r\nMode: ");
+    //Serial.println( mode, HEX);
+    //Serial.print("\r\nLS: ");
+    //Serial.println(p->lowspeed, HEX);
+
+    // Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise
+    regWr(rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode & ~(bmHUBPRE | bmLOWSPEED));
+
+    return 0;
+}
+
+/* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer,   */
+/* depending on request. Actual requests are defined as inlines                                                                                      */
+/* return codes:                */
+/* 00       =   success         */
+
+/* 01-0f    =   non-zero HRSLT  */
+uint8_t USB::ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
+                     uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t *dataptr, USBReadParser *p)
+{
+    bool direction = false; //request direction, IN or OUT
+    uint8_t rcode;
+    SETUP_PKT setup_pkt;
+
+    EpInfo *pep = NULL;
+    uint16_t nak_limit = 0;
+
+    rcode = SetAddress(addr, ep, &pep, &nak_limit);
+
+    if (rcode)
+        return rcode;
+
+    direction = ((bmReqType & 0x80) > 0);
+
+    /* fill in setup packet */
+    setup_pkt.ReqType_u.bmRequestType = bmReqType;
+    setup_pkt.bRequest = bRequest;
+    setup_pkt.wVal_u.wValueLo = wValLo;
+    setup_pkt.wVal_u.wValueHi = wValHi;
+    setup_pkt.wIndex = wInd;
+    setup_pkt.wLength = total;
+
+    bytesWr(rSUDFIFO, 8, (uint8_t *)&setup_pkt); //transfer to setup packet FIFO
+
+    rcode = dispatchPkt(tokSETUP, ep, nak_limit); //dispatch packet
+
+    if (rcode) //return HRSLT if not zero
+        return (rcode);
+
+    if (dataptr != NULL) //data stage, if present
+    {
+        if (direction) //IN transfer
+        {
+            uint16_t left = total;
+
+            pep->bmRcvToggle = 1; //bmRCVTOG1;
+
+            while (left)
+            {
+                // Bytes read into buffer
+#if defined(ESP8266) || defined(ESP32)
+                yield(); // needed in order to reset the watchdog timer on the ESP8266
+#endif
+                uint16_t read = nbytes;
+                //uint16_t read = (left<nbytes) ? left : nbytes;
+
+                rcode = InTransfer(pep, nak_limit, &read, dataptr);
+                if (rcode == hrTOGERR)
+                {
+                    // yes, we flip it wrong here so that next time it is actually correct!
+                    pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
+                    continue;
+                }
+
+                if (rcode)
+                    return rcode;
+
+                // Invoke callback function if inTransfer completed successfully and callback function pointer is specified
+                if (!rcode && p)
+                    ((USBReadParser *)p)->Parse(read, dataptr, total - left);
+
+                left -= read;
+
+                if (read < nbytes)
+                    break;
+            }
+        }
+        else //OUT transfer
+        {
+            pep->bmSndToggle = 1; //bmSNDTOG1;
+            rcode = OutTransfer(pep, nak_limit, nbytes, dataptr);
+        }
+        if (rcode) //return error
+            return (rcode);
+    }
+    // Status stage
+    return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit); //GET if direction
+}
+
+/* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */
+/* Keep sending INs and writes data to memory area pointed by 'data'                                                           */
+
+/* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error,
+            fe USB xfer timeout */
+uint8_t USB::inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval /*= 0*/)
+{
+    EpInfo *pep = NULL;
+    uint16_t nak_limit = 0;
+
+    uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit);
+
+    if (rcode)
+    {
+        USBTRACE3("(USB::InTransfer) SetAddress Failed ", rcode, 0x81);
+        USBTRACE3("(USB::InTransfer) addr requested ", addr, 0x81);
+        USBTRACE3("(USB::InTransfer) ep requested ", ep, 0x81);
+        return rcode;
+    }
+    return InTransfer(pep, nak_limit, nbytesptr, data, bInterval);
+}
+
+uint8_t USB::InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval /*= 0*/)
+{
+    uint8_t rcode = 0;
+    uint8_t pktsize;
+
+    uint16_t nbytes = *nbytesptr;
+    //DEBUG("Requesting %i bytes ", nbytes);
+    uint8_t maxpktsize = pep->maxPktSize;
+
+    *nbytesptr = 0;
+    regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value
+
+    // use a 'break' to exit this loop
+    while (1)
+    {
+#if defined(ESP8266) || defined(ESP32)
+        yield(); // needed in order to reset the watchdog timer on the ESP8266
+#endif
+        rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit); //IN packet to EP-'endpoint'. Function takes care of NAKS.
+        if (rcode == hrTOGERR)
+        {
+            // yes, we flip it wrong here so that next time it is actually correct!
+            pep->bmRcvToggle = (regRd(rHRSL) & bmRCVTOGRD) ? 0 : 1;
+            regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value
+            continue;
+        }
+        if (rcode)
+        {
+            //DEBUG(">>>>>>>> Problem! dispatchPkt %2.2x\r\n", rcode);
+            break; //should be 0, indicating ACK. Else return error code.
+        }
+        /* check for RCVDAVIRQ and generate error if not present
+                 * the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred.
+                 * Need to add handling for that
+                 *
+                 * NOTE: I've seen this happen with SPI corruption -- xxxajk
+                 */
+        if ((regRd(rHIRQ) & bmRCVDAVIRQ) == 0)
+        {
+            //DEBUG(">>>>>>>> Problem! NO RCVDAVIRQ!\r\n");
+            rcode = 0xf0; //receive error
+            break;
+        }
+        pktsize = regRd(rRCVBC); //number of received bytes
+        //DEBUG("Got %i bytes \r\n", pktsize);
+        // This would be OK, but...
+        //assert(pktsize <= nbytes);
+        if (pktsize > nbytes)
+        {
+            // This can happen. Use of assert on Arduino locks up the Arduino.
+            // So I will trim the value, and hope for the best.
+            //DEBUG(">>>>>>>> Problem! Wanted %i bytes but got %i.\r\n", nbytes, pktsize);
+            pktsize = nbytes;
+        }
+
+        int16_t mem_left = (int16_t)nbytes - *((int16_t *)nbytesptr);
+
+        if (mem_left < 0)
+            mem_left = 0;
+
+        data = bytesRd(rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data);
+
+        regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer
+        *nbytesptr += pktsize;     // add this packet's byte count to total transfer length
+
+        /* The transfer is complete under two conditions:           */
+        /* 1. The device sent a short packet (L.T. maxPacketSize)   */
+        /* 2. 'nbytes' have been transferred.                       */
+        if ((pktsize < maxpktsize) || (*nbytesptr >= nbytes)) // have we transferred 'nbytes' bytes?
+        {
+            // Save toggle value
+            pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0;
+            //DEBUG("\r\n");
+            rcode = 0;
+            break;
+        }
+        else if (bInterval > 0)
+            delay(bInterval); // Delay according to polling interval
+    }                         //while( 1 )
+    return (rcode);
+}
+
+/* OUT transfer to arbitrary endpoint. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */
+/* Handles NAK bug per Maxim Application Note 4000 for single buffer transfer   */
+
+/* rcode 0 if no errors. rcode 01-0f is relayed from HRSL                       */
+uint8_t USB::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *data)
+{
+    EpInfo *pep = NULL;
+    uint16_t nak_limit = 0;
+
+    uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit);
+
+    if (rcode)
+        return rcode;
+
+    return OutTransfer(pep, nak_limit, nbytes, data);
+}
+
+uint8_t USB::OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data)
+{
+    uint8_t rcode = hrSUCCESS, retry_count;
+    uint8_t *data_p = data; //local copy of the data pointer
+    uint16_t bytes_tosend, nak_count;
+    uint16_t bytes_left = nbytes;
+
+    uint8_t maxpktsize = pep->maxPktSize;
+
+    if (maxpktsize < 1 || maxpktsize > 64)
+        return USB_ERROR_INVALID_MAX_PKT_SIZE;
+
+    uint32_t timeout = (uint32_t)millis() + USB_XFER_TIMEOUT;
+
+    regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value
+
+    while (bytes_left)
+    {
+#if defined(ESP8266) || defined(ESP32)
+        yield(); // needed in order to reset the watchdog timer on the ESP8266
+#endif
+        retry_count = 0;
+        nak_count = 0;
+        bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left;
+        bytesWr(rSNDFIFO, bytes_tosend, data_p); //filling output FIFO
+        regWr(rSNDBC, bytes_tosend);             //set number of bytes
+        regWr(rHXFR, (tokOUT | pep->epAddr));    //dispatch packet
+        while (!(regRd(rHIRQ) & bmHXFRDNIRQ))
+        {
+#if defined(ESP8266) || defined(ESP32)
+            yield(); // needed in order to reset the watchdog timer on the ESP8266
+#endif
+        }                          //wait for the completion IRQ
+        regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ
+        rcode = (regRd(rHRSL) & 0x0f);
+
+        while (rcode && ((int32_t)((uint32_t)millis() - timeout) < 0L))
+        {
+#if defined(ESP8266) || defined(ESP32)
+            yield(); // needed in order to reset the watchdog timer on the ESP8266
+#endif
+            switch (rcode)
+            {
+            case hrNAK:
+                nak_count++;
+                if (nak_limit && (nak_count == nak_limit))
+                    goto breakout;
+                //return ( rcode);
+                break;
+            case hrTIMEOUT:
+                retry_count++;
+                if (retry_count == USB_RETRY_LIMIT)
+                    goto breakout;
+                //return ( rcode);
+                break;
+            case hrTOGERR:
+                // yes, we flip it wrong here so that next time it is actually correct!
+                pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;
+                regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value
+                break;
+            default:
+                goto breakout;
+            } //switch( rcode
+
+            /* process NAK according to Host out NAK bug */
+            regWr(rSNDBC, 0);
+            regWr(rSNDFIFO, *data_p);
+            regWr(rSNDBC, bytes_tosend);
+            regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet
+            while (!(regRd(rHIRQ) & bmHXFRDNIRQ))
+            {
+#if defined(ESP8266) || defined(ESP32)
+                yield(); // needed in order to reset the watchdog timer on the ESP8266
+#endif
+            }                          //wait for the completion IRQ
+            regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ
+            rcode = (regRd(rHRSL) & 0x0f);
+        } //while( rcode && ....
+        bytes_left -= bytes_tosend;
+        data_p += bytes_tosend;
+    } //while( bytes_left...
+breakout:
+
+    pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0;  //update toggle
+    return (rcode);                                         //should be 0 in all cases
+}
+/* dispatch USB packet. Assumes peripheral address is set and relevant buffer is loaded/empty       */
+/* If NAK, tries to re-send up to nak_limit times                                                   */
+/* If nak_limit == 0, do not count NAKs, exit after timeout                                         */
+/* If bus timeout, re-sends up to USB_RETRY_LIMIT times                                             */
+
+/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout                       */
+uint8_t USB::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit)
+{
+    uint32_t timeout = (uint32_t)millis() + USB_XFER_TIMEOUT;
+    uint8_t tmpdata;
+    uint8_t rcode = hrSUCCESS;
+    uint8_t retry_count = 0;
+    uint16_t nak_count = 0;
+
+    while ((int32_t)((uint32_t)millis() - timeout) < 0L)
+    {
+#if defined(ESP8266) || defined(ESP32)
+        yield(); // needed in order to reset the watchdog timer on the ESP8266
+#endif
+        regWr(rHXFR, (token | ep)); //launch the transfer
+        rcode = USB_ERROR_TRANSFER_TIMEOUT;
+
+        while ((int32_t)((uint32_t)millis() - timeout) < 0L) //wait for transfer completion
+        {
+#if defined(ESP8266) || defined(ESP32)
+            yield(); // needed in order to reset the watchdog timer on the ESP8266
+#endif
+            tmpdata = regRd(rHIRQ);
+
+            if (tmpdata & bmHXFRDNIRQ)
+            {
+                regWr(rHIRQ, bmHXFRDNIRQ); //clear the interrupt
+                rcode = 0x00;
+                break;
+            } //if( tmpdata & bmHXFRDNIRQ
+
+        } //while ( millis() < timeout
+
+        //if (rcode != 0x00) //exit if timeout
+        //        return ( rcode);
+
+        rcode = (regRd(rHRSL) & 0x0f); //analyze transfer result
+
+        switch (rcode)
+        {
+        case hrNAK:
+            nak_count++;
+            if (nak_limit && (nak_count == nak_limit))
+                return (rcode);
+            break;
+        case hrTIMEOUT:
+            retry_count++;
+            if (retry_count == USB_RETRY_LIMIT)
+                return (rcode);
+            break;
+        default:
+            return (rcode);
+        } //switch( rcode
+
+    } //while( timeout > millis()
+    return (rcode);
+}
+
+/* USB main task. Performs enumeration/cleanup */
+void USB::Task(void) //USB state machine
+{
+    uint8_t rcode;
+    uint8_t tmpdata;
+    static uint32_t delay = 0;
+    //USB_DEVICE_DESCRIPTOR buf;
+    bool lowspeed = false;
+
+    MAX3421E::Task(); // 割り込みがあったらコマンドで対応する
+
+    tmpdata = getVbusState();
+
+    /* modify USB task state if Vbus changed */
+    switch (tmpdata)
+    {
+    case SE1: //illegal state
+        usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL;
+        lowspeed = false;
+        break;
+    case SE0: //disconnected
+        if ((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED)
+            usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE;
+        lowspeed = false;
+        break;
+    case LSHOST:
+
+        lowspeed = true;
+        //intentional fallthrough
+    case FSHOST: //attached
+        if ((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED)
+        {
+            delay = (uint32_t)millis() + USB_SETTLE_DELAY;
+            usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE;
+        }
+        break;
+    } // switch( tmpdata
+
+    for (uint8_t i = 0; i < USB_NUMDEVICES; i++)
+        if (devConfig[i])
+        {
+            DEBUG("dev %d, poll\n", i);
+            rcode = devConfig[i]->Poll();
+        }
+
+    switch (usb_task_state)
+    {
+    case USB_DETACHED_SUBSTATE_INITIALIZE:
+        init();
+
+        for (uint8_t i = 0; i < USB_NUMDEVICES; i++)
+            if (devConfig[i])
+                rcode = devConfig[i]->Release();
+
+        usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE;
+        break;
+    case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here
+        break;
+    case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here
+        break;
+    case USB_ATTACHED_SUBSTATE_SETTLE: //settle time for just attached device
+        if ((int32_t)((uint32_t)millis() - delay) >= 0L)
+            usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE;
+        else
+            break; // don't fall through
+    case USB_ATTACHED_SUBSTATE_RESET_DEVICE:
+        regWr(rHCTL, bmBUSRST); //issue bus reset
+        usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE;
+        break;
+    case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE:
+        if ((regRd(rHCTL) & bmBUSRST) == 0)
+        {
+            tmpdata = regRd(rMODE) | bmSOFKAENAB; //start SOF generation
+            regWr(rMODE, tmpdata);
+            usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF;
+            //delay = (uint32_t)millis() + 20; //20ms wait after reset per USB spec
+        }
+        break;
+    case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order
+        if (regRd(rHIRQ) & bmFRAMEIRQ)
+        {
+            //when first SOF received _and_ 20ms has passed we can continue
+            /*
+                                if (delay < (uint32_t)millis()) //20ms passed
+                                        usb_task_state = USB_STATE_CONFIGURING;
+                                 */
+            usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET;
+            delay = (uint32_t)millis() + 20;
+        }
+        break;
+    case USB_ATTACHED_SUBSTATE_WAIT_RESET:
+        if ((int32_t)((uint32_t)millis() - delay) >= 0L)
+            usb_task_state = USB_STATE_CONFIGURING;
+        else
+            break; // don't fall through
+    case USB_STATE_CONFIGURING:
+
+        //Serial.print("\r\nConf.LS: ");
+        //Serial.println(lowspeed, HEX);
+
+        rcode = Configuring(0, 0, lowspeed);
+
+        if (rcode)
+        {
+            if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE)
+            {
+                usb_error = rcode;
+                usb_task_state = USB_STATE_ERROR;
+            }
+        }
+        else
+            usb_task_state = USB_STATE_RUNNING;
+        break;
+    case USB_STATE_RUNNING:
+        break;
+    case USB_STATE_ERROR:
+        //MAX3421E::Init();
+        break;
+    } // switch( usb_task_state )
+}
+
+uint8_t USB::DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed)
+{
+    //uint8_t                buf[12];
+    uint8_t rcode;
+    UsbDevice *p0 = NULL, *p = NULL;
+
+    // Get pointer to pseudo device with address 0 assigned
+    p0 = addrPool.GetUsbDevicePtr(0);
+
+    if (!p0)
+        return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+    if (!p0->epinfo)
+        return USB_ERROR_EPINFO_IS_NULL;
+
+    p0->lowspeed = (lowspeed) ? true : false;
+
+    // Allocate new address according to device class
+    uint8_t bAddress = addrPool.AllocAddress(parent, false, port);
+
+    if (!bAddress)
+        return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+    p = addrPool.GetUsbDevicePtr(bAddress);
+
+    if (!p)
+        return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+    p->lowspeed = lowspeed;
+
+    // Assign new address to the device
+    rcode = setAddr(0, 0, bAddress);
+
+    if (rcode)
+    {
+        addrPool.FreeAddress(bAddress);
+        bAddress = 0;
+        return rcode;
+    }
+    return 0;
+};
+
+uint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed)
+{
+    //DEBUG("AttemptConfig: parent = %i, port = %i\r\n", parent, port);
+    uint8_t retries = 0;
+
+again:
+    uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed);
+    if (rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET)
+    {
+        if (parent == 0)
+        {
+            // Send a bus reset on the root interface.
+            regWr(rHCTL, bmBUSRST); //issue bus reset
+            delay(102);             // delay 102ms, compensate for clock inaccuracy.
+        }
+        else
+        {
+            // reset parent port
+            devConfig[parent]->ResetHubPort(port);
+        }
+    }
+    else if (rcode == hrJERR && retries < 3)
+    { // Some devices returns this when plugged in - trying to initialize the device again usually works
+        delay(100);
+        retries++;
+        goto again;
+    }
+    else if (rcode)
+        return rcode;
+
+    rcode = devConfig[driver]->Init(parent, port, lowspeed);
+    if (rcode == hrJERR && retries < 3)
+    { // Some devices returns this when plugged in - trying to initialize the device again usually works
+        delay(100);
+        retries++;
+        goto again;
+    }
+    if (rcode)
+    {
+        // Issue a bus reset, because the device may be in a limbo state
+        if (parent == 0)
+        {
+            // Send a bus reset on the root interface.
+            regWr(rHCTL, bmBUSRST); //issue bus reset
+            delay(102);             // delay 102ms, compensate for clock inaccuracy.
+        }
+        else
+        {
+            // reset parent port
+            devConfig[parent]->ResetHubPort(port);
+        }
+    }
+    return rcode;
+}
+
+/*
+ * This is broken. We need to enumerate differently.
+ * It causes major problems with several devices if detected in an unexpected order.
+ *
+ *
+ * Oleg - I wouldn't do anything before the newly connected device is considered sane.
+ * i.e.(delays are not indicated for brevity):
+ * 1. reset
+ * 2. GetDevDescr();
+ * 3a. If ACK, continue with allocating address, addressing, etc.
+ * 3b. Else reset again, count resets, stop at some number (5?).
+ * 4. When max.number of resets is reached, toggle power/fail
+ * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD()
+ * it doesn't need to be reset again
+ * New steps proposal:
+ * 1: get address pool instance. exit on fail
+ * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail.
+ * 3: bus reset, 100ms delay
+ * 4: set address
+ * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail
+ * 6: while (configurations) {
+ *              for(each configuration) {
+ *                      for (each driver) {
+ *                              6a: Ask device if it likes configuration. Returns 0 on OK.
+ *                                      If successful, the driver configured device.
+ *                                      The driver now owns the endpoints, and takes over managing them.
+ *                                      The following will need codes:
+ *                                          Everything went well, instance consumed, exit with success.
+ *                                          Instance already in use, ignore it, try next driver.
+ *                                          Not a supported device, ignore it, try next driver.
+ *                                          Not a supported configuration for this device, ignore it, try next driver.
+ *                                          Could not configure device, fatal, exit with fail.
+ *                      }
+ *              }
+ *    }
+ * 7: for(each driver) {
+ *      7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID
+ * 8: if we get here, no driver likes the device plugged in, so exit failure.
+ *
+ */
+uint8_t USB::Configuring(uint8_t parent, uint8_t port, bool lowspeed)
+{
+    //uint8_t bAddress = 0;
+    //DEBUG("Configuring: parent = %i, port = %i\r\n", parent, port);
+    uint8_t devConfigIndex;
+    uint8_t rcode = 0;
+    uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)];
+    USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR *>(buf);
+    UsbDevice *p = NULL;
+    EpInfo *oldep_ptr = NULL;
+    EpInfo epInfo;
+
+    epInfo.epAddr = 0;
+    epInfo.maxPktSize = 8;
+    epInfo.bmSndToggle = 0;
+    epInfo.bmRcvToggle = 0;
+    epInfo.bmNakPower = USB_NAK_MAX_POWER;
+
+    //delay(2000);
+    AddressPool &addrPool = GetAddressPool();
+    // Get pointer to pseudo device with address 0 assigned
+    p = addrPool.GetUsbDevicePtr(0);
+    if (!p)
+    {
+        //DEBUG("Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\r\n");
+        return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+    }
+
+    // Save old pointer to EP_RECORD of address 0
+    oldep_ptr = p->epinfo;
+
+    // Temporary assign new pointer to epInfo to p->epinfo in order to
+    // avoid toggle inconsistence
+
+    p->epinfo = &epInfo;
+
+    p->lowspeed = lowspeed;
+    // Get device descriptor
+    rcode = getDevDescr(0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t *)buf);
+
+    // Restore p->epinfo
+    p->epinfo = oldep_ptr;
+
+    if (rcode)
+    {
+        //DEBUG("Configuring error: Can't get USB_DEVICE_DESCRIPTOR\r\n");
+        return rcode;
+    }
+
+    // to-do?
+    // Allocate new address according to device class
+    //bAddress = addrPool.AllocAddress(parent, false, port);
+
+    uint16_t vid = udd->idVendor;
+    uint16_t pid = udd->idProduct;
+    uint8_t klass = udd->bDeviceClass;
+    uint8_t subklass = udd->bDeviceSubClass;
+    // Attempt to configure if VID/PID or device class matches with a driver
+    // Qualify with subclass too.
+    //
+    // VID/PID & class tests default to false for drivers not yet ported
+    // subclass defaults to true, so you don't have to define it if you don't have to.
+    //
+    for (devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++)
+    {
+        if (!devConfig[devConfigIndex])
+            continue; // no driver
+        if (devConfig[devConfigIndex]->GetAddress())
+            continue; // consumed
+        if (devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass)))
+        {
+            rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed);
+            if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED)
+                break;
+        }
+    }
+
+    if (devConfigIndex < USB_NUMDEVICES)
+    {
+        return rcode;
+    }
+
+    // blindly attempt to configure
+    for (devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++)
+    {
+        if (!devConfig[devConfigIndex])
+            continue;
+        if (devConfig[devConfigIndex]->GetAddress())
+            continue; // consumed
+        if (devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass)))
+            continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above
+        rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed);
+
+        //DEBUG("ERROR ENUMERATING %2.2x\r\n", rcode);
+        if (!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE))
+        {
+            // in case of an error dev_index should be reset to 0
+            //                in order to start from the very beginning the
+            //                next time the program gets here
+            //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE)
+            //        devConfigIndex = 0;
+            return rcode;
+        }
+    }
+    // if we get here that means that the device class is not supported by any of registered classes
+    rcode = DefaultAddressing(parent, port, lowspeed);
+
+    return rcode;
+}
+
+uint8_t USB::ReleaseDevice(uint8_t addr)
+{
+    if (!addr)
+        return 0;
+
+    for (uint8_t i = 0; i < USB_NUMDEVICES; i++)
+    {
+        if (!devConfig[i])
+            continue;
+        if (devConfig[i]->GetAddress() == addr)
+            return devConfig[i]->Release();
+    }
+    return 0;
+}
+
+#if 1 //!defined(USB_METHODS_INLINE)
+//get device descriptor
+
+uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr)
+{
+    return (ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, nbytes, dataptr, NULL));
+}
+//get configuration descriptor
+
+uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t *dataptr)
+{
+    return (ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, nbytes, dataptr, NULL));
+}
+
+/* Requests Configuration Descriptor. Sends two Get Conf Descr requests. The first one gets the total length of all descriptors, then the second one requests this
+ total length. The length of the first request can be shorter ( 4 bytes ), however, there are devices which won't work unless this length is set to 9 */
+uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p)
+{
+    const uint8_t bufSize = 64;
+    uint8_t buf[bufSize];
+    USB_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR *>(buf);
+
+    uint8_t ret = getConfDescr(addr, ep, 9, conf, buf);
+
+    if (ret)
+        return ret;
+
+    uint16_t total = ucd->wTotalLength;
+
+    //USBTRACE2("\r\ntotal conf.size:", total);
+
+    return (ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, total, bufSize, buf, p));
+}
+
+//get string descriptor
+
+uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t *dataptr)
+{
+    return (ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, ns, ns, dataptr, NULL));
+}
+//set address
+
+uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr)
+{
+    uint8_t rcode = ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL);
+    //delay(2); //per USB 2.0 sect.9.2.6.3
+    delay(300); // Older spec says you should wait at least 200ms
+    return rcode;
+    //return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL));
+}
+//set configuration
+
+uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value)
+{
+    return (ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL));
+}
+
+#endif // defined(USB_METHODS_INLINE)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/Usb.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,49 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+/* USB functions */
+#ifndef _usb_h_
+#define _usb_h_
+
+// WARNING: Do not change the order of includes, or stuff will break!
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdio.h>
+
+// None of these should ever be included by a driver, or a user's sketch.
+#include "settings.h"
+#include "printhex.h"
+#include "message.h"
+#include "hexdump.h"
+#include "sink_parser.h"
+#include "max3421e.h"
+#include "address.h"
+#include "avrpins.h"
+#include "usb_ch9.h"
+#include "usbhost.h"
+#include "UsbCore.h"
+#include "parsetools.h"
+#include "confdescparser.h"
+
+#endif //_usb_h_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/UsbCore.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,317 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+// warning
+// #define _usb_h_
+// #define MBED_H
+
+#if !defined(_usb_h_) || defined(USBCORE_H)
+#error "Never include UsbCore.h directly; include Usb.h instead"
+#else
+#define USBCORE_H
+
+// Not used anymore? If anyone uses this, please let us know so that this may be
+// moved to the proper place, settings.h.
+//#define USB_METHODS_INLINE
+
+
+/* Common setup data constant combinations  */
+#define bmREQ_GET_DESCR USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE   //get descriptor request type
+#define bmREQ_SET USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_DEVICE         //set request type for all but 'set feature' and 'set interface'
+#define bmREQ_CL_GET_INTF USB_SETUP_DEVICE_TO_HOST | USB_SETUP_TYPE_CLASS | USB_SETUP_RECIPIENT_INTERFACE //get interface request type
+
+// D7           data transfer direction (0 - host-to-device, 1 - device-to-host)
+// D6-5         Type (0- standard, 1 - class, 2 - vendor, 3 - reserved)
+// D4-0         Recipient (0 - device, 1 - interface, 2 - endpoint, 3 - other, 4..31 - reserved)
+
+// USB Device Classes
+#define USB_CLASS_USE_CLASS_INFO 0x00                                                                     // Use Class Info in the Interface Descriptors
+#define USB_CLASS_AUDIO 0x01                                                                              // Audio
+#define USB_CLASS_COM_AND_CDC_CTRL 0x02                                                                   // Communications and CDC Control
+#define USB_CLASS_HID 0x03                                                                                // HID
+#define USB_CLASS_PHYSICAL 0x05                                                                           // Physical
+#define USB_CLASS_IMAGE 0x06                                                                              // Image
+#define USB_CLASS_PRINTER 0x07                                                                            // Printer
+#define USB_CLASS_MASS_STORAGE 0x08                                                                       // Mass Storage
+#define USB_CLASS_HUB 0x09                                                                                // Hub
+#define USB_CLASS_CDC_DATA 0x0a                                                                           // CDC-Data
+#define USB_CLASS_SMART_CARD 0x0b                                                                         // Smart-Card
+#define USB_CLASS_CONTENT_SECURITY 0x0d                                                                   // Content Security
+#define USB_CLASS_VIDEO 0x0e                                                                              // Video
+#define USB_CLASS_PERSONAL_HEALTH 0x0f                                                                    // Personal Healthcare
+#define USB_CLASS_DIAGNOSTIC_DEVICE 0xdc                                                                  // Diagnostic Device
+#define USB_CLASS_WIRELESS_CTRL 0xe0                                                                      // Wireless Controller
+#define USB_CLASS_MISC 0xef                                                                               // Miscellaneous
+#define USB_CLASS_APP_SPECIFIC 0xfe                                                                       // Application Specific
+#define USB_CLASS_VENDOR_SPECIFIC 0xff                                                                    // Vendor Specific
+
+// Additional Error Codes
+#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED 0xD1
+#define USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE 0xD2
+#define USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS 0xD3
+#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL 0xD4
+#define USB_ERROR_HUB_ADDRESS_OVERFLOW 0xD5
+#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL 0xD6
+#define USB_ERROR_EPINFO_IS_NULL 0xD7
+#define USB_ERROR_INVALID_ARGUMENT 0xD8
+#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE 0xD9
+#define USB_ERROR_INVALID_MAX_PKT_SIZE 0xDA
+#define USB_ERROR_EP_NOT_FOUND_IN_TBL 0xDB
+#define USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET 0xE0
+#define USB_ERROR_FailGetDevDescr 0xE1
+#define USB_ERROR_FailSetDevTblEntry 0xE2
+#define USB_ERROR_FailGetConfDescr 0xE3
+#define USB_ERROR_TRANSFER_TIMEOUT 0xFF
+
+#define USB_XFER_TIMEOUT 5000 // (5000) USB transfer timeout in milliseconds, per section 9.2.6.1 of USB 2.0 spec
+//#define USB_NAK_LIMIT         32000   // NAK limit for a transfer. 0 means NAKs are not counted
+#define USB_RETRY_LIMIT 3     // 3 retry limit for a transfer
+#define USB_SETTLE_DELAY 200  // settle delay in milliseconds
+
+#define USB_NUMDEVICES 16       //number of USB devices
+//#define HUB_MAX_HUBS          7       // maximum number of hubs that can be attached to the host controller
+#define HUB_PORT_RESET_DELAY 20 // hub port reset delay 10 ms recomended, can be up to 20 ms
+
+/* USB state machine states */
+#define USB_STATE_MASK 0xf0
+
+#define USB_STATE_DETACHED 0x10
+#define USB_DETACHED_SUBSTATE_INITIALIZE 0x11
+#define USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE 0x12
+#define USB_DETACHED_SUBSTATE_ILLEGAL 0x13
+#define USB_ATTACHED_SUBSTATE_SETTLE 0x20
+#define USB_ATTACHED_SUBSTATE_RESET_DEVICE 0x30
+#define USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE 0x40
+#define USB_ATTACHED_SUBSTATE_WAIT_SOF 0x50
+#define USB_ATTACHED_SUBSTATE_WAIT_RESET 0x51
+#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE 0x60
+#define USB_STATE_ADDRESSING 0x70
+#define USB_STATE_CONFIGURING 0x80
+#define USB_STATE_RUNNING 0x90
+#define USB_STATE_ERROR 0xa0
+
+class USBDeviceConfig
+{
+public:
+    virtual uint8_t Init(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed __attribute__((unused)))
+    {
+        return 0;
+    }
+
+    virtual uint8_t ConfigureDevice(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed __attribute__((unused)))
+    {
+        return 0;
+    }
+
+    virtual uint8_t Release()
+    {
+        return 0;
+    }
+
+    virtual uint8_t Poll()
+    {
+        return 0;
+    }
+
+    virtual uint8_t GetAddress()
+    {
+        return 0;
+    }
+
+    virtual void ResetHubPort(uint8_t port __attribute__((unused)))
+    {
+        return;
+    } // Note used for hubs only!
+
+    virtual bool VIDPIDOK(uint16_t vid __attribute__((unused)), uint16_t pid __attribute__((unused)))
+    {
+        return false;
+    }
+
+    virtual bool DEVCLASSOK(uint8_t klass __attribute__((unused)))
+    {
+        return false;
+    }
+
+    virtual bool DEVSUBCLASSOK(uint8_t subklass __attribute__((unused)))
+    {
+        return true;
+    }
+};
+
+/* USB Setup Packet Structure   */
+typedef struct
+{
+
+    union {                    // offset   description
+        uint8_t bmRequestType; //   0      Bit-map of request type
+
+        struct
+        {
+            uint8_t recipient : 5; //          Recipient of the request
+            uint8_t type : 2;      //          Type of request
+            uint8_t direction : 1; //          Direction of data X-fer
+        } __attribute__((packed));
+    } ReqType_u;
+    uint8_t bRequest; //   1      Request
+
+    union {
+        uint16_t wValue; //   2      Depends on bRequest
+
+        struct
+        {
+            uint8_t wValueLo;
+            uint8_t wValueHi;
+        } __attribute__((packed));
+    } wVal_u;
+    uint16_t wIndex;  //   4      Depends on bRequest
+    uint16_t wLength; //   6      Depends on bRequest
+} __attribute__((packed)) SETUP_PKT, *PSETUP_PKT;
+
+// Base class for incoming data parser
+
+class USBReadParser
+{
+public:
+    virtual void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) = 0;
+};
+
+class USB : public MAX3421E
+{
+    AddressPoolImpl<USB_NUMDEVICES> addrPool;
+    USBDeviceConfig *devConfig[USB_NUMDEVICES];
+    uint8_t bmHubPre;
+
+private:
+    static Timer arduinoTimer; // for millis() & micros() function in Arduino
+public:
+    static uint32_t read_ms() { return arduinoTimer.read_ms(); }
+    static uint32_t read_us() { return arduinoTimer.read_us(); }
+    
+public:
+    USB(PinName mosi, PinName miso, PinName sclk, PinName ssel, PinName intr);
+
+    void SetHubPreMask()
+    {
+        bmHubPre |= bmHUBPRE;
+    };
+
+    void ResetHubPreMask()
+    {
+        bmHubPre &= (~bmHUBPRE);
+    };
+
+    AddressPool &GetAddressPool()
+    {
+        return (AddressPool &)addrPool;
+    };
+
+    uint8_t RegisterDeviceClass(USBDeviceConfig *pdev)
+    {
+        for (uint8_t i = 0; i < USB_NUMDEVICES; i++)
+        {
+            if (!devConfig[i])
+            {
+                devConfig[i] = pdev;
+                return 0;
+            }
+        }
+        return USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS;
+    };
+
+    void ForEachUsbDevice(UsbDeviceHandleFunc pfunc)
+    {
+        addrPool.ForEachUsbDevice(pfunc);
+    };
+    uint8_t getUsbTaskState(void);
+    void setUsbTaskState(uint8_t state);
+
+    EpInfo *getEpInfoEntry(uint8_t addr, uint8_t ep);
+    uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo *eprecord_ptr);
+
+    /* Control requests */
+    uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr);
+    uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t *dataptr);
+
+    uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p);
+
+    uint8_t getStrDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t index, uint16_t langid, uint8_t *dataptr);
+    uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr);
+    uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value);
+    /**/
+    uint8_t ctrlData(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *dataptr, bool direction);
+    uint8_t ctrlStatus(uint8_t ep, bool direction, uint16_t nak_limit);
+    uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval = 0);
+    uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t *data);
+    uint8_t dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit);
+
+    void Task(void);
+
+    uint8_t DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed);
+    uint8_t Configuring(uint8_t parent, uint8_t port, bool lowspeed);
+    uint8_t ReleaseDevice(uint8_t addr);
+
+    uint8_t ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,
+                    uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t *dataptr, USBReadParser *p);
+    
+private:
+    void init();
+    uint8_t SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit);
+    uint8_t OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data);
+    uint8_t InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data, uint8_t bInterval = 0);
+    uint8_t AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed);
+};
+
+#if 0 //defined(USB_METHODS_INLINE)
+//get device descriptor
+
+inline uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {
+        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, dataptr));
+}
+//get configuration descriptor
+
+inline uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {
+        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, dataptr));
+}
+//get string descriptor
+
+inline uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t nuint8_ts, uint8_t index, uint16_t langid, uint8_t* dataptr) {
+        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, nuint8_ts, dataptr));
+}
+//set address
+
+inline uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {
+        return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, NULL));
+}
+//set configuration
+
+inline uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {
+        return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, NULL));
+}
+
+Timer USB::arduinoTimer;
+
+#endif // defined(USB_METHODS_INLINE)
+
+#endif /* USBCORE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/Wii.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,1280 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+
+ IR camera support added by Allan Glover (adglover9.81@gmail.com) and Kristian Lauszus
+ */
+
+#include "Wii.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report send by the Wii controllers
+
+const uint8_t WII_LEDS[] PROGMEM = {
+        0x00, // OFF
+        0x10, // LED1
+        0x20, // LED2
+        0x40, // LED3
+        0x80, // LED4
+
+        0x90, // LED5
+        0xA0, // LED6
+        0xC0, // LED7
+        0xD0, // LED8
+        0xE0, // LED9
+        0xF0, // LED10
+};
+
+const uint32_t WII_BUTTONS[] PROGMEM = {
+        0x00008, // UP
+        0x00002, // RIGHT
+        0x00004, // DOWN
+        0x00001, // LEFT
+
+        0, // Skip
+        0x00010, // PLUS
+        0x00100, // TWO
+        0x00200, // ONE
+
+        0x01000, // MINUS
+        0x08000, // HOME
+        0x10000, // Z
+        0x20000, // C
+
+        0x00400, // B
+        0x00800, // A
+};
+const uint32_t WII_PROCONTROLLER_BUTTONS[] PROGMEM = {
+        0x00100, // UP
+        0x00080, // RIGHT
+        0x00040, // DOWN
+        0x00200, // LEFT
+
+        0, // Skip
+        0x00004, // PLUS
+        0x20000, // L3
+        0x10000, // R3
+
+        0x00010, // MINUS
+        0x00008, // HOME
+        0, 0, // Skip
+
+        0x04000, // B
+        0x01000, // A
+        0x00800, // X
+        0x02000, // Y
+
+        0x00020, // L
+        0x00002, // R
+        0x08000, // ZL
+        0x00400, // ZR
+};
+
+WII::WII(BTD *p, bool pair) :
+BluetoothService(p) // Pointer to USB class instance - mandatory
+{
+        pBtd->pairWithWii = pair;
+
+        HIDBuffer[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+
+        /* Set device cid for the control and intterrupt channelse - LSB */
+        control_dcid[0] = 0x60; // 0x0060
+        control_dcid[1] = 0x00;
+        interrupt_dcid[0] = 0x61; // 0x0061
+        interrupt_dcid[1] = 0x00;
+
+        Reset();
+}
+
+void WII::Reset() {
+        wiimoteConnected = false;
+        nunchuckConnected = false;
+        motionPlusConnected = false;
+        activateNunchuck = false;
+        motionValuesReset = false;
+        activeConnection = false;
+        motionPlusInside = false;
+        pBtd->wiiUProController = false;
+        wiiUProControllerConnected = false;
+        wiiBalanceBoardConnected = false;
+        l2cap_event_flag = 0; // Reset flags
+        l2cap_state = L2CAP_WAIT;
+}
+
+void WII::disconnect() { // Use this void to disconnect any of the controllers
+        if(!motionPlusInside) { // The old Wiimote needs a delay after the first command or it will automatically reconnect
+                if(motionPlusConnected) {
+#ifdef DEBUG_USB_HOST
+                        Notify(PSTR("\r\nDeactivating Motion Plus"), 0x80);
+#endif
+                        initExtension1(); // This will disable the Motion Plus extension
+                }
+                timer = (uint32_t)millis() + 1000; // We have to wait for the message before the rest of the channels can be deactivated
+        } else
+                timer = (uint32_t)millis(); // Don't wait
+        // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection
+        pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid);
+        Reset();
+        l2cap_state = L2CAP_INTERRUPT_DISCONNECT;
+}
+
+void WII::ACLData(uint8_t* l2capinbuf) {
+        if(!pBtd->l2capConnectionClaimed && pBtd->incomingWii && !wiimoteConnected && !activeConnection) {
+                if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
+                        if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
+                                motionPlusInside = pBtd->motionPlusInside;
+                                pBtd->incomingWii = false;
+                                pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service
+                                activeConnection = true;
+                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
+                                l2cap_state = L2CAP_WAIT;
+                        }
+                }
+        }
+
+        if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok
+                if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U
+                        if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nL2CAP Command Rejected - Reason: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+#endif
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) {
+                                if(((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success
+                                        if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Control Connection Complete"), 0x80);
+                                                identifier = l2capinbuf[9];
+                                                control_scid[0] = l2capinbuf[12];
+                                                control_scid[1] = l2capinbuf[13];
+                                                l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED);
+                                        } else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Interrupt Connection Complete"), 0x80);
+                                                identifier = l2capinbuf[9];
+                                                interrupt_scid[0] = l2capinbuf[12];
+                                                interrupt_scid[1] = l2capinbuf[13];
+                                                l2cap_set_flag(L2CAP_FLAG_INTERRUPT_CONNECTED);
+                                        }
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {
+#ifdef EXTRADEBUG
+                                Notify(PSTR("\r\nL2CAP Connection Request - PSM: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                Notify(PSTR(" SCID: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+                                Notify(PSTR(" Identifier: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
+#endif
+                                if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {
+                                        identifier = l2capinbuf[9];
+                                        control_scid[0] = l2capinbuf[14];
+                                        control_scid[1] = l2capinbuf[15];
+                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST);
+                                } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {
+                                        identifier = l2capinbuf[9];
+                                        interrupt_scid[0] = l2capinbuf[14];
+                                        interrupt_scid[1] = l2capinbuf[15];
+                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {
+                                if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success
+                                        if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Control Configuration Complete"), 0x80);
+                                                identifier = l2capinbuf[9];
+                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);
+                                        } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+                                                //Notify(PSTR("\r\nHID Interrupt Configuration Complete"), 0x80);
+                                                identifier = l2capinbuf[9];
+                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);
+                                        }
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {
+                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+                                        //Notify(PSTR("\r\nHID Control Configuration Request"), 0x80);
+                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);
+                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+                                        //Notify(PSTR("\r\nHID Interrupt Configuration Request"), 0x80);
+                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {
+                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nDisconnect Request: Control Channel"), 0x80);
+#endif
+                                        identifier = l2capinbuf[9];
+                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);
+                                        Reset();
+                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nDisconnect Request: Interrupt Channel"), 0x80);
+#endif
+                                        identifier = l2capinbuf[9];
+                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);
+                                        Reset();
+                                }
+                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {
+                                if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Response: Control Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE);
+                                } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {
+                                        //Notify(PSTR("\r\nDisconnect Response: Interrupt Channel"), 0x80);
+                                        identifier = l2capinbuf[9];
+                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);
+                                }
+                        }
+#ifdef EXTRADEBUG
+                        else {
+                                identifier = l2capinbuf[9];
+                                Notify(PSTR("\r\nL2CAP Unknown Signaling Command: "), 0x80);
+                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);
+                        }
+#endif
+                } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt
+                        //Notify(PSTR("\r\nL2CAP Interrupt"), 0x80);
+                        if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
+                                if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || (l2capinbuf[9] >= 0x30 && l2capinbuf[9] <= 0x37) || l2capinbuf[9] == 0x3e || l2capinbuf[9] == 0x3f) { // These reports include the buttons
+                                        if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33) // These reports have no extensions bytes
+                                                ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8));
+                                        else if(wiiUProControllerConnected)
+                                                ButtonState = (uint32_t)(((~l2capinbuf[23]) & 0xFE) | ((uint16_t)(~l2capinbuf[24]) << 8) | ((uint32_t)((~l2capinbuf[25]) & 0x03) << 16));
+                                        else if(motionPlusConnected) {
+                                                if(l2capinbuf[20] & 0x02) // Only update the Wiimote buttons, since the extension bytes are from the Motion Plus
+                                                        ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)(ButtonState & 0xFFFF0000)));
+                                                else if(nunchuckConnected) // Update if it's a report from the Nunchuck
+                                                        ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)((~l2capinbuf[20]) & 0x0C) << 14));
+                                                //else if(classicControllerConnected) // Update if it's a report from the Classic Controller
+                                        } else if(nunchuckConnected) // The Nunchuck is directly connected
+                                                ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)((~l2capinbuf[20]) & 0x03) << 16));
+                                                //else if(classicControllerConnected) // The Classic Controller is directly connected
+                                        else if(!unknownExtensionConnected)
+                                                ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8));
+#ifdef PRINTREPORT
+                                        Notify(PSTR("ButtonState: "), 0x80);
+                                        D_PrintHex<uint32_t > (ButtonState, 0x80);
+                                        Notify(PSTR("\r\n"), 0x80);
+#endif
+                                        if(ButtonState != OldButtonState) {
+                                                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
+                                                OldButtonState = ButtonState;
+                                        }
+                                }
+                                if(l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33 || l2capinbuf[9] == 0x35 || l2capinbuf[9] == 0x37) { // Read the accelerometer
+                                        accXwiimote = ((l2capinbuf[12] << 2) | (l2capinbuf[10] & 0x60 >> 5)) - 500;
+                                        accYwiimote = ((l2capinbuf[13] << 2) | (l2capinbuf[11] & 0x20 >> 4)) - 500;
+                                        accZwiimote = ((l2capinbuf[14] << 2) | (l2capinbuf[11] & 0x40 >> 5)) - 500;
+                                }
+                                switch(l2capinbuf[9]) {
+                                        case 0x20: // Status Information - (a1) 20 BB BB LF 00 00 VV
+#ifdef EXTRADEBUG
+                                                Notify(PSTR("\r\nStatus report was received"), 0x80);
+#endif
+                                                wiiState = l2capinbuf[12]; // (0x01: Battery is nearly empty), (0x02:  An Extension Controller is connected), (0x04: Speaker enabled), (0x08: IR enabled), (0x10: LED1, 0x20: LED2, 0x40: LED3, 0x80: LED4)
+                                                batteryLevel = l2capinbuf[15]; // Update battery level
+
+                                                if(!checkBatteryLevel) { // If this is true it means that the user must have called getBatteryLevel()
+                                                        if(l2capinbuf[12] & 0x02) { // Check if a extension is connected
+#ifdef DEBUG_USB_HOST
+                                                                if(!unknownExtensionConnected)
+                                                                        Notify(PSTR("\r\nExtension connected"), 0x80);
+#endif
+                                                                unknownExtensionConnected = true;
+#ifdef WIICAMERA
+                                                                if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera
+#endif
+                                                                        setReportMode(false, 0x35); // Also read the extension
+                                                        } else {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nExtension disconnected"), 0x80);
+#endif
+                                                                if(motionPlusConnected) {
+#ifdef DEBUG_USB_HOST
+                                                                        Notify(PSTR(" - from Motion Plus"), 0x80);
+#endif
+                                                                        wii_clear_flag(WII_FLAG_NUNCHUCK_CONNECTED);
+                                                                        if(!activateNunchuck) // If it's already trying to initialize the Nunchuck don't set it to false
+                                                                                nunchuckConnected = false;
+                                                                        //else if(classicControllerConnected)
+                                                                } else if(nunchuckConnected) {
+#ifdef DEBUG_USB_HOST
+                                                                        Notify(PSTR(" - Nunchuck"), 0x80);
+#endif
+                                                                        nunchuckConnected = false; // It must be the Nunchuck controller then
+                                                                        wii_clear_flag(WII_FLAG_NUNCHUCK_CONNECTED);
+                                                                        onInit();
+#ifdef WIICAMERA
+                                                                        if(!isIRCameraEnabled()) // We still want to read from the IR camera, so do not change the report mode
+#endif
+                                                                                setReportMode(false, 0x31); // If there is no extension connected we will read the buttons and accelerometer
+                                                                } else {
+#ifdef WIICAMERA
+                                                                        if(!isIRCameraEnabled()) // We still want to read from the IR camera, so do not change the report mode
+#endif
+                                                                                setReportMode(false, 0x31); // If there is no extension connected we will read the buttons and accelerometer
+                                                                }
+                                                        }
+                                                }
+                                                else {
+#ifdef EXTRADEBUG
+                                                        Notify(PSTR("\r\nChecking battery level"), 0x80);
+#endif
+                                                        checkBatteryLevel = false; // Check for extensions by default
+                                                }
+#ifdef DEBUG_USB_HOST
+                                                if(l2capinbuf[12] & 0x01)
+                                                        Notify(PSTR("\r\nWARNING: Battery is nearly empty"), 0x80);
+#endif
+
+                                                break;
+                                        case 0x21: // Read Memory Data
+                                                if((l2capinbuf[12] & 0x0F) == 0) { // No error
+                                                        uint8_t reportLength = (l2capinbuf[12] >> 4) + 1; // // Bit 4-7 is the length - 1
+                                                        // See: http://wiibrew.org/wiki/Wiimote/Extension_Controllers
+                                                        if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x00 && l2capinbuf[20] == 0x00) {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nNunchuck connected"), 0x80);
+#endif
+                                                                wii_set_flag(WII_FLAG_NUNCHUCK_CONNECTED);
+                                                        } else if(l2capinbuf[16] == 0x00 && (l2capinbuf[17] == 0xA6 || l2capinbuf[17] == 0xA4) && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x00 && l2capinbuf[20] == 0x05) {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nMotion Plus connected"), 0x80);
+#endif
+                                                                wii_set_flag(WII_FLAG_MOTION_PLUS_CONNECTED);
+                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x04 && l2capinbuf[20] == 0x05) {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nMotion Plus activated in normal mode"), 0x80);
+#endif
+                                                                motionPlusConnected = true;
+#ifdef WIICAMERA
+                                                                if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera
+#endif
+                                                                        setReportMode(false, 0x35); // Also read the extension
+                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x05 && l2capinbuf[20] == 0x05) {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nMotion Plus activated in Nunchuck pass-through mode"), 0x80);
+#endif
+                                                                activateNunchuck = false;
+                                                                motionPlusConnected = true;
+                                                                nunchuckConnected = true;
+#ifdef WIICAMERA
+                                                                if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera
+#endif
+                                                                        setReportMode(false, 0x35); // Also read the extension
+                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA6 && l2capinbuf[18] == 0x20 && (l2capinbuf[19] == 0x00 || l2capinbuf[19] == 0x04 || l2capinbuf[19] == 0x05 || l2capinbuf[19] == 0x07) && l2capinbuf[20] == 0x05) {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nInactive Wii Motion Plus"), 0x80);
+                                                                Notify(PSTR("\r\nPlease unplug the Motion Plus, disconnect the Wiimote and then replug the Motion Plus Extension"), 0x80);
+#endif
+                                                                stateCounter = 300; // Skip the rest in "WII_CHECK_MOTION_PLUS_STATE"
+                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x01 && l2capinbuf[20] == 0x20) {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nWii U Pro Controller connected"), 0x80);
+#endif
+                                                                wiiUProControllerConnected = true;
+                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x04 && l2capinbuf[20] == 0x02) {
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nWii Balance Board connected"), 0x80);
+#endif
+                                                                setReportMode(false, 0x32); // Read the Wii Balance Board extension
+                                                                wii_set_flag(WII_FLAG_CALIBRATE_BALANCE_BOARD);
+                                                        }
+                                                        // Wii Balance Board calibration reports (24 bits in total)
+                                                        else if(l2capinbuf[13] == 0x00 && l2capinbuf[14] == 0x24 && reportLength == 16) { // First 16-bit
+                                                                for(uint8_t i = 0; i < 2; i++) {
+                                                                        for(uint8_t j = 0; j < 4; j++)
+                                                                                wiiBalanceBoardCal[i][j] = l2capinbuf[16 + 8 * i + 2 * j] | l2capinbuf[15 + 8 * i + 2 * j] << 8;
+                                                                }
+                                                        } else if(l2capinbuf[13] == 0x00 && l2capinbuf[14] == 0x34 && reportLength == 8) { // Last 8-bit
+                                                                for(uint8_t j = 0; j < 4; j++)
+                                                                        wiiBalanceBoardCal[2][j] = l2capinbuf[16 + 2 * j] | l2capinbuf[15 + 2 * j] << 8;
+#ifdef DEBUG_USB_HOST
+                                                                Notify(PSTR("\r\nWii Balance Board calibration values read successfully"), 0x80);
+#endif
+                                                                wii_clear_flag(WII_FLAG_CALIBRATE_BALANCE_BOARD);
+                                                                wiiBalanceBoardConnected = true;
+                                                        }
+#ifdef DEBUG_USB_HOST
+                                                        else {
+                                                                Notify(PSTR("\r\nUnknown Device: "), 0x80);
+                                                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+                                                                Notify(PSTR("\r\nData: "), 0x80);
+                                                                for(uint8_t i = 0; i < reportLength; i++) {
+                                                                        D_PrintHex<uint8_t > (l2capinbuf[15 + i], 0x80);
+                                                                        Notify(PSTR(" "), 0x80);
+                                                                }
+                                                        }
+#endif
+                                                }
+#ifdef EXTRADEBUG
+                                                else {
+                                                        Notify(PSTR("\r\nReport Error: "), 0x80);
+                                                        D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);
+                                                        D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);
+                                                }
+#endif
+                                                break;
+                                        case 0x22: // Acknowledge output report, return function result
+#ifdef DEBUG_USB_HOST
+                                                if(l2capinbuf[13] != 0x00) { // Check if there is an error
+                                                        Notify(PSTR("\r\nCommand failed: "), 0x80);
+                                                        D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);
+                                                }
+#endif
+                                                break;
+                                        case 0x30: // Core buttons - (a1) 30 BB BB
+                                                break;
+                                        case 0x31: // Core Buttons and Accelerometer - (a1) 31 BB BB AA AA AA
+                                                break;
+                                        case 0x32: // Core Buttons with 8 Extension bytes - (a1) 32 BB BB EE EE EE EE EE EE EE EE
+                                                // See: http://wiibrew.org/wiki/Wii_Balance_Board#Data_Format
+                                                wiiBalanceBoardRaw[TopRight] = l2capinbuf[13] | l2capinbuf[12] << 8; // Top right
+                                                wiiBalanceBoardRaw[BotRight] = l2capinbuf[15] | l2capinbuf[14] << 8; // Bottom right
+                                                wiiBalanceBoardRaw[TopLeft] = l2capinbuf[17] | l2capinbuf[16] << 8; // Top left
+                                                wiiBalanceBoardRaw[BotLeft] = l2capinbuf[19] | l2capinbuf[18] << 8; // Bottom left
+                                                break;
+                                        case 0x33: // Core Buttons with Accelerometer and 12 IR bytes - (a1) 33 BB BB AA AA AA II II II II II II II II II II II II
+#ifdef WIICAMERA
+                                                // Read the IR data
+                                                IR_object_x1 = (l2capinbuf[15] | ((uint16_t)(l2capinbuf[17] & 0x30) << 4)); // x position
+                                                IR_object_y1 = (l2capinbuf[16] | ((uint16_t)(l2capinbuf[17] & 0xC0) << 2)); // y position
+                                                IR_object_s1 = (l2capinbuf[17] & 0x0F); // Size value, 0-15
+
+                                                IR_object_x2 = (l2capinbuf[18] | ((uint16_t)(l2capinbuf[20] & 0x30) << 4));
+                                                IR_object_y2 = (l2capinbuf[19] | ((uint16_t)(l2capinbuf[20] & 0xC0) << 2));
+                                                IR_object_s2 = (l2capinbuf[20] & 0x0F);
+
+                                                IR_object_x3 = (l2capinbuf[21] | ((uint16_t)(l2capinbuf[23] & 0x30) << 4));
+                                                IR_object_y3 = (l2capinbuf[22] | ((uint16_t)(l2capinbuf[23] & 0xC0) << 2));
+                                                IR_object_s3 = (l2capinbuf[23] & 0x0F);
+
+                                                IR_object_x4 = (l2capinbuf[24] | ((uint16_t)(l2capinbuf[26] & 0x30) << 4));
+                                                IR_object_y4 = (l2capinbuf[25] | ((uint16_t)(l2capinbuf[26] & 0xC0) << 2));
+                                                IR_object_s4 = (l2capinbuf[26] & 0x0F);
+#endif
+                                                break;
+                                        case 0x34: // Core Buttons with 19 Extension bytes - (a1) 34 BB BB EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE
+                                                break;
+                                                /* 0x3e and 0x3f both give unknown report types when report mode is 0x3e or 0x3f with mode number 0x05 */
+                                        case 0x3E: // Core Buttons with Accelerometer and 32 IR bytes
+                                                // (a1) 31 BB BB AA AA AA II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II
+                                                // corresponds to output report mode 0x3e
+
+                                                /**** for reading in full mode: DOES NOT WORK YET ****/
+                                                /* When it works it will also have intensity and bounding box data */
+                                                /*
+                                                IR_object_x1 = (l2capinbuf[13] | ((uint16_t)(l2capinbuf[15] & 0x30) << 4));
+                                                IR_object_y1 = (l2capinbuf[14] | ((uint16_t)(l2capinbuf[15] & 0xC0) << 2));
+                                                IR_object_s1 = (l2capinbuf[15] & 0x0F);
+                                                 */
+                                                break;
+                                        case 0x3F:
+                                                /*
+                                                IR_object_x1 = (l2capinbuf[13] | ((uint16_t)(l2capinbuf[15] & 0x30) << 4));
+                                                IR_object_y1 = (l2capinbuf[14] | ((uint16_t)(l2capinbuf[15] & 0xC0) << 2));
+                                                IR_object_s1 = (l2capinbuf[15] & 0x0F);
+                                                 */
+                                                break;
+                                        case 0x35: // Core Buttons and Accelerometer with 16 Extension Bytes
+                                                // (a1) 35 BB BB AA AA AA EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE
+#if 1 // Set this to 0 if you don't want to use an extension, this reduceds the size of the library a lot!
+                                                if(motionPlusConnected) {
+                                                        if(l2capinbuf[20] & 0x02) { // Check if it's a report from the Motion controller or the extension
+                                                                if(motionValuesReset) { // We will only use the values when the gyro value has been set
+                                                                        gyroYawRaw = ((l2capinbuf[15] | ((l2capinbuf[18] & 0xFC) << 6)) - gyroYawZero);
+                                                                        gyroRollRaw = ((l2capinbuf[16] | ((l2capinbuf[19] & 0xFC) << 6)) - gyroRollZero);
+                                                                        gyroPitchRaw = ((l2capinbuf[17] | ((l2capinbuf[20] & 0xFC) << 6)) - gyroPitchZero);
+
+                                                                        yawGyroSpeed = (float)gyroYawRaw / ((float)gyroYawZero / yawGyroScale);
+                                                                        rollGyroSpeed = -(float)gyroRollRaw / ((float)gyroRollZero / rollGyroScale); // We invert these values so they will fit the acc values
+                                                                        pitchGyroSpeed = (float)gyroPitchRaw / ((float)gyroPitchZero / pitchGyroScale);
+
+                                                                        /* The onboard gyro has two ranges for slow and fast mode */
+                                                                        if(!(l2capinbuf[18] & 0x02)) // Check if fast mode is used
+                                                                                yawGyroSpeed *= 4.545;
+                                                                        if(!(l2capinbuf[18] & 0x01)) // Check if fast mode is used
+                                                                                pitchGyroSpeed *= 4.545;
+                                                                        if(!(l2capinbuf[19] & 0x02)) // Check if fast mode is used
+                                                                                rollGyroSpeed *= 4.545;
+
+                                                                        compPitch = (0.93f * (compPitch + (pitchGyroSpeed * (float)((uint32_t)micros() - timer) / 1000000.0f)))+(0.07f * getWiimotePitch()); // Use a complimentary filter to calculate the angle
+                                                                        compRoll = (0.93f * (compRoll + (rollGyroSpeed * (float)((uint32_t)micros() - timer) / 1000000.0f)))+(0.07f * getWiimoteRoll());
+
+                                                                        gyroYaw += (yawGyroSpeed * ((float)((uint32_t)micros() - timer) / 1000000.0f));
+                                                                        gyroRoll += (rollGyroSpeed * ((float)((uint32_t)micros() - timer) / 1000000.0f));
+                                                                        gyroPitch += (pitchGyroSpeed * ((float)((uint32_t)micros() - timer) / 1000000.0f));
+                                                                        timer = (uint32_t)micros();
+                                                                        /*
+                                                                        // Uncomment these lines to tune the gyro scale variabels
+                                                                        Notify(PSTR("\r\ngyroYaw: "), 0x80);
+                                                                        Notify(gyroYaw, 0x80);
+                                                                        Notify(PSTR("\tgyroRoll: "), 0x80);
+                                                                        Notify(gyroRoll, 0x80);
+                                                                        Notify(PSTR("\tgyroPitch: "), 0x80);
+                                                                        Notify(gyroPitch, 0x80);
+                                                                         */
+                                                                        /*
+                                                                        Notify(PSTR("\twiimoteRoll: "), 0x80);
+                                                                        Notify(wiimoteRoll, 0x80);
+                                                                        Notify(PSTR("\twiimotePitch: "), 0x80);
+                                                                        Notify(wiimotePitch, 0x80);
+                                                                         */
+                                                                } else {
+                                                                        if((int32_t)((uint32_t)micros() - timer) > 1000000) { // Loop for 1 sec before resetting the values
+#ifdef DEBUG_USB_HOST
+                                                                                Notify(PSTR("\r\nThe gyro values has been reset"), 0x80);
+#endif
+                                                                                gyroYawZero = (l2capinbuf[15] | ((l2capinbuf[18] & 0xFC) << 6));
+                                                                                gyroRollZero = (l2capinbuf[16] | ((l2capinbuf[19] & 0xFC) << 6));
+                                                                                gyroPitchZero = (l2capinbuf[17] | ((l2capinbuf[20] & 0xFC) << 6));
+
+                                                                                rollGyroScale = 500; // You might need to adjust these
+                                                                                pitchGyroScale = 400;
+                                                                                yawGyroScale = 415;
+
+                                                                                gyroYaw = 0;
+                                                                                gyroRoll = 0;
+                                                                                gyroPitch = 0;
+
+                                                                                motionValuesReset = true;
+                                                                                timer = (uint32_t)micros();
+                                                                        }
+                                                                }
+                                                        } else {
+                                                                if(nunchuckConnected) {
+                                                                        hatValues[HatX] = l2capinbuf[15];
+                                                                        hatValues[HatY] = l2capinbuf[16];
+                                                                        accXnunchuck = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x10 >> 3)) - 416;
+                                                                        accYnunchuck = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x20 >> 4)) - 416;
+                                                                        accZnunchuck = (((l2capinbuf[19] & 0xFE) << 2) | (l2capinbuf[20] & 0xC0 >> 5)) - 416;
+                                                                }
+                                                                //else if(classicControllerConnected) { }
+                                                        }
+                                                        if(l2capinbuf[19] & 0x01) {
+                                                                if(!extensionConnected) {
+                                                                        extensionConnected = true;
+                                                                        unknownExtensionConnected = true;
+#ifdef DEBUG_USB_HOST
+                                                                        Notify(PSTR("\r\nExtension connected to Motion Plus"), 0x80);
+#endif
+                                                                }
+                                                        } else {
+                                                                if(extensionConnected && !unknownExtensionConnected) {
+                                                                        extensionConnected = false;
+                                                                        unknownExtensionConnected = true;
+#ifdef DEBUG_USB_HOST
+                                                                        Notify(PSTR("\r\nExtension disconnected from Motion Plus"), 0x80);
+#endif
+                                                                        nunchuckConnected = false; // There is no extension connected to the Motion Plus if this report is sent
+                                                                }
+                                                        }
+
+                                                } else if(nunchuckConnected) {
+                                                        hatValues[HatX] = l2capinbuf[15];
+                                                        hatValues[HatY] = l2capinbuf[16];
+                                                        accXnunchuck = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x0C >> 2)) - 416;
+                                                        accYnunchuck = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x30 >> 4)) - 416;
+                                                        accZnunchuck = ((l2capinbuf[19] << 2) | (l2capinbuf[20] & 0xC0 >> 6)) - 416;
+                                                } else if(wiiUProControllerConnected) {
+                                                        hatValues[LeftHatX] = (l2capinbuf[15] | l2capinbuf[16] << 8);
+                                                        hatValues[RightHatX] = (l2capinbuf[17] | l2capinbuf[18] << 8);
+                                                        hatValues[LeftHatY] = (l2capinbuf[19] | l2capinbuf[20] << 8);
+                                                        hatValues[RightHatY] = (l2capinbuf[21] | l2capinbuf[22] << 8);
+                                                }
+#endif
+                                                break;
+#ifdef DEBUG_USB_HOST
+                                        default:
+                                                Notify(PSTR("\r\nUnknown Report type: "), 0x80);
+                                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);
+                                                break;
+#endif
+                                }
+                        }
+                }
+                L2CAP_task();
+        }
+}
+
+void WII::L2CAP_task() {
+        switch(l2cap_state) {
+                        /* These states are used if the Wiimote is the host */
+                case L2CAP_CONTROL_SUCCESS:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Control Successfully Configured"), 0x80);
+#endif
+                                l2cap_state = L2CAP_INTERRUPT_SETUP;
+                        }
+                        break;
+
+                case L2CAP_INTERRUPT_SETUP:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Interrupt Incoming Connection Request"), 0x80);
+#endif
+                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);
+                                delay(1);
+                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);
+                                identifier++;
+                                delay(1);
+                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
+
+                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
+                        }
+                        break;
+
+                        /* These states are used if the Arduino is the host */
+                case L2CAP_CONTROL_CONNECT_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONTROL_CONNECTED)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSend HID Control Config Request"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
+                                l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST;
+                        }
+                        break;
+
+                case L2CAP_CONTROL_CONFIG_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSend HID Interrupt Connection Request"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM);
+                                l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST;
+                        }
+                        break;
+
+                case L2CAP_INTERRUPT_CONNECT_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_INTERRUPT_CONNECTED)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSend HID Interrupt Config Request"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);
+                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;
+                        }
+                        break;
+
+                case L2CAP_INTERRUPT_CONFIG_REQUEST:
+                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Channels Established"), 0x80);
+#endif
+                                pBtd->connectToWii = false;
+                                pBtd->pairWithWii = false;
+                                stateCounter = 0;
+                                l2cap_state = WII_CHECK_MOTION_PLUS_STATE;
+                        }
+                        break;
+
+                        /* The next states are in run() */
+
+                case L2CAP_INTERRUPT_DISCONNECT:
+                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE) && ((int32_t)((uint32_t)millis() - timer) >= 0L)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected Interrupt Channel"), 0x80);
+#endif
+                                identifier++;
+                                pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);
+                                l2cap_state = L2CAP_CONTROL_DISCONNECT;
+                        }
+                        break;
+
+                case L2CAP_CONTROL_DISCONNECT:
+                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nDisconnected Control Channel"), 0x80);
+#endif
+                                pBtd->hci_disconnect(hci_handle);
+                                hci_handle = -1; // Reset handle
+                                l2cap_event_flag = 0; // Reset flags
+                                l2cap_state = L2CAP_WAIT;
+                        }
+                        break;
+        }
+}
+
+void WII::Run() {
+        if(l2cap_state == L2CAP_INTERRUPT_DISCONNECT && ((int32_t)((uint32_t)millis() - timer) >= 0L))
+                L2CAP_task(); // Call the rest of the disconnection routine after we have waited long enough
+
+        switch(l2cap_state) {
+                case L2CAP_WAIT:
+                        if(pBtd->connectToWii && !pBtd->l2capConnectionClaimed && !wiimoteConnected && !activeConnection) {
+                                pBtd->l2capConnectionClaimed = true;
+                                activeConnection = true;
+                                motionPlusInside = pBtd->motionPlusInside;
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nSend HID Control Connection Request"), 0x80);
+#endif
+                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection
+                                l2cap_event_flag = 0; // Reset flags
+                                identifier = 0;
+                                pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM);
+                                l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST;
+                        } else if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) {
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nHID Control Incoming Connection Request"), 0x80);
+#endif
+                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);
+                                delay(1);
+                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);
+                                identifier++;
+                                delay(1);
+                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);
+                                l2cap_state = L2CAP_CONTROL_SUCCESS;
+                        }
+                        break;
+
+                case WII_CHECK_MOTION_PLUS_STATE:
+#ifdef DEBUG_USB_HOST
+                        if(stateCounter == 0) // Only print onnce
+                                Notify(PSTR("\r\nChecking if a Motion Plus is connected"), 0x80);
+#endif
+                        stateCounter++;
+                        if(stateCounter % 200 == 0)
+                                checkMotionPresent(); // Check if there is a motion plus connected
+                        if(wii_check_flag(WII_FLAG_MOTION_PLUS_CONNECTED)) {
+                                stateCounter = 0;
+                                l2cap_state = WII_INIT_MOTION_PLUS_STATE;
+                                timer = (uint32_t)micros();
+
+                                if(unknownExtensionConnected) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nA extension is also connected"), 0x80);
+#endif
+                                        activateNunchuck = true; // For we will just set this to true as this the only extension supported so far
+                                }
+
+                        } else if(stateCounter == 601) { // We will try three times to check for the motion plus
+#ifdef DEBUG_USB_HOST
+                                Notify(PSTR("\r\nNo Motion Plus was detected"), 0x80);
+#endif
+                                stateCounter = 0;
+                                l2cap_state = WII_CHECK_EXTENSION_STATE;
+                        }
+                        break;
+
+                case WII_CHECK_EXTENSION_STATE: // This is used to check if there is anything plugged in to the extension port
+#ifdef DEBUG_USB_HOST
+                        if(stateCounter == 0) // Only print onnce
+                                Notify(PSTR("\r\nChecking if there is any extension connected"), 0x80);
+#endif
+                        stateCounter++; // We use this counter as there has to be a short delay between the commands
+                        if(stateCounter == 1)
+                                statusRequest(); // See if a new device has connected
+                        if(stateCounter == 100) {
+                                if(unknownExtensionConnected) // Check if there is a extension is connected to the port
+                                        initExtension1();
+                                else
+                                        stateCounter = 499;
+                        } else if(stateCounter == 200)
+                                initExtension2();
+                        else if(stateCounter == 300) {
+                                readExtensionType();
+                                unknownExtensionConnected = false;
+                        } else if(stateCounter == 400) {
+                                if(wii_check_flag(WII_FLAG_CALIBRATE_BALANCE_BOARD)) {
+#ifdef DEBUG_USB_HOST
+                                        Notify(PSTR("\r\nReading Wii Balance Board calibration values"), 0x80);
+#endif
+                                        readWiiBalanceBoardCalibration();
+                                } else
+                                        stateCounter = 499;
+                        } else if(stateCounter == 500) {
+                                stateCounter = 0;
+                                l2cap_state = TURN_ON_LED;
+                        }
+                        break;
+
+                case WII_INIT_MOTION_PLUS_STATE:
+                        stateCounter++;
+                        if(stateCounter == 1)
+                                initMotionPlus();
+                        else if(stateCounter == 100)
+                                activateMotionPlus();
+                        else if(stateCounter == 200)
+                                readExtensionType(); // Check if it has been activated
+                        else if(stateCounter == 300) {
+                                stateCounter = 0;
+                                unknownExtensionConnected = false; // The motion plus will send a status report when it's activated, we will set this to false so it doesn't reinitialize the Motion Plus
+                                l2cap_state = TURN_ON_LED;
+                        }
+                        break;
+
+                case TURN_ON_LED:
+                        if(wii_check_flag(WII_FLAG_NUNCHUCK_CONNECTED))
+                                nunchuckConnected = true;
+                        wiimoteConnected = true;
+                        onInit();
+                        l2cap_state = L2CAP_DONE;
+                        break;
+
+                case L2CAP_DONE:
+                        if(unknownExtensionConnected) {
+#ifdef DEBUG_USB_HOST
+                                if(stateCounter == 0) // Only print once
+                                        Notify(PSTR("\r\nChecking extension port"), 0x80);
+#endif
+                                stateCounter++; // We will use this counter as there has to be a short delay between the commands
+                                if(stateCounter == 50)
+                                        statusRequest();
+                                else if(stateCounter == 100)
+                                        initExtension1();
+                                else if(stateCounter == 150)
+                                        if((extensionConnected && motionPlusConnected) || (unknownExtensionConnected && !motionPlusConnected))
+                                                initExtension2();
+                                        else
+                                                stateCounter = 299; // There is no extension connected
+                                else if(stateCounter == 200)
+                                        readExtensionType();
+                                else if(stateCounter == 250) {
+                                        if(wii_check_flag(WII_FLAG_NUNCHUCK_CONNECTED)) {
+#ifdef DEBUG_USB_HOST
+                                                Notify(PSTR("\r\nNunchuck was reconnected"), 0x80);
+#endif
+                                                activateNunchuck = true;
+                                                nunchuckConnected = true;
+                                        }
+                                        if(!motionPlusConnected)
+                                                stateCounter = 449;
+                                } else if(stateCounter == 300) {
+                                        if(motionPlusConnected) {
+#ifdef DEBUG_USB_HOST
+                                                Notify(PSTR("\r\nReactivating the Motion Plus"), 0x80);
+#endif
+                                                initMotionPlus();
+                                        } else
+                                                stateCounter = 449;
+                                } else if(stateCounter == 350)
+                                        activateMotionPlus();
+                                else if(stateCounter == 400)
+                                        readExtensionType(); // Check if it has been activated
+                                else if(stateCounter == 450) {
+                                        onInit();
+                                        stateCounter = 0;
+                                        unknownExtensionConnected = false;
+                                }
+                        } else
+                                stateCounter = 0;
+                        break;
+        }
+}
+
+/************************************************************/
+/*                    HID Commands                          */
+/************************************************************/
+
+void WII::HID_Command(uint8_t* data, uint8_t nbytes) {
+        if(motionPlusInside)
+                pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]); // It's the new Wiimote with the Motion Plus Inside or Wii U Pro controller
+        else
+                pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]);
+}
+
+void WII::setAllOff() {
+        HIDBuffer[1] = 0x11;
+        HIDBuffer[2] = 0x00;
+        HID_Command(HIDBuffer, 3);
+}
+
+void WII::setRumbleOff() {
+        HIDBuffer[1] = 0x11;
+        HIDBuffer[2] &= ~0x01; // Bit 0 control the rumble
+        HID_Command(HIDBuffer, 3);
+}
+
+void WII::setRumbleOn() {
+        HIDBuffer[1] = 0x11;
+        HIDBuffer[2] |= 0x01; // Bit 0 control the rumble
+        HID_Command(HIDBuffer, 3);
+}
+
+void WII::setRumbleToggle() {
+        HIDBuffer[1] = 0x11;
+        HIDBuffer[2] ^= 0x01; // Bit 0 control the rumble
+        HID_Command(HIDBuffer, 3);
+}
+
+void WII::setLedRaw(uint8_t value) {
+        HIDBuffer[1] = 0x11;
+        HIDBuffer[2] = value | (HIDBuffer[2] & 0x01); // Keep the rumble bit
+        HID_Command(HIDBuffer, 3);
+}
+
+void WII::setLedOff(LEDEnum a) {
+        HIDBuffer[1] = 0x11;
+        HIDBuffer[2] &= ~(pgm_read_byte(&WII_LEDS[(uint8_t)a]));
+        HID_Command(HIDBuffer, 3);
+}
+
+void WII::setLedOn(LEDEnum a) {
+        if(a == OFF)
+                setLedRaw(0);
+        else {
+                HIDBuffer[1] = 0x11;
+                HIDBuffer[2] |= pgm_read_byte(&WII_LEDS[(uint8_t)a]);
+                HID_Command(HIDBuffer, 3);
+        }
+}
+
+void WII::setLedToggle(LEDEnum a) {
+        HIDBuffer[1] = 0x11;
+        HIDBuffer[2] ^= pgm_read_byte(&WII_LEDS[(uint8_t)a]);
+        HID_Command(HIDBuffer, 3);
+}
+
+void WII::setLedStatus() {
+        HIDBuffer[1] = 0x11;
+        HIDBuffer[2] = (HIDBuffer[2] & 0x01); // Keep the rumble bit
+        if(wiimoteConnected)
+                HIDBuffer[2] |= 0x10; // If it's connected LED1 will light up
+        if(motionPlusConnected)
+                HIDBuffer[2] |= 0x20; // If it's connected LED2 will light up
+        if(nunchuckConnected)
+                HIDBuffer[2] |= 0x40; // If it's connected LED3 will light up
+
+        HID_Command(HIDBuffer, 3);
+}
+
+uint8_t WII::getBatteryLevel() {
+        checkBatteryLevel = true; // This is needed so the library knows that the status response is a response to this function
+        statusRequest(); // This will update the battery level
+        return batteryLevel;
+};
+
+void WII::setReportMode(bool continuous, uint8_t mode) {
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nReport mode was changed to: "), 0x80);
+        D_PrintHex<uint8_t > (mode, 0x80);
+#endif
+        uint8_t cmd_buf[4];
+        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+        cmd_buf[1] = 0x12;
+        if(continuous)
+                cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit
+        else
+                cmd_buf[2] = 0x00 | (HIDBuffer[2] & 0x01); // Keep the rumble bit
+        cmd_buf[3] = mode;
+        HID_Command(cmd_buf, 4);
+}
+
+void WII::statusRequest() {
+        uint8_t cmd_buf[3];
+        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+        cmd_buf[1] = 0x15;
+        cmd_buf[2] = (HIDBuffer[2] & 0x01); // Keep the rumble bit
+        HID_Command(cmd_buf, 3);
+}
+
+/************************************************************/
+/*                    Memmory Commands                      */
+/************************************************************/
+
+void WII::writeData(uint32_t offset, uint8_t size, uint8_t* data) {
+        uint8_t cmd_buf[23];
+        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+        cmd_buf[1] = 0x16; // Write data
+        cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Write to memory, clear bit 2 to write to EEPROM
+        cmd_buf[3] = (uint8_t)((offset & 0xFF0000) >> 16);
+        cmd_buf[4] = (uint8_t)((offset & 0xFF00) >> 8);
+        cmd_buf[5] = (uint8_t)(offset & 0xFF);
+        cmd_buf[6] = size;
+        uint8_t i = 0;
+        for(; i < size; i++)
+                cmd_buf[7 + i] = data[i];
+        for(; i < 16; i++) // Set the rest to zero
+                cmd_buf[7 + i] = 0x00;
+        HID_Command(cmd_buf, 23);
+}
+
+void WII::initExtension1() {
+        uint8_t buf[1];
+        buf[0] = 0x55;
+        writeData(0xA400F0, 1, buf);
+}
+
+void WII::initExtension2() {
+        uint8_t buf[1];
+        buf[0] = 0x00;
+        writeData(0xA400FB, 1, buf);
+}
+
+void WII::initMotionPlus() {
+        uint8_t buf[1];
+        buf[0] = 0x55;
+        writeData(0xA600F0, 1, buf);
+}
+
+void WII::activateMotionPlus() {
+        uint8_t buf[1];
+        if(pBtd->wiiUProController) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nActivating Wii U Pro Controller"), 0x80);
+#endif
+                buf[0] = 0x00; // It seems like you can send anything but 0x04, 0x05, and 0x07
+        } else if(activateNunchuck) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nActivating Motion Plus in pass-through mode"), 0x80);
+#endif
+                buf[0] = 0x05; // Activate nunchuck pass-through mode
+        }//else if(classicControllerConnected && extensionConnected)
+                //buf[0] = 0x07;
+        else {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nActivating Motion Plus in normal mode"), 0x80);
+#endif
+                buf[0] = 0x04; // Don't use any extension
+        }
+        writeData(0xA600FE, 1, buf);
+}
+
+void WII::readData(uint32_t offset, uint16_t size, bool EEPROM) {
+        uint8_t cmd_buf[8];
+        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+        cmd_buf[1] = 0x17; // Read data
+        if(EEPROM)
+                cmd_buf[2] = 0x00 | (HIDBuffer[2] & 0x01); // Read from EEPROM
+        else
+                cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Read from memory
+        cmd_buf[3] = (uint8_t)((offset & 0xFF0000) >> 16);
+        cmd_buf[4] = (uint8_t)((offset & 0xFF00) >> 8);
+        cmd_buf[5] = (uint8_t)(offset & 0xFF);
+        cmd_buf[6] = (uint8_t)((size & 0xFF00) >> 8);
+        cmd_buf[7] = (uint8_t)(size & 0xFF);
+
+        HID_Command(cmd_buf, 8);
+}
+
+void WII::readExtensionType() {
+        readData(0xA400FA, 6, false);
+}
+
+void WII::readCalData() {
+        readData(0x0016, 8, true);
+}
+
+void WII::checkMotionPresent() {
+        readData(0xA600FA, 6, false);
+}
+
+void WII::readWiiBalanceBoardCalibration() {
+        readData(0xA40024, 24, false);
+}
+
+/************************************************************/
+/*                    WII Commands                          */
+/************************************************************/
+
+bool WII::getButtonPress(ButtonEnum b) { // Return true when a button is pressed
+        if(wiiUProControllerConnected)
+                return (ButtonState & pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[(uint8_t)b]));
+        else
+                return (ButtonState & pgm_read_dword(&WII_BUTTONS[(uint8_t)b]));
+}
+
+bool WII::getButtonClick(ButtonEnum b) { // Only return true when a button is clicked
+        uint32_t button;
+        if(wiiUProControllerConnected)
+                button = pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[(uint8_t)b]);
+        else
+                button = pgm_read_dword(&WII_BUTTONS[(uint8_t)b]);
+        bool click = (ButtonClickState & button);
+        ButtonClickState &= ~button; // clear "click" event
+        return click;
+}
+
+uint8_t WII::getAnalogHat(HatEnum a) {
+        if(!nunchuckConnected)
+                return 127; // Return center position
+        else {
+                uint8_t output = hatValues[(uint8_t)a];
+                if(output == 0xFF || output == 0x00) // The joystick will only read 255 or 0 when the cable is unplugged or initializing, so we will just return the center position
+                        return 127;
+                else
+                        return output;
+        }
+}
+
+uint16_t WII::getAnalogHat(AnalogHatEnum a) {
+        if(!wiiUProControllerConnected)
+                return 2000;
+        else {
+                uint16_t output = hatValues[(uint8_t)a];
+                if(output == 0x00) // The joystick will only read 0 when it is first initializing, so we will just return the center position
+                        return 2000;
+                else
+                        return output;
+        }
+}
+
+void WII::onInit() {
+        if(pFuncOnInit)
+                pFuncOnInit(); // Call the user function
+        else
+                setLedStatus();
+}
+
+/************************************************************/
+/*                 Wii Balance Board Commands               */
+/************************************************************/
+
+float WII::getWeight(BalanceBoardEnum pos) {
+        // Use interpolating between two points - based on: https://github.com/skorokithakis/gr8w8upd8m8/blob/master/gr8w8upd8m8.py
+        // wiiBalanceBoardCal[pos][0] is calibration values for 0 kg
+        // wiiBalanceBoardCal[pos][1] is calibration values for 17 kg
+        // wiiBalanceBoardCal[pos][2] is calibration values for 34 kg
+        if(wiiBalanceBoardRaw[pos] < wiiBalanceBoardCal[0][pos])
+            return 0.0f; // Below 0 kg
+        else if(wiiBalanceBoardRaw[pos] < wiiBalanceBoardCal[1][pos]) // Between 0 and 17 kg
+            return 17.0f * (float)(wiiBalanceBoardRaw[pos] - wiiBalanceBoardCal[0][pos]) / (float)(wiiBalanceBoardCal[1][pos] - wiiBalanceBoardCal[0][pos]);
+        else // More than 17 kg
+            return 17.0f + 17.0f * (float)(wiiBalanceBoardRaw[pos] - wiiBalanceBoardCal[1][pos]) / (float)(wiiBalanceBoardCal[2][pos] - wiiBalanceBoardCal[1][pos]);
+};
+
+float WII::getTotalWeight() {
+        return getWeight(TopRight) + getWeight(BotRight) + getWeight(TopLeft) + getWeight(BotLeft);
+};
+
+/************************************************************/
+/*       The following functions are for the IR camera      */
+/************************************************************/
+
+#ifdef WIICAMERA
+
+void WII::IRinitialize() { // Turns on and initialises the IR camera
+
+        enableIRCamera1();
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nEnable IR Camera1 Complete"), 0x80);
+#endif
+        delay(80);
+
+        enableIRCamera2();
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nEnable IR Camera2 Complete"), 0x80);
+#endif
+        delay(80);
+
+        write0x08Value();
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nWrote hex number 0x08"), 0x80);
+#endif
+        delay(80);
+
+        writeSensitivityBlock1();
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nWrote Sensitivity Block 1"), 0x80);
+#endif
+        delay(80);
+
+        writeSensitivityBlock2();
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nWrote Sensitivity Block 2"), 0x80);
+#endif
+        delay(80);
+
+        uint8_t mode_num = 0x03;
+        setWiiModeNumber(mode_num); // Change input for whatever mode you want i.e. 0x01, 0x03, or 0x05
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nSet Wii Mode Number To 0x"), 0x80);
+        D_PrintHex<uint8_t > (mode_num, 0x80);
+#endif
+        delay(80);
+
+        write0x08Value();
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nWrote Hex Number 0x08"), 0x80);
+#endif
+        delay(80);
+
+        setReportMode(false, 0x33);
+        //setReportMode(false, 0x3f); // For full reporting mode, doesn't work yet
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nSet Report Mode to 0x33"), 0x80);
+#endif
+        delay(80);
+
+        statusRequest(); // Used to update wiiState - call isIRCameraEnabled() afterwards to check if it actually worked
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nIR Initialized"), 0x80);
+#endif
+}
+
+void WII::enableIRCamera1() {
+        uint8_t cmd_buf[3];
+        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+        cmd_buf[1] = 0x13; // Output report 13
+        cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit and sets bit 2
+        HID_Command(cmd_buf, 3);
+}
+
+void WII::enableIRCamera2() {
+        uint8_t cmd_buf[3];
+        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)
+        cmd_buf[1] = 0x1A; // Output report 1A
+        cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit and sets bit 2
+        HID_Command(cmd_buf, 3);
+}
+
+void WII::writeSensitivityBlock1() {
+        uint8_t buf[9];
+        buf[0] = 0x00;
+        buf[1] = 0x00;
+        buf[2] = 0x00;
+        buf[3] = 0x00;
+        buf[4] = 0x00;
+        buf[5] = 0x00;
+        buf[6] = 0x90;
+        buf[7] = 0x00;
+        buf[8] = 0x41;
+
+        writeData(0xB00000, 9, buf);
+}
+
+void WII::writeSensitivityBlock2() {
+        uint8_t buf[2];
+        buf[0] = 0x40;
+        buf[1] = 0x00;
+
+        writeData(0xB0001A, 2, buf);
+}
+
+void WII::write0x08Value() {
+        uint8_t cmd = 0x08;
+        writeData(0xb00030, 1, &cmd);
+}
+
+void WII::setWiiModeNumber(uint8_t mode_number) { // mode_number in hex i.e. 0x03 for extended mode
+        writeData(0xb00033, 1, &mode_number);
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/Wii.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,519 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+
+ IR camera support added by Allan Glover (adglover9.81@gmail.com) and Kristian Lauszus
+ */
+
+#ifndef _wii_h_
+#define _wii_h_
+
+#include "BTD.h"
+#include "controllerEnums.h"
+
+/* Wii event flags */
+#define WII_FLAG_MOTION_PLUS_CONNECTED          (1 << 0)
+#define WII_FLAG_NUNCHUCK_CONNECTED             (1 << 1)
+#define WII_FLAG_CALIBRATE_BALANCE_BOARD        (1 << 2)
+
+#define wii_check_flag(flag)  (wii_event_flag & (flag))
+#define wii_set_flag(flag)  (wii_event_flag |= (flag))
+#define wii_clear_flag(flag)  (wii_event_flag &= ~(flag))
+
+/** Enum used to read the joystick on the Nunchuck. */
+enum HatEnum {
+        /** Read the x-axis on the Nunchuck joystick. */
+        HatX = 0,
+        /** Read the y-axis on the Nunchuck joystick. */
+        HatY = 1,
+};
+
+/** Enum used to read the weight on Wii Balance Board. */
+enum BalanceBoardEnum {
+        TopRight = 0,
+        BotRight = 1,
+        TopLeft = 2,
+        BotLeft = 3,
+};
+
+/**
+ * This BluetoothService class implements support for the Wiimote including the Nunchuck and Motion Plus extension.
+ *
+ * It also support the Wii U Pro Controller.
+ */
+class WII : public BluetoothService {
+public:
+        /**
+         * Constructor for the WII class.
+         * @param  p   Pointer to BTD class instance.
+         * @param  pair   Set this to true in order to pair with the Wiimote. If the argument is omitted then it won't pair with it.
+         * One can use ::PAIR to set it to true.
+         */
+        WII(BTD *p, bool pair = false);
+
+        /** @name BluetoothService implementation */
+        /** Used this to disconnect any of the controllers. */
+        void disconnect();
+        /**@}*/
+
+        /** @name Wii Controller functions */
+        /**
+         * getButtonPress(Button b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(Button b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(Button b),
+         * but if you need to drive a robot forward you would use getButtonPress(Button b).
+         * @param  b          ::ButtonEnum to read.
+         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.
+         */
+        bool getButtonPress(ButtonEnum b);
+        bool getButtonClick(ButtonEnum b);
+        /**@}*/
+
+        /** @name Wii Controller functions */
+
+        /** Call this to start the pairing sequence with a controller */
+        void pair(void) {
+                if(pBtd)
+                        pBtd->pairWithWiimote();
+        };
+        /**
+         * Used to read the joystick of the Nunchuck.
+         * @param  a Either ::HatX or ::HatY.
+         * @return   Return the analog value in the range from approximately 25-230.
+         */
+        uint8_t getAnalogHat(HatEnum a);
+        /**
+         * Used to read the joystick of the Wii U Pro Controller.
+         * @param  a Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.
+         * @return   Return the analog value in the range from approximately 800-3200.
+         */
+        uint16_t getAnalogHat(AnalogHatEnum a);
+
+        /**
+         * Pitch calculated from the Wiimote. A complimentary filter is used if the Motion Plus is connected.
+         * @return Pitch in the range from 0-360.
+         */
+        float getPitch() {
+                if(motionPlusConnected)
+                        return compPitch;
+                return getWiimotePitch();
+        };
+
+        /**
+         * Roll calculated from the Wiimote. A complimentary filter is used if the Motion Plus is connected.
+         * @return Roll in the range from 0-360.
+         */
+        float getRoll() {
+                if(motionPlusConnected)
+                        return compRoll;
+                return getWiimoteRoll();
+        };
+
+        /**
+         * This is the yaw calculated by the gyro.
+         *
+         * <B>NOTE:</B> This angle will drift a lot and is only available if the Motion Plus extension is connected.
+         * @return The angle calculated using the gyro.
+         */
+        float getYaw() {
+                return gyroYaw;
+        };
+
+        /** Used to set all LEDs and rumble off. */
+        void setAllOff();
+        /** Turn off rumble. */
+        void setRumbleOff();
+        /** Turn on rumble. */
+        void setRumbleOn();
+        /** Toggle rumble. */
+        void setRumbleToggle();
+
+        /**
+         * Set LED value without using the ::LEDEnum.
+         * @param value See: ::LEDEnum.
+         */
+        void setLedRaw(uint8_t value);
+
+        /** Turn all LEDs off. */
+        void setLedOff() {
+                setLedRaw(0);
+        };
+        /**
+         * Turn the specific ::LEDEnum off.
+         * @param a The ::LEDEnum to turn off.
+         */
+        void setLedOff(LEDEnum a);
+        /**
+         * Turn the specific ::LEDEnum on.
+         * @param a The ::LEDEnum to turn on.
+         */
+        void setLedOn(LEDEnum a);
+        /**
+         * Toggle the specific ::LEDEnum.
+         * @param a The ::LEDEnum to toggle.
+         */
+        void setLedToggle(LEDEnum a);
+        /**
+         * This will set the LEDs, so the user can see which connections are active.
+         *
+         * The first ::LEDEnum indicate that the Wiimote is connected,
+         * the second ::LEDEnum indicate indicate that a Motion Plus is also connected
+         * the third ::LEDEnum will indicate that a Nunchuck controller is also connected.
+         */
+        void setLedStatus();
+
+        /**
+         * Return the battery level of the Wiimote.
+         * @return The battery level in the range 0-255.
+         */
+        uint8_t getBatteryLevel();
+
+        /**
+         * Return the Wiimote state.
+         * @return See: http://wiibrew.org/wiki/Wiimote#0x20:_Status.
+         */
+        uint8_t getWiiState() {
+                return wiiState;
+        };
+        /**@}*/
+
+        /**@{*/
+        /** Variable used to indicate if a Wiimote is connected. */
+        bool wiimoteConnected;
+        /** Variable used to indicate if a Nunchuck controller is connected. */
+        bool nunchuckConnected;
+        /** Variable used to indicate if a Nunchuck controller is connected. */
+        bool motionPlusConnected;
+        /** Variable used to indicate if a Wii U Pro controller is connected. */
+        bool wiiUProControllerConnected;
+        /** Variable used to indicate if a Wii Balance Board is connected. */
+        bool wiiBalanceBoardConnected;
+        /**@}*/
+
+        /* IMU Data, might be usefull if you need to do something more advanced than just calculating the angle */
+
+        /**@{*/
+
+        /** Pitch and roll calculated from the accelerometer inside the Wiimote. */
+        float getWiimotePitch() {
+                return (atan2f(accYwiimote, accZwiimote) + PI) * RAD_TO_DEG;
+        };
+
+        float getWiimoteRoll() {
+                return (atan2f(accXwiimote, accZwiimote) + PI) * RAD_TO_DEG;
+        };
+        /**@}*/
+
+        /**@{*/
+
+        /** Pitch and roll calculated from the accelerometer inside the Nunchuck. */
+        float getNunchuckPitch() {
+                return (atan2f(accYnunchuck, accZnunchuck) + PI) * RAD_TO_DEG;
+        };
+
+        float getNunchuckRoll() {
+                return (atan2f(accXnunchuck, accZnunchuck) + PI) * RAD_TO_DEG;
+        };
+        /**@}*/
+
+        /**@{*/
+        /** Accelerometer values used to calculate pitch and roll. */
+        int16_t accXwiimote, accYwiimote, accZwiimote;
+        int16_t accXnunchuck, accYnunchuck, accZnunchuck;
+        /**@}*/
+
+        /* Variables for the gyro inside the Motion Plus */
+        /** This is the pitch calculated by the gyro - use this to tune WII#pitchGyroScale. */
+        float gyroPitch;
+        /** This is the roll calculated by the gyro - use this to tune WII#rollGyroScale. */
+        float gyroRoll;
+        /** This is the yaw calculated by the gyro - use this to tune WII#yawGyroScale. */
+        float gyroYaw;
+
+        /**@{*/
+        /** The speed in deg/s from the gyro. */
+        float pitchGyroSpeed;
+        float rollGyroSpeed;
+        float yawGyroSpeed;
+        /**@}*/
+
+        /**@{*/
+        /** You might need to fine-tune these values. */
+        uint16_t pitchGyroScale;
+        uint16_t rollGyroScale;
+        uint16_t yawGyroScale;
+        /**@}*/
+
+        /**@{*/
+        /** Raw value read directly from the Motion Plus. */
+        int16_t gyroYawRaw;
+        int16_t gyroRollRaw;
+        int16_t gyroPitchRaw;
+        /**@}*/
+
+        /**@{*/
+        /** These values are set when the controller is first initialized. */
+        int16_t gyroYawZero;
+        int16_t gyroRollZero;
+        int16_t gyroPitchZero;
+        /**@}*/
+
+        /** @name Wii Balance Board functions */
+
+        /**
+         * Used to get the weight at the specific position on the Wii Balance Board.
+         * @param  pos ::BalanceBoardEnum to read from.
+         * @return     Returns the weight in kg.
+         */
+        float getWeight(BalanceBoardEnum pos);
+
+        /**
+         * Used to get total weight on the Wii Balance Board.
+         * @return Returns the weight in kg.
+         */
+        float getTotalWeight();
+
+        /**
+         * Used to get the raw reading at the specific position on the Wii Balance Board.
+         * @param  pos ::BalanceBoardEnum to read from.
+         * @return     Returns the raw reading.
+         */
+        uint16_t getWeightRaw(BalanceBoardEnum pos) {
+                return wiiBalanceBoardRaw[pos];
+        };
+        /**@}*/
+
+#ifdef WIICAMERA
+        /** @name Wiimote IR camera functions
+         * You will have to set ::ENABLE_WII_IR_CAMERA in settings.h to 1 in order use the IR camera.
+         */
+        /** Initialises the camera as per the steps from: http://wiibrew.org/wiki/Wiimote#IR_Camera */
+        void IRinitialize();
+
+        /**
+         * IR object 1 x-position read from the Wii IR camera.
+         * @return The x-position of the object in the range 0-1023.
+         */
+        uint16_t getIRx1() {
+                return IR_object_x1;
+        };
+
+        /**
+         * IR object 1 y-position read from the Wii IR camera.
+         * @return The y-position of the object in the range 0-767.
+         */
+        uint16_t getIRy1() {
+                return IR_object_y1;
+        };
+
+        /**
+         * IR object 1 size read from the Wii IR camera.
+         * @return The size of the object in the range 0-15.
+         */
+        uint8_t getIRs1() {
+                return IR_object_s1;
+        };
+
+        /**
+         * IR object 2 x-position read from the Wii IR camera.
+         * @return The x-position of the object in the range 0-1023.
+         */
+        uint16_t getIRx2() {
+                return IR_object_x2;
+        };
+
+        /**
+         * IR object 2 y-position read from the Wii IR camera.
+         * @return The y-position of the object in the range 0-767.
+         */
+        uint16_t getIRy2() {
+                return IR_object_y2;
+        };
+
+        /**
+         * IR object 2 size read from the Wii IR camera.
+         * @return The size of the object in the range 0-15.
+         */
+        uint8_t getIRs2() {
+                return IR_object_s2;
+        };
+
+        /**
+         * IR object 3 x-position read from the Wii IR camera.
+         * @return The x-position of the object in the range 0-1023.
+         */
+        uint16_t getIRx3() {
+                return IR_object_x3;
+        };
+
+        /**
+         * IR object 3 y-position read from the Wii IR camera.
+         * @return The y-position of the object in the range 0-767.
+         */
+        uint16_t getIRy3() {
+                return IR_object_y3;
+        };
+
+        /**
+         * IR object 3 size read from the Wii IR camera.
+         * @return The size of the object in the range 0-15.
+         */
+        uint8_t getIRs3() {
+                return IR_object_s3;
+        };
+
+        /**
+         * IR object 4 x-position read from the Wii IR camera.
+         * @return The x-position of the object in the range 0-1023.
+         */
+        uint16_t getIRx4() {
+                return IR_object_x4;
+        };
+
+        /**
+         * IR object 4 y-position read from the Wii IR camera.
+         * @return The y-position of the object in the range 0-767.
+         */
+        uint16_t getIRy4() {
+                return IR_object_y4;
+        };
+
+        /**
+         * IR object 4 size read from the Wii IR camera.
+         * @return The size of the object in the range 0-15.
+         */
+        uint8_t getIRs4() {
+                return IR_object_s4;
+        };
+
+        /**
+         * Use this to check if the camera is enabled or not.
+         * If not call WII#IRinitialize to initialize the IR camera.
+         * @return     True if it's enabled, false if not.
+         */
+        bool isIRCameraEnabled() {
+                return (wiiState & 0x08);
+        };
+        /**@}*/
+#endif
+
+protected:
+        /** @name BluetoothService implementation */
+        /**
+         * Used to pass acldata to the services.
+         * @param ACLData Incoming acldata.
+         */
+        void ACLData(uint8_t* ACLData);
+        /** Used to run part of the state machine. */
+        void Run();
+        /** Use this to reset the service. */
+        void Reset();
+        /**
+         * Called when the controller is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        void onInit();
+        /**@}*/
+
+private:
+
+        void L2CAP_task(); // L2CAP state machine
+
+        /* Variables filled from HCI event management */
+        bool activeConnection; // Used to indicate if it's already has established a connection
+
+        /* Variables used by high level L2CAP task */
+        uint8_t l2cap_state;
+        uint8_t wii_event_flag; // Used for Wii flags
+
+        uint32_t ButtonState;
+        uint32_t OldButtonState;
+        uint32_t ButtonClickState;
+        uint16_t hatValues[4];
+
+        uint8_t HIDBuffer[3]; // Used to store HID commands
+
+        uint16_t stateCounter;
+        bool unknownExtensionConnected;
+        bool extensionConnected;
+        bool checkBatteryLevel; // Set to true when getBatteryLevel() is called otherwise if should be false
+        bool motionPlusInside; // True if it's a new Wiimote with the Motion Plus extension build into it
+
+        /* L2CAP Channels */
+        uint8_t control_scid[2]; // L2CAP source CID for HID_Control
+        uint8_t control_dcid[2]; // 0x0060
+        uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt
+        uint8_t interrupt_dcid[2]; // 0x0061
+
+        /* HID Commands */
+        void HID_Command(uint8_t* data, uint8_t nbytes);
+        void setReportMode(bool continuous, uint8_t mode);
+
+        void writeData(uint32_t offset, uint8_t size, uint8_t* data);
+        void initExtension1();
+        void initExtension2();
+
+        void statusRequest(); // Used to update the Wiimote state and battery level
+
+        void readData(uint32_t offset, uint16_t size, bool EEPROM);
+        void readExtensionType();
+        void readCalData();
+        void readWiiBalanceBoardCalibration(); // Used by the library to read the Wii Balance Board calibration values
+
+        void checkMotionPresent(); // Used to see if a Motion Plus is connected to the Wiimote
+        void initMotionPlus();
+        void activateMotionPlus();
+
+        uint16_t wiiBalanceBoardRaw[4]; // Wii Balance Board raw values
+        uint16_t wiiBalanceBoardCal[3][4]; // Wii Balance Board calibration values
+
+        float compPitch; // Fusioned angle using a complimentary filter if the Motion Plus is connected
+        float compRoll; // Fusioned angle using a complimentary filter if the Motion Plus is connected
+
+        bool activateNunchuck;
+        bool motionValuesReset; // This bool is true when the gyro values has been reset
+        uint32_t timer;
+
+        uint8_t wiiState; // Stores the value in l2capinbuf[12] - (0x01: Battery is nearly empty), (0x02:  An Extension Controller is connected), (0x04: Speaker enabled), (0x08: IR enabled), (0x10: LED1, 0x20: LED2, 0x40: LED3, 0x80: LED4)
+        uint8_t batteryLevel;
+
+#ifdef WIICAMERA
+        /* Private function and variables for the readings from the IR Camera */
+        void enableIRCamera1(); // Sets bit 2 of output report 13
+        void enableIRCamera2(); // Sets bit 2 of output report 1A
+        void writeSensitivityBlock1();
+        void writeSensitivityBlock2();
+        void write0x08Value();
+        void setWiiModeNumber(uint8_t mode_number);
+
+        uint16_t IR_object_x1; // IR x position 10 bits
+        uint16_t IR_object_y1; // IR y position 10 bits
+        uint8_t IR_object_s1; // IR size value
+        uint16_t IR_object_x2;
+        uint16_t IR_object_y2;
+        uint8_t IR_object_s2;
+        uint16_t IR_object_x3; // IR x position 10 bits
+        uint16_t IR_object_y3; // IR y position 10 bits
+        uint8_t IR_object_s3; // IR size value
+        uint16_t IR_object_x4;
+        uint16_t IR_object_y4;
+        uint8_t IR_object_s4;
+#endif
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/XBOXOLD.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,339 @@
+/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "XBOXOLD.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report send by the Xbox controller
+
+/** Buttons on the controllers */
+const uint8_t XBOXOLD_BUTTONS[] PROGMEM = {
+        0x01, // UP
+        0x08, // RIGHT
+        0x02, // DOWN
+        0x04, // LEFT
+
+        0x20, // BACK
+        0x10, // START
+        0x40, // L3
+        0x80, // R3
+
+        // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
+        4, // BLACK
+        5, // WHTIE
+        6, // L1
+        7, // R1
+
+        1, // B
+        0, // A
+        2, // X
+        3, // Y
+};
+
+XBOXOLD::XBOXOLD(USB *p) :
+pUsb(p), // pointer to USB class instance - mandatory
+bAddress(0), // device address - mandatory
+bPollEnable(false) { // don't start polling before dongle is connected
+        for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+
+        if(pUsb) // register in USB subsystem
+                pUsb->RegisterDeviceClass(this); //set devConfig[] entry
+}
+
+uint8_t XBOXOLD::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint16_t PID;
+        uint16_t VID;
+
+        // get memory address of USB device address pool
+        AddressPool &addrPool = pUsb->GetAddressPool();
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
+#endif
+        // check if address has already been assigned to an instance
+        if(bAddress) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress in use"), 0x80);
+#endif
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        if(!p->epinfo) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nepinfo is null"), 0x80);
+#endif
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        VID = udd->idVendor;
+        PID = udd->idProduct;
+
+        if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_OLD_PID1 && PID != XBOX_OLD_PID2 && PID != XBOX_OLD_PID3 && PID != XBOX_OLD_PID4)) // Check if VID and PID match
+                goto FailUnknownDevice;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nsetAddr: "), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+#endif
+                return rcode;
+        }
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nAddr: "), 0x80);
+        D_PrintHex<uint8_t > (bAddress, 0x80);
+#endif
+        //delay(300); // Spec says you should wait at least 200ms
+
+        p->lowspeed = false;
+
+        //get pointer to assigned address record
+        p = addrPool.GetUsbDevicePtr(bAddress);
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        // Assign epInfo to epinfo pointer - only EP0 is known
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        /* The application will work in reduced host mode, so we can save program and data
+           memory space. After verifying the VID we will use known values for the
+           configuration values for device, interface, endpoints and HID for the XBOX controllers */
+
+        /* Initialize data structures for endpoints of device */
+        epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX report endpoint
+        epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
+        epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX output endpoint
+        epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
+
+        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        delay(200); // Give time for address change
+
+        rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
+        if(rcode)
+                goto FailSetConfDescr;
+
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox Controller Connected\r\n"), 0x80);
+#endif
+        if(pFuncOnInit)
+                pFuncOnInit(); // Call the user function
+        XboxConnected = true;
+        bPollEnable = true;
+        return 0; // Successful configuration
+
+        /* Diagnostic messages */
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+#endif
+        goto Fail;
+
+FailUnknownDevice:
+#ifdef DEBUG_USB_HOST
+        NotifyFailUnknownDevice(VID, PID);
+#endif
+        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+Fail:
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox Init Failed, error code: "), 0x80);
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+/* Performs a cleanup after failed Init() attempt */
+uint8_t XBOXOLD::Release() {
+        XboxConnected = false;
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+        bAddress = 0;
+        bPollEnable = false;
+        return 0;
+}
+
+uint8_t XBOXOLD::Poll() {
+        if(!bPollEnable)
+                return 0;
+        uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
+        pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
+        readReport();
+#ifdef PRINTREPORT
+        printReport(BUFFER_SIZE); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
+#endif
+        return 0;
+}
+
+void XBOXOLD::readReport() {
+        ButtonState = readBuf[2];
+
+        for(uint8_t i = 0; i < sizeof (buttonValues); i++)
+                buttonValues[i] = readBuf[i + 4]; // A, B, X, Y, BLACK, WHITE, L1, and R1
+
+        hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[12] << 8) | readBuf[13]);
+        hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[14] << 8) | readBuf[15]);
+        hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[16] << 8) | readBuf[17]);
+        hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[18] << 8) | readBuf[19]);
+
+        //Notify(PSTR("\r\nButtonState"), 0x80);
+        //PrintHex<uint8_t>(ButtonState, 0x80);
+
+        if(ButtonState != OldButtonState || memcmp(buttonValues, oldButtonValues, sizeof (buttonValues)) != 0) {
+                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
+                OldButtonState = ButtonState;
+
+                for(uint8_t i = 0; i < sizeof (buttonValues); i++) {
+                        if(oldButtonValues[i] == 0 && buttonValues[i] != 0)
+                                buttonClicked[i] = true; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state
+                        oldButtonValues[i] = buttonValues[i];
+                }
+        }
+}
+
+void XBOXOLD::printReport(uint16_t length __attribute__((unused))) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox controller
+#ifdef PRINTREPORT
+        if(readBuf == NULL)
+                return;
+        for(uint8_t i = 0; i < length; i++) {
+                D_PrintHex<uint8_t > (readBuf[i], 0x80);
+                Notify(PSTR(" "), 0x80);
+        }
+        Notify(PSTR("\r\n"), 0x80);
+#endif
+}
+
+uint8_t XBOXOLD::getButtonPress(ButtonEnum b) {
+        uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);
+        if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
+                return buttonValues[button]; // Analog buttons
+        return (ButtonState & button); // Digital buttons
+}
+
+bool XBOXOLD::getButtonClick(ButtonEnum b) {
+        uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);
+        if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) { // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons
+                if(buttonClicked[button]) {
+                        buttonClicked[button] = false;
+                        return true;
+                }
+                return false;
+        }
+
+        bool click = (ButtonClickState & button);
+        ButtonClickState &= ~button; // clear "click" event
+        return click;
+}
+
+int16_t XBOXOLD::getAnalogHat(AnalogHatEnum a) {
+        return hatValue[a];
+}
+
+/* Xbox Controller commands */
+void XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {
+        //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)
+        pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
+}
+
+void XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) {
+        uint8_t writeBuf[6];
+
+        writeBuf[0] = 0x00;
+        writeBuf[1] = 0x06;
+        writeBuf[2] = 0x00;
+        writeBuf[3] = rValue; // small weight
+        writeBuf[4] = 0x00;
+        writeBuf[5] = lValue; // big weight
+
+        XboxCommand(writeBuf, 6);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/XBOXOLD.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,186 @@
+/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _xboxold_h_
+#define _xboxold_h_
+
+#include "Usb.h"
+#include "usbhid.h"
+#include "controllerEnums.h"
+
+/* Data Xbox taken from descriptors */
+#define EP_MAXPKTSIZE       32 // Max size for data via USB
+
+/* Names we give to the 3 Xbox pipes */
+#define XBOX_CONTROL_PIPE    0
+#define XBOX_INPUT_PIPE      1
+#define XBOX_OUTPUT_PIPE     2
+
+// PID and VID of the different devices
+#define XBOX_VID                                0x045E // Microsoft Corporation
+#define MADCATZ_VID                             0x1BAD // For unofficial Mad Catz controllers
+#define JOYTECH_VID                             0x162E // For unofficial Joytech controllers
+
+#define XBOX_OLD_PID1                           0x0202 // Original Microsoft Xbox controller (US)
+#define XBOX_OLD_PID2                           0x0285 // Original Microsoft Xbox controller (Japan)
+#define XBOX_OLD_PID3                           0x0287 // Microsoft Microsoft Xbox Controller S
+#define XBOX_OLD_PID4                           0x0289 // Smaller Microsoft Xbox controller (US)
+
+#define XBOX_MAX_ENDPOINTS   3
+
+/** This class implements support for a the original Xbox controller via USB. */
+class XBOXOLD : public USBDeviceConfig {
+public:
+        /**
+         * Constructor for the XBOXOLD class.
+         * @param  pUsb   Pointer to USB class instance.
+         */
+        XBOXOLD(USB *pUsb);
+
+        /** @name USBDeviceConfig implementation */
+        /**
+         * Initialize the Xbox Controller.
+         * @param  parent   Hub number.
+         * @param  port     Port number on the hub.
+         * @param  lowspeed Speed of the device.
+         * @return          0 on success.
+         */
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        /**
+         * Release the USB device.
+         * @return 0 on success.
+         */
+        uint8_t Release();
+        /**
+         * Poll the USB Input endpoins and run the state machines.
+         * @return 0 on success.
+         */
+        uint8_t Poll();
+
+        /**
+         * Get the device address.
+         * @return The device address.
+         */
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        /**
+         * Used to check if the controller has been initialized.
+         * @return True if it's ready.
+         */
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID) && (pid == XBOX_OLD_PID1 || pid == XBOX_OLD_PID2 || pid == XBOX_OLD_PID3 || pid == XBOX_OLD_PID4));
+        };
+        /**@}*/
+
+        /** @name Xbox Controller functions */
+        /**
+         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(ButtonEnum b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
+         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
+         * @param  b          ::ButtonEnum to read.
+         * @return            getButtonClick(ButtonEnum b) will return a bool, while getButtonPress(ButtonEnum b) will return a byte if reading ::L2 or ::R2.
+         */
+        uint8_t getButtonPress(ButtonEnum b);
+        bool getButtonClick(ButtonEnum b);
+        /**@}*/
+
+        /** @name Xbox Controller functions */
+        /**
+         * Return the analog value from the joysticks on the controller.
+         * @param  a          Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.
+         * @return            Returns a signed 16-bit integer.
+         */
+        int16_t getAnalogHat(AnalogHatEnum a);
+
+        /** Turn rumble off the controller. */
+        void setRumbleOff() {
+                setRumbleOn(0, 0);
+        };
+        /**
+         * Turn rumble on.
+         * @param lValue     Left motor (big weight) inside the controller.
+         * @param rValue     Right motor (small weight) inside the controller.
+         */
+        void setRumbleOn(uint8_t lValue, uint8_t rValue);
+
+        /**
+         * Used to call your own function when the controller is successfully initialized.
+         * @param funcOnInit Function to call.
+         */
+        void attachOnInit(void (*funcOnInit)(void)) {
+                pFuncOnInit = funcOnInit;
+        };
+        /**@}*/
+
+        /** True if a Xbox controller is connected. */
+        bool XboxConnected;
+
+protected:
+        /** Pointer to USB class instance. */
+        USB *pUsb;
+        /** Device address. */
+        uint8_t bAddress;
+        /** Endpoint info structure. */
+        EpInfo epInfo[XBOX_MAX_ENDPOINTS];
+
+private:
+        /**
+         * Called when the controller is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        void (*pFuncOnInit)(void); // Pointer to function called in onInit()
+
+        bool bPollEnable;
+
+        /* Variables to store the digital buttons */
+        uint8_t ButtonState;
+        uint8_t OldButtonState;
+        uint8_t ButtonClickState;
+
+        /* Variables to store the analog buttons */
+        uint8_t buttonValues[8]; // A, B, X, Y, BLACK, WHITE, L1, and R1
+        uint8_t oldButtonValues[8];
+        bool buttonClicked[8];
+
+        int16_t hatValue[4]; // Joystick values
+
+        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data
+
+        void readReport(); // Read incoming data
+        void printReport(uint16_t length); // Print incoming date
+
+        /* Private commands */
+        void XboxCommand(uint8_t* data, uint16_t nbytes);
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/XBOXONE.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,493 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+   Copyright (C) 2015 guruthree
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+
+ guruthree
+ Web      :  https://github.com/guruthree/
+ */
+
+#include "XBOXONE.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report send by the Xbox ONE Controller
+
+XBOXONE::XBOXONE(USB *p) :
+pUsb(p), // pointer to USB class instance - mandatory
+bAddress(0), // device address - mandatory
+bNumEP(1), // If config descriptor needs to be parsed
+qNextPollTime(0), // Reset NextPollTime
+pollInterval(0),
+bPollEnable(false) { // don't start polling before dongle is connected
+        for(uint8_t i = 0; i < XBOX_ONE_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+
+        if(pUsb) // register in USB subsystem
+                pUsb->RegisterDeviceClass(this); //set devConfig[] entry
+}
+
+uint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint16_t PID, VID;
+        uint8_t num_of_conf; // Number of configurations
+
+        // get memory address of USB device address pool
+        AddressPool &addrPool = pUsb->GetAddressPool();
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nXBOXONE Init"), 0x80);
+#endif
+        // check if address has already been assigned to an instance
+        if(bAddress) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress in use"), 0x80);
+#endif
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        if(!p->epinfo) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nepinfo is null"), 0x80);
+#endif
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        VID = udd->idVendor;
+        PID = udd->idProduct;
+
+        if(!VIDPIDOK(VID, PID)) // Check VID
+                goto FailUnknownDevice;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nsetAddr: "), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+#endif
+                return rcode;
+        }
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nAddr: "), 0x80);
+        D_PrintHex<uint8_t > (bAddress, 0x80);
+#endif
+        //delay(300); // Spec says you should wait at least 200ms
+
+        p->lowspeed = false;
+
+        //get pointer to assigned address record
+        p = addrPool.GetUsbDevicePtr(bAddress);
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        // Assign epInfo to epinfo pointer - only EP0 is known
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        num_of_conf = udd->bNumConfigurations; // Number of configurations
+
+        USBTRACE2("NC:", num_of_conf);
+
+        // Check if attached device is a Xbox One controller and fill endpoint data structure
+        for(uint8_t i = 0; i < num_of_conf; i++) {
+                ConfigDescParser<0, 0, 0, 0> confDescrParser(this); // Allow all devices, as we have already verified that it is a Xbox One controller from the VID and PID
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+                if(rcode) // Check error code
+                        goto FailGetConfDescr;
+                if(bNumEP >= XBOX_ONE_MAX_ENDPOINTS) // All endpoints extracted
+                        break;
+        }
+
+        if(bNumEP < XBOX_ONE_MAX_ENDPOINTS)
+                goto FailUnknownDevice;
+
+        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        delay(200); // Give time for address change
+
+        rcode = pUsb->setConf(bAddress, epInfo[ XBOX_ONE_CONTROL_PIPE ].epAddr, bConfNum);
+        if(rcode)
+                goto FailSetConfDescr;
+
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox One Controller Connected\r\n"), 0x80);
+#endif
+
+        delay(200); // let things settle
+
+        // Initialize the controller for input
+        cmdCounter = 0; // Reset the counter used when sending out the commands
+        uint8_t writeBuf[5];
+        writeBuf[0] = 0x05;
+        writeBuf[1] = 0x20;
+        // Byte 2 is set in "XboxCommand"
+        writeBuf[3] = 0x01;
+        writeBuf[4] = 0x00;
+        rcode = XboxCommand(writeBuf, 5);
+        if (rcode)
+                goto Fail;
+
+        onInit();
+        XboxOneConnected = true;
+        bPollEnable = true;
+        return 0; // Successful configuration
+
+        /* Diagnostic messages */
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+#endif
+        goto Fail;
+
+FailUnknownDevice:
+#ifdef DEBUG_USB_HOST
+        NotifyFailUnknownDevice(VID, PID);
+#endif
+        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+Fail:
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox One Init Failed, error code: "), 0x80);
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+/* Extracts endpoint information from config descriptor */
+void XBOXONE::EndpointXtract(uint8_t conf,
+        uint8_t iface __attribute__((unused)),
+        uint8_t alt __attribute__((unused)),
+        uint8_t proto __attribute__((unused)),
+        const USB_ENDPOINT_DESCRIPTOR *pep)
+{
+        
+    bConfNum = conf;
+        uint8_t index;
+
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT) { // Interrupt endpoint
+                index = (pep->bEndpointAddress & 0x80) == 0x80 ? XBOX_ONE_INPUT_PIPE : XBOX_ONE_OUTPUT_PIPE; // Set the endpoint index
+        } else
+                return;
+
+        // Fill the rest of endpoint data structure
+        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+#ifdef EXTRADEBUG
+        PrintEndpointDescriptor(pep);
+#endif
+        if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints
+                pollInterval = pep->bInterval;
+        bNumEP++;
+}
+
+void XBOXONE::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr
+    __attribute__((unused)))
+{
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nEndpoint descriptor:"), 0x80);
+        Notify(PSTR("\r\nLength:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
+        Notify(PSTR("\r\nType:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
+        Notify(PSTR("\r\nAddress:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
+        Notify(PSTR("\r\nAttributes:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
+        Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
+        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
+        Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
+#endif
+}
+
+/* Performs a cleanup after failed Init() attempt */
+uint8_t XBOXONE::Release() {
+        XboxOneConnected = false;
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+        bAddress = 0; // Clear device address
+        bNumEP = 1; // Must have to be reset to 1
+        qNextPollTime = 0; // Reset next poll time
+        pollInterval = 0;
+        bPollEnable = false;
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox One Controller Disconnected\r\n"), 0x80);
+#endif
+        return 0;
+}
+
+uint8_t XBOXONE::Poll() {
+        uint8_t rcode = 0;
+
+        if(!bPollEnable)
+                return 0;
+
+        if((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) { // Do not poll if shorter than polling interval
+                qNextPollTime = (uint32_t)millis() + pollInterval; // Set new poll time
+                uint16_t length =  (uint16_t)epInfo[ XBOX_ONE_INPUT_PIPE ].maxPktSize; // Read the maximum packet size from the endpoint
+                uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_ONE_INPUT_PIPE ].epAddr, &length, readBuf, pollInterval);
+                if(!rcode) {
+                        readReport();
+#ifdef PRINTREPORT // Uncomment "#define PRINTREPORT" to print the report send by the Xbox ONE Controller
+                        for(uint8_t i = 0; i < length; i++) {
+                                D_PrintHex<uint8_t > (readBuf[i], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                        }
+                        Notify(PSTR("\r\n"), 0x80);
+#endif
+                }
+#ifdef DEBUG_USB_HOST
+                else if(rcode != hrNAK) { // Not a matter of no update to send
+                        Notify(PSTR("\r\nXbox One Poll Failed, error code: "), 0x80);
+                        NotifyFail(rcode);
+                }
+#endif
+    }
+    return rcode;
+}
+
+void XBOXONE::readReport() {
+        if(readBuf[0] == 0x07) {
+                // The XBOX button has a separate message
+                if(readBuf[4] == 1)
+                        ButtonState |= pgm_read_word(&XBOX_BUTTONS[XBOX]);
+                else
+                        ButtonState &= ~pgm_read_word(&XBOX_BUTTONS[XBOX]);
+
+                if(ButtonState != OldButtonState) {
+                    ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
+                    OldButtonState = ButtonState;
+                }
+        }
+        if(readBuf[0] != 0x20) { // Check if it's the correct report, otherwise return - the controller also sends different status reports
+#ifdef EXTRADEBUG
+                Notify(PSTR("\r\nXbox Poll: "), 0x80);
+                D_PrintHex<uint8_t > (readBuf[0], 0x80); // 0x03 is a heart beat report!
+#endif
+                return;
+        }
+
+        uint16_t xbox = ButtonState & pgm_read_word(&XBOX_BUTTONS[XBOX]); // Since the XBOX button is separate, save it and add it back in
+        // xbox button from before, dpad, abxy, start/back, sync, stick click, shoulder buttons
+        ButtonState = xbox | (((uint16_t)readBuf[5] & 0xF) << 8) | (readBuf[4] & 0xF0)  | (((uint16_t)readBuf[4] & 0x0C) << 10) | ((readBuf[4] & 0x01) << 3) | (((uint16_t)readBuf[5] & 0xC0) << 8) | ((readBuf[5] & 0x30) >> 4);
+
+        triggerValue[0] = (uint16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
+        triggerValue[1] = (uint16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
+
+        hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
+        hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
+        hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
+        hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
+
+        //Notify(PSTR("\r\nButtonState"), 0x80);
+        //PrintHex<uint16_t>(ButtonState, 0x80);
+
+        if(ButtonState != OldButtonState) {
+                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
+                OldButtonState = ButtonState;
+        }
+
+        // Handle click detection for triggers
+        if(triggerValue[0] != 0 && triggerValueOld[0] == 0)
+                L2Clicked = true;
+        triggerValueOld[0] = triggerValue[0];
+        if(triggerValue[1] != 0 && triggerValueOld[1] == 0)
+                R2Clicked = true;
+        triggerValueOld[1] = triggerValue[1];
+}
+
+uint16_t XBOXONE::getButtonPress(ButtonEnum b) {
+        if(b == L2) // These are analog buttons
+                return triggerValue[0];
+        else if(b == R2)
+                return triggerValue[1];
+        return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b])));
+}
+
+bool XBOXONE::getButtonClick(ButtonEnum b) {
+        if(b == L2) {
+                if(L2Clicked) {
+                        L2Clicked = false;
+                        return true;
+                }
+                return false;
+        } else if(b == R2) {
+                if(R2Clicked) {
+                        R2Clicked = false;
+                        return true;
+                }
+                return false;
+        }
+        uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
+        bool click = (ButtonClickState & button);
+        ButtonClickState &= ~button; // Clear "click" event
+        return click;
+}
+
+int16_t XBOXONE::getAnalogHat(AnalogHatEnum a) {
+        return hatValue[a];
+}
+
+/* Xbox Controller commands */
+uint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) {
+        data[2] = cmdCounter++; // Increment the output command counter
+        uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_ONE_OUTPUT_PIPE ].epAddr, nbytes, data);
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXboxCommand, Return: "), 0x80);
+        D_PrintHex<uint8_t > (rcode, 0x80);
+#endif
+        return rcode;
+}
+
+// The Xbox One packets are described at: https://github.com/quantus/xbox-one-controller-protocol
+void XBOXONE::onInit() {
+        // A short buzz to show the controller is active
+        uint8_t writeBuf[13];
+
+        // Activate rumble
+        writeBuf[0] = 0x09;
+        writeBuf[1] = 0x00;
+        // Byte 2 is set in "XboxCommand"
+
+        // Single rumble effect
+        writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has)
+        writeBuf[4] = 0x00; // Mode
+        writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R)
+        writeBuf[6] = 0x04; // lT force
+        writeBuf[7] = 0x04; // rT force
+        writeBuf[8] = 0x20; // L force
+        writeBuf[9] = 0x20; // R force
+        writeBuf[10] = 0x80; // Length of pulse
+        writeBuf[11] = 0x00; // Off period
+        writeBuf[12] = 0x00; // Repeat count
+        XboxCommand(writeBuf, 13);
+
+        if(pFuncOnInit)
+                pFuncOnInit(); // Call the user function
+}
+
+void XBOXONE::setRumbleOff() {
+        uint8_t writeBuf[13];
+
+        // Activate rumble
+        writeBuf[0] = 0x09;
+        writeBuf[1] = 0x00;
+        // Byte 2 is set in "XboxCommand"
+
+        // Continuous rumble effect
+        writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has)
+        writeBuf[4] = 0x00; // Mode
+        writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R)
+        writeBuf[6] = 0x00; // lT force
+        writeBuf[7] = 0x00; // rT force
+        writeBuf[8] = 0x00; // L force
+        writeBuf[9] = 0x00; // R force
+        writeBuf[10] = 0x00; // On period
+        writeBuf[11] = 0x00; // Off period
+        writeBuf[12] = 0x00; // Repeat count
+        XboxCommand(writeBuf, 13);
+}
+
+void XBOXONE::setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor) {
+        uint8_t writeBuf[13];
+
+        // Activate rumble
+        writeBuf[0] = 0x09;
+        writeBuf[1] = 0x00;
+        // Byte 2 is set in "XboxCommand"
+
+        // Continuous rumble effect
+        writeBuf[3] = 0x09; // Substructure (what substructure rest of this packet has)
+        writeBuf[4] = 0x00; // Mode
+        writeBuf[5] = 0x0F; // Rumble mask (what motors are activated) (0000 lT rT L R)
+        writeBuf[6] = leftTrigger; // lT force
+        writeBuf[7] = rightTrigger; // rT force
+        writeBuf[8] = leftMotor; // L force
+        writeBuf[9] = rightMotor; // R force
+        writeBuf[10] = 0xFF; // On period
+        writeBuf[11] = 0x00; // Off period
+        writeBuf[12] = 0xFF; // Repeat count
+        XboxCommand(writeBuf, 13);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/XBOXONE.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,241 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+   Copyright (C) 2015 guruthree
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+
+ guruthree
+ Web      :  https://github.com/guruthree/
+ */
+
+
+#ifndef _xboxone_h_
+#define _xboxone_h_
+
+#include "Usb.h"
+#include "xboxEnums.h"
+
+/* Xbox One data taken from descriptors */
+#define XBOX_ONE_EP_MAXPKTSIZE                  64 // Max size for data via USB
+
+/* Names we give to the 3 XboxONE pipes */
+#define XBOX_ONE_CONTROL_PIPE                   0
+#define XBOX_ONE_OUTPUT_PIPE                    1
+#define XBOX_ONE_INPUT_PIPE                     2
+
+#define XBOX_ONE_MAX_ENDPOINTS                  3
+
+// PID and VID of the different versions of the controller - see: https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c
+
+// Official controllers
+#define XBOX_VID1                               0x045E // Microsoft Corporation
+#define XBOX_ONE_PID1                           0x02D1 // Microsoft X-Box One pad
+#define XBOX_ONE_PID2                           0x02DD // Microsoft X-Box One pad (Firmware 2015)
+#define XBOX_ONE_PID3                           0x02E3 // Microsoft X-Box One Elite pad
+#define XBOX_ONE_PID4                           0x02EA // Microsoft X-Box One S pad
+#define XBOX_ONE_PID13                          0x0B0A // Microsoft X-Box One Adaptive Controller
+
+// Unofficial controllers
+#define XBOX_VID2                               0x0738 // Mad Catz
+#define XBOX_VID3                               0x0E6F // Afterglow
+#define XBOX_VID4                               0x0F0D // HORIPAD ONE
+#define XBOX_VID5                               0x1532 // Razer
+#define XBOX_VID6                               0x24C6 // PowerA
+
+#define XBOX_ONE_PID5                           0x4A01 // Mad Catz FightStick TE 2 - might have different mapping for triggers?
+#define XBOX_ONE_PID6                           0x0139 // Afterglow Prismatic Wired Controller
+#define XBOX_ONE_PID7                           0x0146 // Rock Candy Wired Controller for Xbox One
+#define XBOX_ONE_PID8                           0x0067 // HORIPAD ONE
+#define XBOX_ONE_PID9                           0x0A03 // Razer Wildcat
+#define XBOX_ONE_PID10                          0x541A // PowerA Xbox One Mini Wired Controller
+#define XBOX_ONE_PID11                          0x542A // Xbox ONE spectra
+#define XBOX_ONE_PID12                          0x543A // PowerA Xbox One wired controller
+
+/** This class implements support for a Xbox ONE controller connected via USB. */
+class XBOXONE : public USBDeviceConfig, public UsbConfigXtracter {
+public:
+        /**
+         * Constructor for the XBOXONE class.
+         * @param  pUsb   Pointer to USB class instance.
+         */
+        XBOXONE(USB *pUsb);
+
+        /** @name USBDeviceConfig implementation */
+        /**
+         * Initialize the Xbox Controller.
+         * @param  parent   Hub number.
+         * @param  port     Port number on the hub.
+         * @param  lowspeed Speed of the device.
+         * @return          0 on success.
+         */
+        virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        /**
+         * Release the USB device.
+         * @return 0 on success.
+         */
+        virtual uint8_t Release();
+        /**
+         * Poll the USB Input endpoins and run the state machines.
+         * @return 0 on success.
+         */
+        virtual uint8_t Poll();
+
+        /**
+         * Get the device address.
+         * @return The device address.
+         */
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        /**
+         * Used to check if the controller has been initialized.
+         * @return True if it's ready.
+         */
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        /**
+         * Read the poll interval taken from the endpoint descriptors.
+         * @return The poll interval in ms.
+         */
+        uint8_t readPollInterval() {
+                return pollInterval;
+        };
+
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return ((vid == XBOX_VID1 || vid == XBOX_VID2 || vid == XBOX_VID3 || vid == XBOX_VID4 || vid == XBOX_VID5 || vid == XBOX_VID6) &&
+                    (pid == XBOX_ONE_PID1 || pid == XBOX_ONE_PID2 || pid == XBOX_ONE_PID3 || pid == XBOX_ONE_PID4 ||
+                        pid == XBOX_ONE_PID5 || pid == XBOX_ONE_PID6 || pid == XBOX_ONE_PID7 || pid == XBOX_ONE_PID8 ||
+                        pid == XBOX_ONE_PID9 || pid == XBOX_ONE_PID10 || pid == XBOX_ONE_PID11 || pid == XBOX_ONE_PID12 || pid == XBOX_ONE_PID13));
+        };
+        /**@}*/
+
+        /** @name Xbox Controller functions */
+        /**
+         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(ButtonEnum b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
+         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
+         * @param  b          ::ButtonEnum to read.
+         * @return            getButtonClick(ButtonEnum b) will return a bool, while getButtonPress(ButtonEnum b) will return a word if reading ::L2 or ::R2.
+         */
+        uint16_t getButtonPress(ButtonEnum b);
+        bool getButtonClick(ButtonEnum b);
+
+        /**
+         * Return the analog value from the joysticks on the controller.
+         * @param  a          Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.
+         * @return            Returns a signed 16-bit integer.
+         */
+        int16_t getAnalogHat(AnalogHatEnum a);
+
+        /**
+         * Used to call your own function when the controller is successfully initialized.
+         * @param funcOnInit Function to call.
+         */
+        void attachOnInit(void (*funcOnInit)(void)) {
+                pFuncOnInit = funcOnInit;
+        };
+
+        /** Used to set the rumble off. */
+        void setRumbleOff();
+
+        /**
+         * Used to turn on rumble continuously.
+         * @param leftTrigger  Left trigger force.
+         * @param rightTrigger Right trigger force.
+         * @param leftMotor    Left motor force.
+         * @param rightMotor   Right motor force.
+         */
+        void setRumbleOn(uint8_t leftTrigger, uint8_t rightTrigger, uint8_t leftMotor, uint8_t rightMotor);
+        /**@}*/
+
+        /** True if a Xbox ONE controller is connected. */
+        bool XboxOneConnected;
+
+protected:
+        /** Pointer to USB class instance. */
+        USB *pUsb;
+        /** Device address. */
+        uint8_t bAddress;
+        /** Endpoint info structure. */
+        EpInfo epInfo[XBOX_ONE_MAX_ENDPOINTS];
+
+        /** Configuration number. */
+        uint8_t bConfNum;
+        /** Total number of endpoints in the configuration. */
+        uint8_t bNumEP;
+        /** Next poll time based on poll interval taken from the USB descriptor. */
+        uint32_t qNextPollTime;
+
+        /** @name UsbConfigXtracter implementation */
+        /**
+         * UsbConfigXtracter implementation, used to extract endpoint information.
+         * @param conf  Configuration value.
+         * @param iface Interface number.
+         * @param alt   Alternate setting.
+         * @param proto Interface Protocol.
+         * @param ep    Endpoint Descriptor.
+         */
+        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+        /**@}*/
+
+        /**
+         * Used to print the USB Endpoint Descriptor.
+         * @param ep_ptr Pointer to USB Endpoint Descriptor.
+         */
+        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
+
+private:
+        /**
+         * Called when the controller is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         */
+        void onInit();
+        void (*pFuncOnInit)(void); // Pointer to function called in onInit()
+
+        uint8_t pollInterval;
+        bool bPollEnable;
+
+        /* Variables to store the buttons */
+        uint16_t ButtonState;
+        uint16_t OldButtonState;
+        uint16_t ButtonClickState;
+        int16_t hatValue[4];
+        uint16_t triggerValue[2];
+        uint16_t triggerValueOld[2];
+
+        bool L2Clicked; // These buttons are analog, so we use we use these bools to check if they where clicked or not
+        bool R2Clicked;
+
+        uint8_t readBuf[XBOX_ONE_EP_MAXPKTSIZE]; // General purpose buffer for input data
+        uint8_t cmdCounter;
+
+        void readReport(); // Used to read the incoming data
+
+        /* Private commands */
+        uint8_t XboxCommand(uint8_t* data, uint16_t nbytes);
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/XBOXRECV.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,585 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+
+ getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net
+ */
+
+#include "XBOXRECV.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller
+
+XBOXRECV::XBOXRECV(USB *p) :
+pUsb(p), // pointer to USB class instance - mandatory
+bAddress(0), // device address - mandatory
+bPollEnable(false) { // don't start polling before dongle is connected
+        for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+
+        if(pUsb) // register in USB subsystem
+                pUsb->RegisterDeviceClass(this); //set devConfig[] entry
+}
+
+uint8_t XBOXRECV::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint16_t PID, VID;
+
+        AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nXBOXRECV Init"), 0x80);
+#endif
+
+        if(bAddress) { // Check if address has already been assigned to an instance
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress in use"), 0x80);
+#endif
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+
+        p = addrPool.GetUsbDevicePtr(0); // Get pointer to pseudo device with address 0 assigned
+
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        if(!p->epinfo) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nepinfo is null"), 0x80);
+#endif
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        oldep_ptr = p->epinfo; // Save old pointer to EP_RECORD of address 0
+        p->epinfo = epInfo; // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->lowspeed = lowspeed;
+
+        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
+
+        p->epinfo = oldep_ptr; // Restore p->epinfo
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        VID = udd->idVendor;
+        PID = udd->idProduct;
+
+        if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_WIRELESS_RECEIVER_PID && PID != XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)) { // Check if it's a Xbox receiver using the Vendor ID and Product ID
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nYou'll need a wireless receiver for this libary to work"), 0x80);
+#endif
+                goto FailUnknownDevice;
+        }
+
+        bAddress = addrPool.AllocAddress(parent, false, port); // Allocate new address according to device class
+
+        if(!bAddress) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nOut of address space"), 0x80);
+#endif
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+        }
+
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0; // Extract Max Packet Size from device descriptor
+
+        delay(20); // Wait a little before resetting device
+
+        return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;
+
+        /* Diagnostic messages */
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr(rcode);
+#endif
+        if(rcode != hrJERR)
+                rcode = USB_ERROR_FailGetDevDescr;
+        goto Fail;
+
+FailUnknownDevice:
+#ifdef DEBUG_USB_HOST
+        NotifyFailUnknownDevice(VID, PID);
+#endif
+        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+Fail:
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+};
+
+uint8_t XBOXRECV::Init(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed) {
+        uint8_t rcode;
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nBTD Init"), 0x80);
+#endif
+        UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record
+
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        delay(300); // Assign new address to the device
+
+        rcode = pUsb->setAddr(0, 0, bAddress); // Assign new address to the device
+        if(rcode) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nsetAddr: "), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+#endif
+                p->lowspeed = false;
+                goto Fail;
+        }
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nAddr: "), 0x80);
+        D_PrintHex<uint8_t > (bAddress, 0x80);
+#endif
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        p->lowspeed = lowspeed;
+
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); // Assign epInfo to epinfo pointer - only EP0 is known
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        /* The application will work in reduced host mode, so we can save program and data
+           memory space. After verifying the VID we will use known values for the
+           configuration values for device, interface, endpoints and HID for the XBOX360 Wireless receiver */
+
+        /* Initialize data structures for endpoints of device */
+        epInfo[ XBOX_INPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 report endpoint - poll interval 1ms
+        epInfo[ XBOX_INPUT_PIPE_1 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_INPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_INPUT_PIPE_1 ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_INPUT_PIPE_1 ].bmSndToggle = 0;
+        epInfo[ XBOX_INPUT_PIPE_1 ].bmRcvToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 output endpoint - poll interval 8ms
+        epInfo[ XBOX_OUTPUT_PIPE_1 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_OUTPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_OUTPUT_PIPE_1 ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_OUTPUT_PIPE_1 ].bmSndToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE_1 ].bmRcvToggle = 0;
+
+        epInfo[ XBOX_INPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 report endpoint - poll interval 1ms
+        epInfo[ XBOX_INPUT_PIPE_2 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_INPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_INPUT_PIPE_2 ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_INPUT_PIPE_2 ].bmSndToggle = 0;
+        epInfo[ XBOX_INPUT_PIPE_2 ].bmRcvToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 output endpoint - poll interval 8ms
+        epInfo[ XBOX_OUTPUT_PIPE_2 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_OUTPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_OUTPUT_PIPE_2 ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_OUTPUT_PIPE_2 ].bmSndToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE_2 ].bmRcvToggle = 0;
+
+        epInfo[ XBOX_INPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 report endpoint - poll interval 1ms
+        epInfo[ XBOX_INPUT_PIPE_3 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_INPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_INPUT_PIPE_3 ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_INPUT_PIPE_3 ].bmSndToggle = 0;
+        epInfo[ XBOX_INPUT_PIPE_3 ].bmRcvToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 output endpoint - poll interval 8ms
+        epInfo[ XBOX_OUTPUT_PIPE_3 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_OUTPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_OUTPUT_PIPE_3 ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_OUTPUT_PIPE_3 ].bmSndToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE_3 ].bmRcvToggle = 0;
+
+        epInfo[ XBOX_INPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 report endpoint - poll interval 1ms
+        epInfo[ XBOX_INPUT_PIPE_4 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_INPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_INPUT_PIPE_4 ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_INPUT_PIPE_4 ].bmSndToggle = 0;
+        epInfo[ XBOX_INPUT_PIPE_4 ].bmRcvToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 output endpoint - poll interval 8ms
+        epInfo[ XBOX_OUTPUT_PIPE_4 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_OUTPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_OUTPUT_PIPE_4 ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_OUTPUT_PIPE_4 ].bmSndToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE_4 ].bmRcvToggle = 0;
+
+        rcode = pUsb->setEpInfoEntry(bAddress, 9, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        delay(200); //Give time for address change
+
+        rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
+        if(rcode)
+                goto FailSetConfDescr;
+
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox Wireless Receiver Connected\r\n"), 0x80);
+#endif
+        XboxReceiverConnected = true;
+        bPollEnable = true;
+        checkStatusTimer = 0; // Reset timer
+        return 0; // Successful configuration
+
+        /* Diagnostic messages */
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+#endif
+
+Fail:
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+/* Performs a cleanup after failed Init() attempt */
+uint8_t XBOXRECV::Release() {
+        XboxReceiverConnected = false;
+        for(uint8_t i = 0; i < 4; i++)
+                Xbox360Connected[i] = 0x00;
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+        bAddress = 0;
+        bPollEnable = false;
+        return 0;
+}
+
+uint8_t XBOXRECV::Poll() {
+        if(!bPollEnable)
+                return 0;
+        if(!checkStatusTimer || ((int32_t)((uint32_t)millis() - checkStatusTimer) > 3000)) { // Run checkStatus every 3 seconds
+                checkStatusTimer = (uint32_t)millis();
+                checkStatus();
+        }
+
+        uint8_t inputPipe;
+        uint16_t bufferSize;
+        for(uint8_t i = 0; i < 4; i++) {
+                if(i == 0)
+                        inputPipe = XBOX_INPUT_PIPE_1;
+                else if(i == 1)
+                        inputPipe = XBOX_INPUT_PIPE_2;
+                else if(i == 2)
+                        inputPipe = XBOX_INPUT_PIPE_3;
+                else
+                        inputPipe = XBOX_INPUT_PIPE_4;
+
+                bufferSize = EP_MAXPKTSIZE; // This is the maximum number of bytes we want to receive
+                pUsb->inTransfer(bAddress, epInfo[ inputPipe ].epAddr, &bufferSize, readBuf);
+                if(bufferSize > 0) { // The number of received bytes
+#ifdef EXTRADEBUG
+                        Notify(PSTR("Bytes Received: "), 0x80);
+                        D_PrintHex<uint16_t > (bufferSize, 0x80);
+                        Notify(PSTR("\r\n"), 0x80);
+#endif
+                        readReport(i);
+#ifdef PRINTREPORT
+                        printReport(i, bufferSize); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
+#endif
+                }
+        }
+        return 0;
+}
+
+void XBOXRECV::readReport(uint8_t controller) {
+        if(readBuf == NULL)
+                return;
+        // This report is send when a controller is connected and disconnected
+        if(readBuf[0] == 0x08 && readBuf[1] != Xbox360Connected[controller]) {
+                Xbox360Connected[controller] = readBuf[1];
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("Controller "), 0x80);
+                Notify(controller, 0x80);
+#endif
+                if(Xbox360Connected[controller]) {
+#ifdef DEBUG_USB_HOST
+                        const char* str = 0;
+                        switch(readBuf[1]) {
+                                case 0x80: str = PSTR(" as controller\r\n");
+                                        break;
+                                case 0x40: str = PSTR(" as headset\r\n");
+                                        break;
+                                case 0xC0: str = PSTR(" as controller+headset\r\n");
+                                        break;
+                        }
+                        Notify(PSTR(": connected"), 0x80);
+                        Notify(str, 0x80);
+#endif
+                        onInit(controller);
+                }
+#ifdef DEBUG_USB_HOST
+                else
+                        Notify(PSTR(": disconnected\r\n"), 0x80);
+#endif
+                return;
+        }
+        // Controller status report
+        if(readBuf[1] == 0x00 && readBuf[3] & 0x13 && readBuf[4] >= 0x22) {
+                controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4];
+                return;
+        }
+        if(readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports
+                return;
+
+        // A controller must be connected if it's sending data
+        if(!Xbox360Connected[controller])
+                Xbox360Connected[controller] |= 0x80;
+
+        ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24));
+
+        hatValue[controller][LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
+        hatValue[controller][LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
+        hatValue[controller][RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);
+        hatValue[controller][RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);
+
+        //Notify(PSTR("\r\nButtonState: "), 0x80);
+        //PrintHex<uint32_t>(ButtonState[controller], 0x80);
+
+        if(ButtonState[controller] != OldButtonState[controller]) {
+                buttonStateChanged[controller] = true;
+                ButtonClickState[controller] = (ButtonState[controller] >> 16) & ((~OldButtonState[controller]) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
+                if(((uint8_t)OldButtonState[controller]) == 0 && ((uint8_t)ButtonState[controller]) != 0) // The L2 and R2 buttons are special as they are analog buttons
+                        R2Clicked[controller] = true;
+                if((uint8_t)(OldButtonState[controller] >> 8) == 0 && (uint8_t)(ButtonState[controller] >> 8) != 0)
+                        L2Clicked[controller] = true;
+                OldButtonState[controller] = ButtonState[controller];
+        }
+}
+
+void XBOXRECV::printReport(uint8_t controller __attribute__((unused)), uint8_t nBytes __attribute__((unused))) { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
+#ifdef PRINTREPORT
+        if(readBuf == NULL)
+                return;
+        Notify(PSTR("Controller "), 0x80);
+        Notify(controller, 0x80);
+        Notify(PSTR(": "), 0x80);
+        for(uint8_t i = 0; i < nBytes; i++) {
+                D_PrintHex<uint8_t > (readBuf[i], 0x80);
+                Notify(PSTR(" "), 0x80);
+        }
+        Notify(PSTR("\r\n"), 0x80);
+#endif
+}
+
+uint8_t XBOXRECV::getButtonPress(ButtonEnum b, uint8_t controller) {
+        if(b == L2) // These are analog buttons
+                return (uint8_t)(ButtonState[controller] >> 8);
+        else if(b == R2)
+                return (uint8_t)ButtonState[controller];
+        return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));
+}
+
+bool XBOXRECV::getButtonClick(ButtonEnum b, uint8_t controller) {
+        if(b == L2) {
+                if(L2Clicked[controller]) {
+                        L2Clicked[controller] = false;
+                        return true;
+                }
+                return false;
+        } else if(b == R2) {
+                if(R2Clicked[controller]) {
+                        R2Clicked[controller] = false;
+                        return true;
+                }
+                return false;
+        }
+        uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
+        bool click = (ButtonClickState[controller] & button);
+        ButtonClickState[controller] &= ~button; // clear "click" event
+        return click;
+}
+
+int16_t XBOXRECV::getAnalogHat(AnalogHatEnum a, uint8_t controller) {
+        return hatValue[controller][a];
+}
+
+bool XBOXRECV::buttonChanged(uint8_t controller) {
+        bool state = buttonStateChanged[controller];
+        buttonStateChanged[controller] = false;
+        return state;
+}
+
+/*
+ControllerStatus Breakdown
+ControllerStatus[controller] & 0x0001   // 0
+ControllerStatus[controller] & 0x0002   // normal batteries, no rechargeable battery pack
+ControllerStatus[controller] & 0x0004   // controller starting up / settling
+ControllerStatus[controller] & 0x0008   // headset adapter plugged in, but no headphones connected (mute?)
+ControllerStatus[controller] & 0x0010   // 0
+ControllerStatus[controller] & 0x0020   // 1
+ControllerStatus[controller] & 0x0040   // battery level (high bit)
+ControllerStatus[controller] & 0x0080   // battery level (low bit)
+ControllerStatus[controller] & 0x0100   // 1
+ControllerStatus[controller] & 0x0200   // 1
+ControllerStatus[controller] & 0x0400   // headset adapter plugged in
+ControllerStatus[controller] & 0x0800   // 0
+ControllerStatus[controller] & 0x1000   // 1
+ControllerStatus[controller] & 0x2000   // 0
+ControllerStatus[controller] & 0x4000   // 0
+ControllerStatus[controller] & 0x8000   // 0
+ */
+uint8_t XBOXRECV::getBatteryLevel(uint8_t controller) {
+        return ((controllerStatus[controller] & 0x00C0) >> 6);
+}
+
+void XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) {
+#ifdef EXTRADEBUG
+        uint8_t rcode;
+#endif
+        uint8_t outputPipe;
+        switch(controller) {
+                case 0: outputPipe = XBOX_OUTPUT_PIPE_1;
+                        break;
+                case 1: outputPipe = XBOX_OUTPUT_PIPE_2;
+                        break;
+                case 2: outputPipe = XBOX_OUTPUT_PIPE_3;
+                        break;
+                case 3: outputPipe = XBOX_OUTPUT_PIPE_4;
+                        break;
+                default:
+                        return;
+        }
+#ifdef EXTRADEBUG
+        rcode =
+#endif
+                pUsb->outTransfer(bAddress, epInfo[ outputPipe ].epAddr, nbytes, data);
+#ifdef EXTRADEBUG
+        if(rcode)
+                Notify(PSTR("Error sending Xbox message\r\n"), 0x80);
+#endif
+}
+
+void XBOXRECV::disconnect(uint8_t controller) {
+        writeBuf[0] = 0x00;
+        writeBuf[1] = 0x00;
+        writeBuf[2] = 0x08;
+        writeBuf[3] = 0xC0;
+
+        XboxCommand(controller, writeBuf, 4);
+}
+
+void XBOXRECV::setLedRaw(uint8_t value, uint8_t controller) {
+        writeBuf[0] = 0x00;
+        writeBuf[1] = 0x00;
+        writeBuf[2] = 0x08;
+        writeBuf[3] = value | 0x40;
+
+        XboxCommand(controller, writeBuf, 4);
+}
+
+void XBOXRECV::setLedOn(LEDEnum led, uint8_t controller) {
+        if(led == OFF)
+                setLedRaw(0, controller);
+        else if(led != ALL) // All LEDs can't be on a the same time
+                setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4, controller);
+}
+
+void XBOXRECV::setLedBlink(LEDEnum led, uint8_t controller) {
+        setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]), controller);
+}
+
+void XBOXRECV::setLedMode(LEDModeEnum ledMode, uint8_t controller) { // This function is used to do some speciel LED stuff the controller supports
+        setLedRaw((uint8_t)ledMode, controller);
+}
+
+/* PC runs this at interval of approx 2 seconds
+Thanks to BusHound from Perisoft.net for the Windows USB Analysis output
+Found by timstamp.co.uk
+ */
+void XBOXRECV::checkStatus() {
+        if(!bPollEnable)
+                return;
+        // Get controller info
+        writeBuf[0] = 0x08;
+        writeBuf[1] = 0x00;
+        writeBuf[2] = 0x0f;
+        writeBuf[3] = 0xc0;
+        for(uint8_t i = 0; i < 4; i++) {
+                XboxCommand(i, writeBuf, 4);
+        }
+        // Get battery status
+        writeBuf[0] = 0x00;
+        writeBuf[1] = 0x00;
+        writeBuf[2] = 0x00;
+        writeBuf[3] = 0x40;
+        for(uint8_t i = 0; i < 4; i++) {
+                if(Xbox360Connected[i])
+                        XboxCommand(i, writeBuf, 4);
+        }
+}
+
+void XBOXRECV::setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller) {
+        writeBuf[0] = 0x00;
+        writeBuf[1] = 0x01;
+        writeBuf[2] = 0x0f;
+        writeBuf[3] = 0xc0;
+        writeBuf[4] = 0x00;
+        writeBuf[5] = lValue; // big weight
+        writeBuf[6] = rValue; // small weight
+
+        XboxCommand(controller, writeBuf, 7);
+}
+
+void XBOXRECV::onInit(uint8_t controller) {
+        if(pFuncOnInit)
+                pFuncOnInit(); // Call the user function
+        else {
+                LEDEnum led;
+                if(controller == 0)
+                        led = static_cast<LEDEnum>(CONTROLLER_LED1);
+                else if(controller == 1)
+                        led = static_cast<LEDEnum>(CONTROLLER_LED2);
+                else if(controller == 2)
+                        led = static_cast<LEDEnum>(CONTROLLER_LED3);
+                else
+                        led = static_cast<LEDEnum>(CONTROLLER_LED4);
+                setLedOn(led, controller);
+        }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/XBOXRECV.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,277 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+
+ getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net
+ */
+
+#ifndef _xboxrecv_h_
+#define _xboxrecv_h_
+
+#include "Usb.h"
+#include "xboxEnums.h"
+
+/* Data Xbox 360 taken from descriptors */
+#define EP_MAXPKTSIZE       32 // max size for data via USB
+
+/* Names we give to the 9 Xbox360 pipes */
+#define XBOX_CONTROL_PIPE   0
+#define XBOX_INPUT_PIPE_1   1
+#define XBOX_OUTPUT_PIPE_1  2
+#define XBOX_INPUT_PIPE_2   3
+#define XBOX_OUTPUT_PIPE_2  4
+#define XBOX_INPUT_PIPE_3   5
+#define XBOX_OUTPUT_PIPE_3  6
+#define XBOX_INPUT_PIPE_4   7
+#define XBOX_OUTPUT_PIPE_4  8
+
+// PID and VID of the different devices
+#define XBOX_VID                                0x045E  // Microsoft Corporation
+#define MADCATZ_VID                             0x1BAD  // For unofficial Mad Catz receivers
+#define JOYTECH_VID                             0x162E  // For unofficial Joytech controllers
+
+#define XBOX_WIRELESS_RECEIVER_PID              0x0719  // Microsoft Wireless Gaming Receiver
+#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID  0x0291  // Third party Wireless Gaming Receiver
+
+#define XBOX_MAX_ENDPOINTS   9
+
+/**
+ * This class implements support for a Xbox Wireless receiver.
+ *
+ * Up to four controllers can connect to one receiver, if more is needed one can use a second receiver via the USBHub class.
+ */
+class XBOXRECV : public USBDeviceConfig {
+public:
+        /**
+         * Constructor for the XBOXRECV class.
+         * @param  pUsb   Pointer to USB class instance.
+         */
+        XBOXRECV(USB *pUsb);
+
+        /** @name USBDeviceConfig implementation */
+        /**
+         * Address assignment and basic initilization is done here.
+         * @param  parent   Hub number.
+         * @param  port     Port number on the hub.
+         * @param  lowspeed Speed of the device.
+         * @return          0 on success.
+         */
+        uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);
+        /**
+         * Initialize the Xbox wireless receiver.
+         * @param  parent   Hub number.
+         * @param  port     Port number on the hub.
+         * @param  lowspeed Speed of the device.
+         * @return          0 on success.
+         */
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        /**
+         * Release the USB device.
+         * @return 0 on success.
+         */
+        uint8_t Release();
+        /**
+         * Poll the USB Input endpoins and run the state machines.
+         * @return 0 on success.
+         */
+        uint8_t Poll();
+
+        /**
+         * Get the device address.
+         * @return The device address.
+         */
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        /**
+         * Used to check if the controller has been initialized.
+         * @return True if it's ready.
+         */
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID) && (pid == XBOX_WIRELESS_RECEIVER_PID || pid == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID));
+        };
+        /**@}*/
+
+        /** @name Xbox Controller functions */
+        /**
+         * getButtonPress(uint8_t controller, ButtonEnum b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(uint8_t controller, ButtonEnum b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(uint8_t controller, ButtonEnum b),
+         * but if you need to drive a robot forward you would use getButtonPress(uint8_t controller, ButtonEnum b).
+         * @param  b          ::ButtonEnum to read.
+         * @param  controller The controller to read from. Default to 0.
+         * @return            getButtonClick(uint8_t controller, ButtonEnum b) will return a bool, while getButtonPress(uint8_t controller, ButtonEnum b) will return a byte if reading ::L2 or ::R2.
+         */
+        uint8_t getButtonPress(ButtonEnum b, uint8_t controller = 0);
+        bool getButtonClick(ButtonEnum b, uint8_t controller = 0);
+        /**@}*/
+
+        /** @name Xbox Controller functions */
+        /**
+         * Return the analog value from the joysticks on the controller.
+         * @param  a          Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.
+         * @param  controller The controller to read from. Default to 0.
+         * @return            Returns a signed 16-bit integer.
+         */
+        int16_t getAnalogHat(AnalogHatEnum a, uint8_t controller = 0);
+
+        /**
+         * Used to disconnect any of the controllers.
+         * @param controller The controller to disconnect. Default to 0.
+         */
+        void disconnect(uint8_t controller = 0);
+
+        /**
+         * Turn rumble off and all the LEDs on the specific controller.
+         * @param  controller The controller to write to. Default to 0.
+         */
+        void setAllOff(uint8_t controller = 0) {
+                setRumbleOn(0, 0, controller);
+                setLedOff(controller);
+        };
+
+        /**
+         * Turn rumble off the specific controller.
+         * @param  controller The controller to write to. Default to 0.
+         */
+        void setRumbleOff(uint8_t controller = 0) {
+                setRumbleOn(0, 0, controller);
+        };
+        /**
+         * Turn rumble on.
+         * @param lValue     Left motor (big weight) inside the controller.
+         * @param rValue     Right motor (small weight) inside the controller.
+         * @param controller The controller to write to. Default to 0.
+         */
+        void setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller = 0);
+        /**
+         * Set LED value. Without using the ::LEDEnum or ::LEDModeEnum.
+         * @param value      See:
+         * setLedOff(uint8_t controller), setLedOn(uint8_t controller, LED l),
+         * setLedBlink(uint8_t controller, LED l), and setLedMode(uint8_t controller, LEDMode lm).
+         * @param controller The controller to write to. Default to 0.
+         */
+        void setLedRaw(uint8_t value, uint8_t controller = 0);
+
+        /**
+         * Turn all LEDs off the specific controller.
+         * @param controller The controller to write to. Default to 0.
+         */
+        void setLedOff(uint8_t controller = 0) {
+                setLedRaw(0, controller);
+        };
+        /**
+         * Turn on a LED by using ::LEDEnum.
+         * @param l          ::OFF, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller.
+         * @param controller The controller to write to. Default to 0.
+         */
+        void setLedOn(LEDEnum l, uint8_t controller = 0);
+        /**
+         * Turn on a LED by using ::LEDEnum.
+         * @param l          ::ALL, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller.
+         * @param controller The controller to write to. Default to 0.
+         */
+        void setLedBlink(LEDEnum l, uint8_t controller = 0);
+        /**
+         * Used to set special LED modes supported by the Xbox controller.
+         * @param lm         See ::LEDModeEnum.
+         * @param controller The controller to write to. Default to 0.
+         */
+        void setLedMode(LEDModeEnum lm, uint8_t controller = 0);
+        /**
+         * Used to get the battery level from the controller.
+         * @param  controller The controller to read from. Default to 0.
+         * @return            Returns the battery level as an integer in the range of 0-3.
+         */
+        uint8_t getBatteryLevel(uint8_t controller = 0);
+        /**
+         * Used to check if a button has changed.
+         * @param  controller The controller to read from. Default to 0.
+         * @return            True if a button has changed.
+         */
+        bool buttonChanged(uint8_t controller = 0);
+
+        /**
+         * Used to call your own function when the controller is successfully initialized.
+         * @param funcOnInit Function to call.
+         */
+        void attachOnInit(void (*funcOnInit)(void)) {
+                pFuncOnInit = funcOnInit;
+        };
+        /**@}*/
+
+        /** True if a wireless receiver is connected. */
+        bool XboxReceiverConnected;
+        /** Variable used to indicate if the XBOX 360 controller is successfully connected. */
+        uint8_t Xbox360Connected[4];
+
+protected:
+        /** Pointer to USB class instance. */
+        USB *pUsb;
+        /** Device address. */
+        uint8_t bAddress;
+        /** Endpoint info structure. */
+        EpInfo epInfo[XBOX_MAX_ENDPOINTS];
+
+private:
+        /**
+         * Called when the controller is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         * @param controller The initialized controller.
+         */
+        void onInit(uint8_t controller);
+        void (*pFuncOnInit)(void); // Pointer to function called in onInit()
+
+        bool bPollEnable;
+
+        /* Variables to store the buttons */
+        uint32_t ButtonState[4];
+        uint32_t OldButtonState[4];
+        uint16_t ButtonClickState[4];
+        int16_t hatValue[4][4];
+        uint16_t controllerStatus[4];
+        bool buttonStateChanged[4]; // True if a button has changed
+
+        bool L2Clicked[4]; // These buttons are analog, so we use we use these bools to check if they where clicked or not
+        bool R2Clicked[4];
+
+        uint32_t checkStatusTimer; // Timing for checkStatus() signals
+
+        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data
+        uint8_t writeBuf[7]; // General purpose buffer for output data
+
+        void readReport(uint8_t controller); // read incoming data
+        void printReport(uint8_t controller, uint8_t nBytes); // print incoming date - Uncomment for debugging
+
+        /* Private commands */
+        void XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes);
+        void checkStatus();
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/XBOXUSB.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,363 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#include "XBOXUSB.h"
+// To enable serial debugging see "settings.h"
+//#define EXTRADEBUG // Uncomment to get even more debugging data
+//#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller
+
+XBOXUSB::XBOXUSB(USB *p) :
+pUsb(p), // pointer to USB class instance - mandatory
+bAddress(0), // device address - mandatory
+bPollEnable(false) { // don't start polling before dongle is connected
+        for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+
+        if(pUsb) // register in USB subsystem
+                pUsb->RegisterDeviceClass(this); //set devConfig[] entry
+}
+
+uint8_t XBOXUSB::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint16_t PID;
+        uint16_t VID;
+
+        // get memory address of USB device address pool
+        AddressPool &addrPool = pUsb->GetAddressPool();
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nXBOXUSB Init"), 0x80);
+#endif
+        // check if address has already been assigned to an instance
+        if(bAddress) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress in use"), 0x80);
+#endif
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nAddress not found"), 0x80);
+#endif
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        if(!p->epinfo) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nepinfo is null"), 0x80);
+#endif
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        VID = udd->idVendor;
+        PID = udd->idProduct;
+
+        if(VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID && VID != GAMESTOP_VID) // Check VID
+                goto FailUnknownDevice;
+        if(PID == XBOX_WIRELESS_PID) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication"), 0x80);
+#endif
+                goto FailUnknownDevice;
+        } else if(PID == XBOX_WIRELESS_RECEIVER_PID || PID == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID) {
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nThis library only supports Xbox 360 controllers via USB"), 0x80);
+#endif
+                goto FailUnknownDevice;
+        } else if(PID != XBOX_WIRED_PID && PID != MADCATZ_WIRED_PID && PID != GAMESTOP_WIRED_PID && PID != AFTERGLOW_WIRED_PID && PID != JOYTECH_WIRED_PID) // Check PID
+                goto FailUnknownDevice;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+#ifdef DEBUG_USB_HOST
+                Notify(PSTR("\r\nsetAddr: "), 0x80);
+                D_PrintHex<uint8_t > (rcode, 0x80);
+#endif
+                return rcode;
+        }
+#ifdef EXTRADEBUG
+        Notify(PSTR("\r\nAddr: "), 0x80);
+        D_PrintHex<uint8_t > (bAddress, 0x80);
+#endif
+        //delay(300); // Spec says you should wait at least 200ms
+
+        p->lowspeed = false;
+
+        //get pointer to assigned address record
+        p = addrPool.GetUsbDevicePtr(bAddress);
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        // Assign epInfo to epinfo pointer - only EP0 is known
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        /* The application will work in reduced host mode, so we can save program and data
+           memory space. After verifying the VID we will use known values for the
+           configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */
+
+        /* Initialize data structures for endpoints of device */
+        epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint
+        epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;
+        epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint
+        epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;
+        epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints
+        epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;
+        epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;
+        epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;
+
+        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        delay(200); // Give time for address change
+
+        rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);
+        if(rcode)
+                goto FailSetConfDescr;
+
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox 360 Controller Connected\r\n"), 0x80);
+#endif
+        onInit();
+        Xbox360Connected = true;
+        bPollEnable = true;
+        return 0; // Successful configuration
+
+        /* Diagnostic messages */
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+#endif
+        goto Fail;
+
+FailUnknownDevice:
+#ifdef DEBUG_USB_HOST
+        NotifyFailUnknownDevice(VID, PID);
+#endif
+        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+Fail:
+#ifdef DEBUG_USB_HOST
+        Notify(PSTR("\r\nXbox 360 Init Failed, error code: "), 0x80);
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+/* Performs a cleanup after failed Init() attempt */
+uint8_t XBOXUSB::Release() {
+        Xbox360Connected = false;
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+        bAddress = 0;
+        bPollEnable = false;
+        return 0;
+}
+
+uint8_t XBOXUSB::Poll() {
+        if(!bPollEnable)
+                return 0;
+        uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;
+        pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1
+        readReport();
+#ifdef PRINTREPORT
+        printReport(); // Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
+#endif
+        return 0;
+}
+
+void XBOXUSB::readReport() {
+        if(readBuf == NULL)
+                return;
+        if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports
+                return;
+        }
+
+        ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));
+
+        hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);
+        hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);
+        hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);
+        hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);
+
+        //Notify(PSTR("\r\nButtonState"), 0x80);
+        //PrintHex<uint32_t>(ButtonState, 0x80);
+
+        if(ButtonState != OldButtonState) {
+                ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2
+                if(((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons
+                        R2Clicked = true;
+                if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)
+                        L2Clicked = true;
+                OldButtonState = ButtonState;
+        }
+}
+
+void XBOXUSB::printReport() { //Uncomment "#define PRINTREPORT" to print the report send by the Xbox 360 Controller
+#ifdef PRINTREPORT
+        if(readBuf == NULL)
+                return;
+        for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {
+                D_PrintHex<uint8_t > (readBuf[i], 0x80);
+                Notify(PSTR(" "), 0x80);
+        }
+        Notify(PSTR("\r\n"), 0x80);
+#endif
+}
+
+uint8_t XBOXUSB::getButtonPress(ButtonEnum b) {
+        if(b == L2) // These are analog buttons
+                return (uint8_t)(ButtonState >> 8);
+        else if(b == R2)
+                return (uint8_t)ButtonState;
+        return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));
+}
+
+bool XBOXUSB::getButtonClick(ButtonEnum b) {
+        if(b == L2) {
+                if(L2Clicked) {
+                        L2Clicked = false;
+                        return true;
+                }
+                return false;
+        } else if(b == R2) {
+                if(R2Clicked) {
+                        R2Clicked = false;
+                        return true;
+                }
+                return false;
+        }
+        uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);
+        bool click = (ButtonClickState & button);
+        ButtonClickState &= ~button; // clear "click" event
+        return click;
+}
+
+int16_t XBOXUSB::getAnalogHat(AnalogHatEnum a) {
+        return hatValue[a];
+}
+
+/* Xbox Controller commands */
+void XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {
+        //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)
+        pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);
+}
+
+void XBOXUSB::setLedRaw(uint8_t value) {
+        writeBuf[0] = 0x01;
+        writeBuf[1] = 0x03;
+        writeBuf[2] = value;
+
+        XboxCommand(writeBuf, 3);
+}
+
+void XBOXUSB::setLedOn(LEDEnum led) {
+        if(led == OFF)
+                setLedRaw(0);
+        else if(led != ALL) // All LEDs can't be on a the same time
+                setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4);
+}
+
+void XBOXUSB::setLedBlink(LEDEnum led) {
+        setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]));
+}
+
+void XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports
+        setLedRaw((uint8_t)ledMode);
+}
+
+void XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {
+        writeBuf[0] = 0x00;
+        writeBuf[1] = 0x08;
+        writeBuf[2] = 0x00;
+        writeBuf[3] = lValue; // big weight
+        writeBuf[4] = rValue; // small weight
+        writeBuf[5] = 0x00;
+        writeBuf[6] = 0x00;
+        writeBuf[7] = 0x00;
+
+        XboxCommand(writeBuf, 8);
+}
+
+void XBOXUSB::onInit() {
+        if(pFuncOnInit)
+                pFuncOnInit(); // Call the user function
+        else
+                setLedOn(static_cast<LEDEnum>(CONTROLLER_LED1));
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/XBOXUSB.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,226 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _xboxusb_h_
+#define _xboxusb_h_
+
+#include "Usb.h"
+#include "usbhid.h"
+#include "xboxEnums.h"
+
+/* Data Xbox 360 taken from descriptors */
+#define EP_MAXPKTSIZE       32 // max size for data via USB
+
+/* Names we give to the 3 Xbox360 pipes */
+#define XBOX_CONTROL_PIPE    0
+#define XBOX_INPUT_PIPE      1
+#define XBOX_OUTPUT_PIPE     2
+
+// PID and VID of the different devices
+#define XBOX_VID                                0x045E // Microsoft Corporation
+#define MADCATZ_VID                             0x1BAD // For unofficial Mad Catz controllers
+#define JOYTECH_VID                             0x162E // For unofficial Joytech controllers
+#define GAMESTOP_VID                            0x0E6F // Gamestop controller
+
+#define XBOX_WIRED_PID                          0x028E // Microsoft 360 Wired controller
+#define XBOX_WIRELESS_PID                       0x028F // Wireless controller only support charging
+#define XBOX_WIRELESS_RECEIVER_PID              0x0719 // Microsoft Wireless Gaming Receiver
+#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID  0x0291 // Third party Wireless Gaming Receiver
+#define MADCATZ_WIRED_PID                       0xF016 // Mad Catz wired controller
+#define JOYTECH_WIRED_PID                       0xBEEF // For Joytech wired controller
+#define GAMESTOP_WIRED_PID                      0x0401 // Gamestop wired controller
+#define AFTERGLOW_WIRED_PID                     0x0213 // Afterglow wired controller - it uses the same VID as a Gamestop controller
+
+#define XBOX_REPORT_BUFFER_SIZE 14 // Size of the input report buffer
+
+#define XBOX_MAX_ENDPOINTS   3
+
+/** This class implements support for a Xbox wired controller via USB. */
+class XBOXUSB : public USBDeviceConfig {
+public:
+        /**
+         * Constructor for the XBOXUSB class.
+         * @param  pUsb   Pointer to USB class instance.
+         */
+        XBOXUSB(USB *pUsb);
+
+        /** @name USBDeviceConfig implementation */
+        /**
+         * Initialize the Xbox Controller.
+         * @param  parent   Hub number.
+         * @param  port     Port number on the hub.
+         * @param  lowspeed Speed of the device.
+         * @return          0 on success.
+         */
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        /**
+         * Release the USB device.
+         * @return 0 on success.
+         */
+        uint8_t Release();
+        /**
+         * Poll the USB Input endpoins and run the state machines.
+         * @return 0 on success.
+         */
+        uint8_t Poll();
+
+        /**
+         * Get the device address.
+         * @return The device address.
+         */
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        /**
+         * Used to check if the controller has been initialized.
+         * @return True if it's ready.
+         */
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID || vid == GAMESTOP_VID) && (pid == XBOX_WIRED_PID || pid == MADCATZ_WIRED_PID || pid == GAMESTOP_WIRED_PID || pid == AFTERGLOW_WIRED_PID || pid == JOYTECH_WIRED_PID));
+        };
+        /**@}*/
+
+        /** @name Xbox Controller functions */
+        /**
+         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.
+         *
+         * While getButtonClick(ButtonEnum b) will only return it once.
+         *
+         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),
+         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).
+         * @param  b          ::ButtonEnum to read.
+         * @return            getButtonClick(ButtonEnum b) will return a bool, while getButtonPress(ButtonEnum b) will return a byte if reading ::L2 or ::R2.
+         */
+        uint8_t getButtonPress(ButtonEnum b);
+        bool getButtonClick(ButtonEnum b);
+        /**@}*/
+
+        /** @name Xbox Controller functions */
+        /**
+         * Return the analog value from the joysticks on the controller.
+         * @param  a          Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.
+         * @return            Returns a signed 16-bit integer.
+         */
+        int16_t getAnalogHat(AnalogHatEnum a);
+
+        /** Turn rumble off and all the LEDs on the controller. */
+        void setAllOff() {
+                setRumbleOn(0, 0);
+                setLedRaw(0);
+        };
+
+        /** Turn rumble off the controller. */
+        void setRumbleOff() {
+                setRumbleOn(0, 0);
+        };
+        /**
+         * Turn rumble on.
+         * @param lValue     Left motor (big weight) inside the controller.
+         * @param rValue     Right motor (small weight) inside the controller.
+         */
+        void setRumbleOn(uint8_t lValue, uint8_t rValue);
+        /**
+         * Set LED value. Without using the ::LEDEnum or ::LEDModeEnum.
+         * @param value      See:
+         * setLedOff(), setLedOn(LEDEnum l),
+         * setLedBlink(LEDEnum l), and setLedMode(LEDModeEnum lm).
+         */
+        void setLedRaw(uint8_t value);
+
+        /** Turn all LEDs off the controller. */
+        void setLedOff() {
+                setLedRaw(0);
+        };
+        /**
+         * Turn on a LED by using ::LEDEnum.
+         * @param l          ::OFF, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller.
+         */
+        void setLedOn(LEDEnum l);
+        /**
+         * Turn on a LED by using ::LEDEnum.
+         * @param l          ::ALL, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller.
+         */
+        void setLedBlink(LEDEnum l);
+        /**
+         * Used to set special LED modes supported by the Xbox controller.
+         * @param lm         See ::LEDModeEnum.
+         */
+        void setLedMode(LEDModeEnum lm);
+
+        /**
+         * Used to call your own function when the controller is successfully initialized.
+         * @param funcOnInit Function to call.
+         */
+        void attachOnInit(void (*funcOnInit)(void)) {
+                pFuncOnInit = funcOnInit;
+        };
+        /**@}*/
+
+        /** True if a Xbox 360 controller is connected. */
+        bool Xbox360Connected;
+
+protected:
+        /** Pointer to USB class instance. */
+        USB *pUsb;
+        /** Device address. */
+        uint8_t bAddress;
+        /** Endpoint info structure. */
+        EpInfo epInfo[XBOX_MAX_ENDPOINTS];
+
+private:
+        /**
+         * Called when the controller is successfully initialized.
+         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
+         * This is useful for instance if you want to set the LEDs in a specific way.
+         */
+        void onInit();
+        void (*pFuncOnInit)(void); // Pointer to function called in onInit()
+
+        bool bPollEnable;
+
+        /* Variables to store the buttons */
+        uint32_t ButtonState;
+        uint32_t OldButtonState;
+        uint16_t ButtonClickState;
+        int16_t hatValue[4];
+        uint16_t controllerStatus;
+
+        bool L2Clicked; // These buttons are analog, so we use we use these bools to check if they where clicked or not
+        bool R2Clicked;
+
+        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data
+        uint8_t writeBuf[8]; // General purpose buffer for output data
+
+        void readReport(); // read incoming data
+        void printReport(); // print incoming date - Uncomment for debugging
+
+        /* Private commands */
+        void XboxCommand(uint8_t* data, uint16_t nbytes);
+};
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/address.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,291 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(_usb_h_) || defined(__ADDRESS_H__)
+#error "Never include address.h directly; include Usb.h instead"
+#else
+#define __ADDRESS_H__
+
+
+
+/* NAK powers. To save space in endpoint data structure, amount of retries before giving up and returning 0x4 is stored in */
+/* bmNakPower as a power of 2. The actual nak_limit is then calculated as nak_limit = ( 2^bmNakPower - 1) */
+#define USB_NAK_MAX_POWER               15              //NAK binary order maximum value
+#define USB_NAK_DEFAULT                 14              //default 32K-1 NAKs before giving up
+#define USB_NAK_NOWAIT                  1               //Single NAK stops transfer
+#define USB_NAK_NONAK                   0               //Do not count NAKs, stop retrying after USB Timeout
+
+struct EpInfo {
+        uint8_t epAddr; // Endpoint address
+        uint8_t maxPktSize; // Maximum packet size
+
+        union {
+                uint8_t epAttribs;
+
+                struct {
+                        uint8_t bmSndToggle : 1; // Send toggle, when zero bmSNDTOG0, bmSNDTOG1 otherwise
+                        uint8_t bmRcvToggle : 1; // Send toggle, when zero bmRCVTOG0, bmRCVTOG1 otherwise
+                        uint8_t bmNakPower : 6; // Binary order for NAK_LIMIT value
+                } __attribute__((packed));
+        };
+} __attribute__((packed));
+
+//        7   6   5   4   3   2   1   0
+//  ---------------------------------
+//  |   | H | P | P | P | A | A | A |
+//  ---------------------------------
+//
+// H - if 1 the address is a hub address
+// P - parent hub address
+// A - device address / port number in case of hub
+//
+
+struct UsbDeviceAddress {
+
+        union {
+
+                struct {
+                        uint8_t bmAddress : 3; // device address/port number
+                        uint8_t bmParent : 3; // parent hub address
+                        uint8_t bmHub : 1; // hub flag
+                        uint8_t bmReserved : 1; // reserved, must be zero
+                } __attribute__((packed));
+                uint8_t devAddress;
+        };
+} __attribute__((packed));
+
+#define bmUSB_DEV_ADDR_ADDRESS          0x07
+#define bmUSB_DEV_ADDR_PARENT           0x38
+#define bmUSB_DEV_ADDR_HUB              0x40
+
+struct UsbDevice {
+        EpInfo *epinfo; // endpoint info pointer
+        UsbDeviceAddress address;
+        uint8_t epcount; // number of endpoints
+        bool lowspeed; // indicates if a device is the low speed one
+        //      uint8_t devclass; // device class
+} __attribute__((packed));
+
+class AddressPool {
+public:
+        virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) = 0;
+        virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) = 0;
+        virtual void FreeAddress(uint8_t addr) = 0;
+};
+
+typedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev);
+
+#define ADDR_ERROR_INVALID_INDEX                0xFF
+#define ADDR_ERROR_INVALID_ADDRESS              0xFF
+
+template <const uint8_t MAX_DEVICES_ALLOWED>
+class AddressPoolImpl : public AddressPool {
+        EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device
+
+        uint8_t hubCounter; // hub counter is kept
+        // in order to avoid hub address duplication
+
+        UsbDevice thePool[MAX_DEVICES_ALLOWED];
+
+        // Initializes address pool entry
+
+        void InitEntry(uint8_t index) {
+                thePool[index].address.devAddress = 0;
+                thePool[index].epcount = 1;
+                thePool[index].lowspeed = 0;
+                thePool[index].epinfo = &dev0ep;
+        };
+
+        // Returns thePool index for a given address
+
+        uint8_t FindAddressIndex(uint8_t address = 0) {
+                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) {
+                        if(thePool[i].address.devAddress == address)
+                                return i;
+                }
+                return 0;
+        };
+
+        // Returns thePool child index for a given parent
+
+        uint8_t FindChildIndex(UsbDeviceAddress addr, uint8_t start = 1) {
+                for(uint8_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; i++) {
+                        if(thePool[i].address.bmParent == addr.bmAddress)
+                                return i;
+                }
+                return 0;
+        };
+
+        // Frees address entry specified by index parameter
+
+        void FreeAddressByIndex(uint8_t index) {
+                // Zero field is reserved and should not be affected
+                if(index == 0)
+                        return;
+
+                UsbDeviceAddress uda = thePool[index].address;
+                // If a hub was switched off all port addresses should be freed
+                if(uda.bmHub == 1) {
+                        for(uint8_t i = 1; (i = FindChildIndex(uda, i));)
+                                FreeAddressByIndex(i);
+
+                        // If the hub had the last allocated address, hubCounter should be decremented
+                        if(hubCounter == uda.bmAddress)
+                                hubCounter--;
+                }
+                InitEntry(index);
+        }
+
+        // Initializes the whole address pool at once
+
+        void InitAllAddresses() {
+                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
+                        InitEntry(i);
+
+                hubCounter = 0;
+        };
+
+public:
+
+        AddressPoolImpl() : hubCounter(0) {
+                // Zero address is reserved
+                InitEntry(0);
+
+                thePool[0].address.devAddress = 0;
+                thePool[0].epinfo = &dev0ep;
+                dev0ep.epAddr = 0;
+                dev0ep.maxPktSize = 8;
+                dev0ep.bmSndToggle = 0; // Set DATA0/1 toggles to 0
+                dev0ep.bmRcvToggle = 0;
+                dev0ep.bmNakPower = USB_NAK_MAX_POWER;
+
+                InitAllAddresses();
+        };
+
+        // Returns a pointer to a specified address entry
+
+        virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) {
+                if(!addr)
+                        return thePool;
+
+                uint8_t index = FindAddressIndex(addr);
+
+                return (!index) ? NULL : thePool + index;
+        };
+
+        // Performs an operation specified by pfunc for each addressed device
+
+        void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {
+                if(!pfunc)
+                        return;
+
+                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)
+                        if(thePool[i].address.devAddress)
+                                pfunc(thePool + i);
+        };
+
+        // Allocates new address
+
+        virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) {
+                /* if (parent != 0 && port == 0)
+                        USB_HOST_SERIAL.println("PRT:0"); */
+                UsbDeviceAddress _parent;
+                _parent.devAddress = parent;
+                if(_parent.bmReserved || port > 7)
+                        //if(parent > 127 || port > 7)
+                        return 0;
+
+                if(is_hub && hubCounter == 7)
+                        return 0;
+
+                // finds first empty address entry starting from one
+                uint8_t index = FindAddressIndex(0);
+
+                if(!index) // if empty entry is not found
+                        return 0;
+
+                if(_parent.devAddress == 0) {
+                        if(is_hub) {
+                                thePool[index].address.devAddress = 0x41;
+                                hubCounter++;
+                        } else
+                                thePool[index].address.devAddress = 1;
+
+                        return thePool[index].address.devAddress;
+                }
+
+                UsbDeviceAddress addr;
+                addr.devAddress = 0; // Ensure all bits are zero
+                addr.bmParent = _parent.bmAddress;
+                if(is_hub) {
+                        addr.bmHub = 1;
+                        addr.bmAddress = ++hubCounter;
+                } else {
+                        addr.bmHub = 0;
+                        addr.bmAddress = port;
+                }
+                thePool[index].address = addr;
+                /*
+                                USB_HOST_SERIAL.print("Addr:");
+                                USB_HOST_SERIAL.print(addr.bmHub, HEX);
+                                USB_HOST_SERIAL.print(".");
+                                USB_HOST_SERIAL.print(addr.bmParent, HEX);
+                                USB_HOST_SERIAL.print(".");
+                                USB_HOST_SERIAL.println(addr.bmAddress, HEX);
+                 */
+                return thePool[index].address.devAddress;
+        };
+
+        // Empties pool entry
+
+        virtual void FreeAddress(uint8_t addr) {
+                // if the root hub is disconnected all the addresses should be initialized
+                if(addr == 0x41) {
+                        InitAllAddresses();
+                        return;
+                }
+                uint8_t index = FindAddressIndex(addr);
+                FreeAddressByIndex(index);
+        };
+
+        // Returns number of hubs attached
+        // It can be rather helpfull to find out if there are hubs attached than getting the exact number of hubs.
+        //uint8_t GetNumHubs()
+        //{
+        //        return hubCounter;
+        //};
+        //uint8_t GetNumDevices()
+        //{
+        //        uint8_t counter = 0;
+
+        //        for (uint8_t i=1; i<MAX_DEVICES_ALLOWED; i++)
+        //                if (thePool[i].address != 0);
+        //                        counter ++;
+
+        //        return counter;
+        //};
+};
+
+#endif // __ADDRESS_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/adk.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,373 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+/* Google ADK interface */
+
+#include "adk.h"
+
+const uint8_t ADK::epDataInIndex = 1;
+const uint8_t ADK::epDataOutIndex = 2;
+
+ADK::ADK(USB *p, const char* manufacturer,
+        const char* model,
+        const char* description,
+        const char* version,
+        const char* uri,
+        const char* serial) :
+
+/* ADK ID Strings */
+manufacturer(manufacturer),
+model(model),
+description(description),
+version(version),
+uri(uri),
+serial(serial),
+pUsb(p), //pointer to USB class instance - mandatory
+bAddress(0), //device address - mandatory
+bConfNum(0), //configuration number
+bNumEP(1), //if config descriptor needs to be parsed
+ready(false) {
+        // initialize endpoint data structures
+        for(uint8_t i = 0; i < ADK_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }//for(uint8_t i=0; i<ADK_MAX_ENDPOINTS; i++...
+
+        // register in USB subsystem
+        if(pUsb) {
+                pUsb->RegisterDeviceClass(this); //set devConfig[] entry
+        }
+}
+
+uint8_t ADK::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
+        return Init(parent, port, lowspeed); // Just call Init. Yes, really!
+}
+
+/* Connection initialization of an Android phone */
+uint8_t ADK::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        uint8_t num_of_conf; // number of configurations
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+
+        // get memory address of USB device address pool
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        USBTRACE("\r\nADK Init");
+
+        // check if address has already been assigned to an instance
+        if(bAddress) {
+                USBTRACE("\r\nAddress in use");
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p) {
+                USBTRACE("\r\nAddress not found");
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        if(!p->epinfo) {
+                USBTRACE("epinfo is null\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode) {
+                goto FailGetDevDescr;
+        }
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        // Extract Max Packet Size from device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                //USBTRACE2("setAddr:",rcode);
+                return rcode;
+        }//if (rcode...
+
+        //USBTRACE2("\r\nAddr:", bAddress);
+        // Spec says you should wait at least 200ms.
+        //delay(300);
+
+        p->lowspeed = false;
+
+        //get pointer to assigned address record
+        p = addrPool.GetUsbDevicePtr(bAddress);
+        if(!p) {
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        p->lowspeed = lowspeed;
+
+        // Assign epInfo to epinfo pointer - only EP0 is known
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+        if(rcode) {
+                goto FailSetDevTblEntry;
+        }
+
+        //check if ADK device is already in accessory mode; if yes, configure and exit
+        if(udd->idVendor == ADK_VID &&
+                (udd->idProduct == ADK_PID || udd->idProduct == ADB_PID)) {
+                USBTRACE("\r\nAcc.mode device detected");
+                /* go through configurations, find first bulk-IN, bulk-OUT EP, fill epInfo and quit */
+                num_of_conf = udd->bNumConfigurations;
+
+                //USBTRACE2("\r\nNC:",num_of_conf);
+                for(uint8_t i = 0; i < num_of_conf; i++) {
+                        ConfigDescParser < 0, 0, 0, 0 > confDescrParser(this);
+                        delay(1);
+                        rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+#if defined(XOOM)
+                        //added by Jaylen Scott Vanorden
+                        if(rcode) {
+                                USBTRACE2("\r\nGot 1st bad code for config: ", rcode);
+                                // Try once more
+                                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+                        }
+#endif
+                        if(rcode) {
+                                goto FailGetConfDescr;
+                        }
+                        if(bNumEP > 2) {
+                                break;
+                        }
+                } // for (uint8_t i=0; i<num_of_conf; i++...
+
+                if(bNumEP == 3) {
+                        // Assign epInfo to epinfo pointer - this time all 3 endpoins
+                        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
+                        if(rcode) {
+                                goto FailSetDevTblEntry;
+                        }
+                }
+
+                // Set Configuration Value
+                rcode = pUsb->setConf(bAddress, 0, bConfNum);
+                if(rcode) {
+                        goto FailSetConfDescr;
+                }
+                /* print endpoint structure */
+                /*
+                USBTRACE("\r\nEndpoint Structure:");
+                USBTRACE("\r\nEP0:");
+                USBTRACE2("\r\nAddr: ", epInfo[0].epAddr);
+                USBTRACE2("\r\nMax.pkt.size: ", epInfo[0].maxPktSize);
+                USBTRACE2("\r\nAttr: ", epInfo[0].epAttribs);
+                USBTRACE("\r\nEpout:");
+                USBTRACE2("\r\nAddr: ", epInfo[epDataOutIndex].epAddr);
+                USBTRACE2("\r\nMax.pkt.size: ", epInfo[epDataOutIndex].maxPktSize);
+                USBTRACE2("\r\nAttr: ", epInfo[epDataOutIndex].epAttribs);
+                USBTRACE("\r\nEpin:");
+                USBTRACE2("\r\nAddr: ", epInfo[epDataInIndex].epAddr);
+                USBTRACE2("\r\nMax.pkt.size: ", epInfo[epDataInIndex].maxPktSize);
+                USBTRACE2("\r\nAttr: ", epInfo[epDataInIndex].epAttribs);
+                 */
+
+                USBTRACE("\r\nConfiguration successful");
+                ready = true;
+                return 0; //successful configuration
+        }//if( buf->idVendor == ADK_VID...
+
+        //probe device - get accessory protocol revision
+        {
+                uint16_t adkproto = -1;
+                delay(1);
+                rcode = getProto((uint8_t*) & adkproto);
+#if defined(XOOM)
+                //added by Jaylen Scott Vanorden
+                if(rcode) {
+                        USBTRACE2("\r\nGot 1st bad code for proto: ", rcode);
+                        // Try once more
+                        rcode = getProto((uint8_t*) & adkproto);
+                }
+#endif
+                if(rcode) {
+                        goto FailGetProto; //init fails
+                }
+                USBTRACE2("\r\nADK protocol rev. ", adkproto);
+        }
+
+        delay(100);
+
+        //sending ID strings
+        sendStr(ACCESSORY_STRING_MANUFACTURER, manufacturer);
+        delay(10);
+        sendStr(ACCESSORY_STRING_MODEL, model);
+        delay(10);
+        sendStr(ACCESSORY_STRING_DESCRIPTION, description);
+        delay(10);
+        sendStr(ACCESSORY_STRING_VERSION, version);
+        delay(10);
+        sendStr(ACCESSORY_STRING_URI, uri);
+        delay(10);
+        sendStr(ACCESSORY_STRING_SERIAL, serial);
+
+        delay(100);
+
+        //switch to accessory mode
+        //the Android phone will reset
+        rcode = switchAcc();
+        if(rcode) {
+                goto FailSwAcc; //init fails
+        }
+        rcode = USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;
+        delay(100); // Give Android a chance to do its reset. This is a guess, and possibly could be lower.
+        goto SwAttempt; //switch to accessory mode attempted
+
+        /* diagnostic messages */
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr(rcode);
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry(rcode);
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr(rcode);
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr(rcode);
+        goto Fail;
+#endif
+
+FailGetProto:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("\r\ngetProto:");
+        goto Fail;
+#endif
+
+FailSwAcc:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("\r\nswAcc:");
+        goto Fail;
+#endif
+
+        //FailOnInit:
+        //        USBTRACE("OnInit:");
+        //        goto Fail;
+        //
+SwAttempt:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("\r\nAccessory mode switch attempt");
+Fail:
+#endif
+        //USBTRACE2("\r\nADK Init Failed, error code: ", rcode);
+        //NotifyFail(rcode);
+        Release();
+        return rcode;
+}
+
+/* Extracts bulk-IN and bulk-OUT endpoint information from config descriptor */
+void ADK::EndpointXtract(uint8_t conf, uint8_t iface __attribute__((unused)), uint8_t alt __attribute__((unused)), uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR *pep) {
+        //ErrorMessage<uint8_t>(PSTR("Conf.Val"), conf);
+        //ErrorMessage<uint8_t>(PSTR("Iface Num"), iface);
+        //ErrorMessage<uint8_t>(PSTR("Alt.Set"), alt);
+
+        //added by Yuuichi Akagawa
+        if(bNumEP == 3) {
+                return;
+        }
+
+        bConfNum = conf;
+
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_BULK) {
+                uint8_t index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
+                // Fill in the endpoint info structure
+                epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+                epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+
+                bNumEP++;
+
+                //PrintEndpointDescriptor(pep);
+        }
+}
+
+/* Performs a cleanup after failed Init() attempt */
+uint8_t ADK::Release() {
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+
+        bNumEP = 1; //must have to be reset to 1
+
+        bAddress = 0;
+        ready = false;
+        return 0;
+}
+
+uint8_t ADK::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) {
+        //USBTRACE2("\r\nAddr: ", bAddress );
+        //USBTRACE2("\r\nEP: ",epInfo[epDataInIndex].epAddr);
+        return pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr);
+}
+
+uint8_t ADK::SndData(uint16_t nbytes, uint8_t *dataptr) {
+        return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr);
+}
+
+void ADK::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {
+        Notify(PSTR("Endpoint descriptor:"), 0x80);
+        Notify(PSTR("\r\nLength:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
+        Notify(PSTR("\r\nType:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
+        Notify(PSTR("\r\nAddress:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
+        Notify(PSTR("\r\nAttributes:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
+        Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
+        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
+        Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
+        Notify(PSTR("\r\n"), 0x80);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/adk.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,141 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+/* Google ADK interface support header */
+
+#if !defined(_ADK_H_)
+#define _ADK_H_
+
+#include "Usb.h"
+
+#define ADK_VID   0x18D1
+#define ADK_PID   0x2D00
+#define ADB_PID   0x2D01
+
+#define XOOM  //enables repeating getProto() and getConf() attempts
+//necessary for slow devices such as Motorola XOOM
+//defined by default, can be commented out to save memory
+
+/* requests */
+
+#define ADK_GETPROTO      51  //check USB accessory protocol version
+#define ADK_SENDSTR       52  //send identifying string
+#define ADK_ACCSTART      53  //start device in accessory mode
+
+#define bmREQ_ADK_GET     USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE
+#define bmREQ_ADK_SEND    USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE
+
+#define ACCESSORY_STRING_MANUFACTURER   0
+#define ACCESSORY_STRING_MODEL          1
+#define ACCESSORY_STRING_DESCRIPTION    2
+#define ACCESSORY_STRING_VERSION        3
+#define ACCESSORY_STRING_URI            4
+#define ACCESSORY_STRING_SERIAL         5
+
+#define ADK_MAX_ENDPOINTS 3 //endpoint 0, bulk_IN, bulk_OUT
+
+class ADK;
+
+class ADK : public USBDeviceConfig, public UsbConfigXtracter {
+private:
+        /* ID strings */
+        const char* manufacturer;
+        const char* model;
+        const char* description;
+        const char* version;
+        const char* uri;
+        const char* serial;
+
+        /* ADK proprietary requests */
+        uint8_t getProto(uint8_t* adkproto);
+        uint8_t sendStr(uint8_t index, const char* str);
+        uint8_t switchAcc(void);
+
+protected:
+        static const uint8_t epDataInIndex; // DataIn endpoint index
+        static const uint8_t epDataOutIndex; // DataOUT endpoint index
+
+        /* mandatory members */
+        USB *pUsb;
+        uint8_t bAddress;
+        uint8_t bConfNum; // configuration number
+
+        uint8_t bNumEP; // total number of EP in the configuration
+        bool ready;
+
+        /* Endpoint data structure */
+        EpInfo epInfo[ADK_MAX_ENDPOINTS];
+
+        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
+
+public:
+        ADK(USB *pUsb, const char* manufacturer,
+                const char* model,
+                const char* description,
+                const char* version,
+                const char* uri,
+                const char* serial);
+
+        // Methods for receiving and sending data
+        uint8_t RcvData(uint16_t *nbytesptr, uint8_t *dataptr);
+        uint8_t SndData(uint16_t nbytes, uint8_t *dataptr);
+
+
+        // USBDeviceConfig implementation
+        uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t Release();
+
+        virtual uint8_t Poll() {
+                return 0;
+        };
+
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        virtual bool isReady() {
+                return ready;
+        };
+
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return (vid == ADK_VID && (pid == ADK_PID || pid == ADB_PID));
+        };
+
+        //UsbConfigXtracter implementation
+        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+}; //class ADK : public USBDeviceConfig ...
+
+/* get ADK protocol version */
+
+/* returns 2 bytes in *adkproto */
+inline uint8_t ADK::getProto(uint8_t* adkproto) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_GET, ADK_GETPROTO, 0, 0, 0, 2, 2, adkproto, NULL));
+}
+
+/* send ADK string */
+inline uint8_t ADK::sendStr(uint8_t index, const char* str) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_SEND, ADK_SENDSTR, 0, 0, index, strlen(str) + 1, strlen(str) + 1, (uint8_t*)str, NULL));
+}
+
+/* switch to accessory mode */
+inline uint8_t ADK::switchAcc(void) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_SEND, ADK_ACCSTART, 0, 0, 0, 0, 0, NULL, NULL));
+}
+
+#endif // _ADK_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/avrpins.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,45 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+/* derived from Konstantin Chizhov's AVR port templates */
+
+/*
+warning
+#define _usb_h_
+#define MBED_H
+*/
+
+#if !defined(_usb_h_) || defined(_avrpins_h_)
+#error "Never include avrpins.h directly; include Usb.h instead"
+#else
+#define _avrpins_h_
+
+#if defined(MBED_H)
+// for mbed 
+// pointers are 32 bits on ARM
+#define pgm_read_pointer(p) pgm_read_dword(p)
+
+#endif
+
+#endif //_avrpins_h_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/cdc_XR21B1411.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,212 @@
+/* Copyright (C) 2015 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#include "cdc_XR21B1411.h"
+
+XR21B1411::XR21B1411(USB *p, CDCAsyncOper *pasync) :
+ACM(p, pasync) {
+        // Is this needed??
+        _enhanced_status = enhanced_features(); // Set up features
+}
+
+uint8_t XR21B1411::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint8_t num_of_conf; // number of configurations
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        USBTRACE("XR Init\r\n");
+
+        if(bAddress)
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        if(!p->epinfo) {
+                USBTRACE("epinfo\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                USBTRACE2("setAddr:", rcode);
+                return rcode;
+        }
+
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        num_of_conf = udd->bNumConfigurations;
+
+        if((((udd->idVendor != 0x2890U) || (udd->idProduct != 0x0201U)) && ((udd->idVendor != 0x04e2U) || (udd->idProduct != 0x1411U))))
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        USBTRACE2("NC:", num_of_conf);
+
+        for(uint8_t i = 0; i < num_of_conf; i++) {
+                ConfigDescParser< USB_CLASS_COM_AND_CDC_CTRL,
+                        CDC_SUBCLASS_ACM,
+                        CDC_PROTOCOL_ITU_T_V_250,
+                        CP_MASK_COMPARE_CLASS |
+                        CP_MASK_COMPARE_SUBCLASS |
+                        CP_MASK_COMPARE_PROTOCOL > CdcControlParser(this);
+
+                ConfigDescParser<USB_CLASS_CDC_DATA, 0, 0,
+                        CP_MASK_COMPARE_CLASS> CdcDataParser(this);
+
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcControlParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcDataParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                if(bNumEP > 1)
+                        break;
+        } // for
+
+        if(bNumEP < 4)
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+
+        USBTRACE2("Conf:", bConfNum);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+        // Set up features status
+        _enhanced_status = enhanced_features();
+        half_duplex(false);
+        autoflowRTS(false);
+        autoflowDSR(false);
+        autoflowXON(false);
+        wide(false); // Always false, because this is only available in custom mode.
+
+        rcode = pAsync->OnInit(this);
+
+        if(rcode)
+                goto FailOnInit;
+
+        USBTRACE("XR configured\r\n");
+
+        ready = true;
+
+        //bPollEnable = true;
+
+        //USBTRACE("Poll enabled\r\n");
+        return 0;
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+        goto Fail;
+#endif
+
+FailOnInit:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("OnInit:");
+#endif
+
+#ifdef DEBUG_USB_HOST
+Fail:
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/cdc_XR21B1411.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,273 @@
+/* Copyright (C) 2015 Andrew J. Kroll
+   and
+   Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__CDC_XR21B1411_H__)
+#define __CDC_XR21B1411_H__
+
+#include "cdcacm.h"
+
+#define XR_REG_CUSTOM_DRIVER                    (0x020DU) // DRIVER SELECT
+#define XR_REG_CUSTOM_DRIVER_ACTIVE             (0x0001U) // 0: CDC 1: CUSTOM
+
+#define XR_REG_ACM_FLOW_CTL                     (0x0216U) // FLOW CONTROL REGISTER CDCACM MODE
+#define XR_REG_FLOW_CTL                         (0x0C06U) // FLOW CONTROL REGISTER CUSTOM MODE
+#define XR_REG_FLOW_CTL_HALF_DPLX               (0x0008U) // 0:FULL DUPLEX 1:HALF DUPLEX
+#define XR_REG_FLOW_CTL_MODE_MASK               (0x0007U) // MODE BITMASK
+#define XR_REG_FLOW_CTL_NONE                    (0x0000U) // NO FLOW CONTROL
+#define XR_REG_FLOW_CTL_HW                      (0x0001U) // HARDWARE FLOW CONTROL
+#define XR_REG_FLOW_CTL_SW                      (0x0002U) // SOFTWARE FLOW CONTROL
+#define XR_REG_FLOW_CTL_MMMRX                   (0x0003U) // MULTIDROP RX UPON ADDRESS MATCH
+#define XR_REG_FLOW_CTL_MMMRXTX                 (0x0004U) // MULTIDROP RX/TX UPON ADDRESS MATCH
+
+#define XR_REG_ACM_GPIO_MODE                    (0x0217U) // GPIO MODE REGISTER IN CDCACM MODE
+#define XR_REG_GPIO_MODE                        (0x0C0CU) // GPIO MODE REGISTER IN CUSTOM MODE
+#define XR_REG_GPIO_MODE_GPIO                   (0x0000U) // ALL GPIO PINS ACM PROGRAMMABLE
+#define XR_REG_GPIO_MODE_FC_RTSCTS              (0x0001U) // AUTO RTSCTS HW FC (GPIO 4/5)
+#define XR_REG_GPIO_MODE_FC_DTRDSR              (0x0002U) // AUTO DTRDSR HW FC (GPIO 2/3)
+#define XR_REG_GPIO_MODE_ATE                    (0x0003U) // AUTO TRANSCEIVER ENABLE DURING TX (GPIO 5)
+#define XR_REG_GPIO_MODE_ATE_ADDRESS            (0x0004U) // AUTO TRANSCEIVER ENABLE ON ADDRESS MATCH (GPIO 5)
+
+#define XR_REG_ACM_GPIO_DIR                     (0x0218U) // GPIO DIRECTION REGISTER CDCACM MODE, 0:IN 1:OUT
+#define XR_REG_GPIO_DIR                         (0x0C0DU) // GPIO DIRECTION REGISTER CUSTOM MODE, 0:IN 1:OUT
+
+#define XR_REG_ACM_GPIO_INT                     (0x0219U) // GPIO PIN CHANGE INTERRUPT ENABLE CDCACM MODE, 0: ENABLED 1: DISABLED
+#define XR_REG_GPIO_INT                         (0x0C11U) // GPIO PIN CHANGE INTERRUPT ENABLE CUSTOM MODE, 0: ENABLED 1: DISABLED
+#define XR_REG_GPIO_MASK                        (0x001FU) // GPIO REGISTERS BITMASK
+
+#define XR_REG_UART_ENABLE                      (0x0C00U) // UART I/O ENABLE REGISTER
+#define XR_REG_UART_ENABLE_RX                   (0x0002U) // 0:DISABLED 1:ENABLED
+#define XR_REG_UART_ENABLE_TX                   (0x0001U) // 0:DISABLED 1:ENABLED
+
+#define XR_REG_ERROR_STATUS                     (0x0C09U) // ERROR STATUS REGISTER
+#define XR_REG_ERROR_STATUS_MASK                (0x00F8U) // ERROR STATUS BITMASK
+#define XR_REG_ERROR_STATUS_ERROR               (0x0070U) // ERROR STATUS ERROR BITMASK
+#define XR_REG_ERROR_STATUS_BREAK               (0x0008U) // BREAK HAS BEEN DETECTED
+#define XR_REG_ERROR_STATUS_OVERRUN             (0x0010U) // RX OVERRUN ERROR
+#define XR_REG_ERROR_STATUS_PARITY              (0x0020U) // PARITY ERROR
+#define XR_REG_ERROR_STATUS_FRAME               (0x0040U) // FRAMING ERROR
+#define XR_REG_ERROR_STATUS_BREAKING            (0x0080U) // BREAK IS BEING DETECTED
+
+#define XR_REG_TX_BREAK                         (0x0C0AU) // TRANSMIT BREAK. 0X0001-0XFFE TIME IN MS, 0X0000 STOP, 0X0FFF BREAK ON
+
+#define XR_REG_XCVR_EN_DELAY                    (0x0C0BU) // TURN-ARROUND DELAY IN BIT-TIMES 0X0000-0X000F
+
+#define XR_REG_GPIO_SET                         (0x0C0EU) // 1:SET GPIO PIN
+
+#define XR_REG_GPIO_CLR                         (0x0C0FU) // 1:CLEAR GPIO PIN
+
+#define XR_REG_GPIO_STATUS                      (0x0C10U) // READ GPIO PINS
+
+#define XR_REG_CUSTOMISED_INT                   (0x0C12U) // 0:STANDARD 1:CUSTOM SEE DATA SHEET
+
+#define XR_REG_PIN_PULLUP_ENABLE                (0x0C14U) // 0:DISABLE 1:ENABLE, BITS 0-5:GPIO, 6:RX 7:TX
+
+#define XR_REG_PIN_PULLDOWN_ENABLE              (0x0C15U) // 0:DISABLE 1:ENABLE, BITS 0-5:GPIO, 6:RX 7:TX
+
+#define XR_REG_LOOPBACK                         (0x0C16U) // 0:DISABLE 1:ENABLE, SEE DATA SHEET
+
+#define XR_REG_RX_FIFO_LATENCY                  (0x0CC2U) // FIFO LATENCY REGISTER
+#define XR_REG_RX_FIFO_LATENCY_ENABLE           (0x0001U) //
+
+#define XR_REG_WIDE_MODE                        (0x0D02U)
+#define XR_REG_WIDE_MODE_ENABLE                 (0x0001U)
+
+#define XR_REG_XON_CHAR                         (0x0C07U)
+#define XR_REG_XOFF_CHAR                        (0x0C08U)
+
+#define XR_REG_TX_FIFO_RESET                    (0x0C80U) // 1: RESET, SELF-CLEARING
+#define XR_REG_TX_FIFO_COUNT                    (0x0C81U) // READ-ONLY
+#define XR_REG_RX_FIFO_RESET                    (0x0CC0U) // 1: RESET, SELF-CLEARING
+#define XR_REG_RX_FIFO_COUNT                    (0x0CC1U) // READ-ONLY
+
+#define XR_WRITE_REQUEST_TYPE                   (0x40U)
+
+#define XR_READ_REQUEST_TYPE                    (0xC0U)
+
+#define XR_MAX_ENDPOINTS                        4
+
+class XR21B1411 : public ACM {
+protected:
+
+public:
+        XR21B1411(USB *pusb, CDCAsyncOper *pasync);
+
+        /**
+         * Used by the USB core to check what this driver support.
+         * @param  vid The device's VID.
+         * @param  pid The device's PID.
+         * @return     Returns true if the device's VID and PID matches this driver.
+         */
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return (((vid == 0x2890U) && (pid == 0x0201U)) || ((vid == 0x04e2U) && (pid == 0x1411U)));
+        };
+
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+
+        virtual tty_features enhanced_features(void) {
+                tty_features rv;
+                rv.enhanced = true;
+                rv.autoflow_RTS = true;
+                rv.autoflow_DSR = true;
+                rv.autoflow_XON = true;
+                rv.half_duplex = true;
+                rv.wide = true;
+                return rv;
+        };
+
+        uint8_t read_register(uint16_t reg, uint16_t *val) {
+                return (pUsb->ctrlReq(bAddress, 0, XR_READ_REQUEST_TYPE, 1, 0, 0, reg, 2, 2, (uint8_t *)val, NULL));
+        }
+
+        uint8_t write_register(uint16_t reg, uint16_t val) {
+                return (pUsb->ctrlReq(bAddress, 0, XR_WRITE_REQUEST_TYPE, 0, BGRAB0(val), BGRAB1(val), reg, 0, 0, NULL, NULL));
+        }
+
+
+        ////////////////////////////////////////////////////////////////////////
+        // The following methods set the CDC-ACM defaults.
+        ////////////////////////////////////////////////////////////////////////
+
+        virtual void autoflowRTS(bool s) {
+                uint16_t val;
+                uint8_t rval;
+                rval = read_register(XR_REG_ACM_FLOW_CTL, &val);
+                if(!rval) {
+                        if(s) {
+                                val &= XR_REG_FLOW_CTL_HALF_DPLX;
+                                val |= XR_REG_FLOW_CTL_HW;
+                        } else {
+                                val &= XR_REG_FLOW_CTL_HALF_DPLX;
+                        }
+                        rval = write_register(XR_REG_ACM_FLOW_CTL, val);
+                        if(!rval) {
+                                rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO);
+                                if(!rval) {
+                                        // ACM commands apply the new settings.
+                                        LINE_CODING LCT;
+                                        rval = GetLineCoding(&LCT);
+                                        if(!rval) {
+                                                rval = SetLineCoding(&LCT);
+                                                if(!rval) {
+                                                        _enhanced_status.autoflow_XON = false;
+                                                        _enhanced_status.autoflow_DSR = false;
+                                                        _enhanced_status.autoflow_RTS = s;
+                                                }
+                                        }
+                                }
+                        }
+                }
+        };
+
+        virtual void autoflowDSR(bool s) {
+                uint16_t val;
+                uint8_t rval;
+                rval = read_register(XR_REG_ACM_FLOW_CTL, &val);
+                if(!rval) {
+                        if(s) {
+                                val &= XR_REG_FLOW_CTL_HALF_DPLX;
+                                val |= XR_REG_FLOW_CTL_HW;
+                        } else {
+                                val &= XR_REG_FLOW_CTL_HALF_DPLX;
+                        }
+                        rval = write_register(XR_REG_ACM_FLOW_CTL, val);
+                        if(!rval) {
+                                if(s) {
+                                        rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_FC_DTRDSR);
+                                } else {
+                                        rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO);
+                                }
+                                if(!rval) {
+                                        // ACM commands apply the new settings.
+                                        LINE_CODING LCT;
+                                        rval = GetLineCoding(&LCT);
+                                        if(!rval) {
+                                                rval = SetLineCoding(&LCT);
+                                                if(!rval) {
+                                                        _enhanced_status.autoflow_XON = false;
+                                                        _enhanced_status.autoflow_RTS = false;
+                                                        _enhanced_status.autoflow_DSR = s;
+                                                }
+                                        }
+                                }
+                        }
+                }
+        };
+
+        virtual void autoflowXON(bool s) {
+                // NOTE: hardware defaults to the normal XON/XOFF
+                uint16_t val;
+                uint8_t rval;
+                rval = read_register(XR_REG_ACM_FLOW_CTL, &val);
+                if(!rval) {
+                        if(s) {
+                                val &= XR_REG_FLOW_CTL_HALF_DPLX;
+                                val |= XR_REG_FLOW_CTL_SW;
+                        } else {
+                                val &= XR_REG_FLOW_CTL_HALF_DPLX;
+                        }
+                        rval = write_register(XR_REG_ACM_FLOW_CTL, val);
+                        if(!rval) {
+                                rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO);
+                                if(!rval) {
+                                        // ACM commands apply the new settings.
+                                        LINE_CODING LCT;
+                                        rval = GetLineCoding(&LCT);
+                                        if(!rval) {
+                                                rval = SetLineCoding(&LCT);
+                                                if(!rval) {
+                                                        _enhanced_status.autoflow_RTS = false;
+                                                        _enhanced_status.autoflow_DSR = false;
+                                                        _enhanced_status.autoflow_XON = s;
+                                                }
+                                        }
+                                }
+                        }
+                }
+        };
+
+        virtual void half_duplex(bool s) {
+                uint16_t val;
+                uint8_t rval;
+                rval = read_register(XR_REG_ACM_FLOW_CTL, &val);
+                if(!rval) {
+                        if(s) {
+                                val |= XR_REG_FLOW_CTL_HALF_DPLX;
+                        } else {
+                                val &= XR_REG_FLOW_CTL_MODE_MASK;
+                        }
+                        rval = write_register(XR_REG_ACM_FLOW_CTL, val);
+                        if(!rval) {
+                                // ACM commands apply the new settings.
+                                LINE_CODING LCT;
+                                rval = GetLineCoding(&LCT);
+                                if(!rval) {
+                                        rval = SetLineCoding(&LCT);
+                                        if(!rval) {
+                                                _enhanced_status.half_duplex = s;
+                                        }
+                                }
+                        }
+                }
+        };
+
+
+
+};
+
+#endif // __CDCPROLIFIC_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/cdcacm.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,368 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#include "cdcacm.h"
+
+const uint8_t ACM::epDataInIndex = 1;
+const uint8_t ACM::epDataOutIndex = 2;
+const uint8_t ACM::epInterruptInIndex = 3;
+
+ACM::ACM(USB *p, CDCAsyncOper *pasync) :
+pUsb(p),
+pAsync(pasync),
+bAddress(0),
+bControlIface(0),
+bDataIface(0),
+bNumEP(1),
+qNextPollTime(0),
+bPollEnable(false),
+ready(false) {
+        _enhanced_status = enhanced_features(); // Set up features
+        for(uint8_t i = 0; i < ACM_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i == epDataInIndex) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+
+        }
+        if(pUsb)
+                pUsb->RegisterDeviceClass(this);
+}
+
+uint8_t ACM::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint8_t num_of_conf; // number of configurations
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        USBTRACE("ACM Init\r\n");
+
+        if(bAddress)
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        if(!p->epinfo) {
+                USBTRACE("epinfo\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                USBTRACE2("setAddr:", rcode);
+                return rcode;
+        }
+
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        num_of_conf = udd->bNumConfigurations;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        USBTRACE2("NC:", num_of_conf);
+
+        for(uint8_t i = 0; i < num_of_conf; i++) {
+                ConfigDescParser< USB_CLASS_COM_AND_CDC_CTRL,
+                        CDC_SUBCLASS_ACM,
+                        CDC_PROTOCOL_ITU_T_V_250,
+                        CP_MASK_COMPARE_CLASS |
+                        CP_MASK_COMPARE_SUBCLASS |
+                        CP_MASK_COMPARE_PROTOCOL > CdcControlParser(this);
+
+                ConfigDescParser<USB_CLASS_CDC_DATA, 0, 0,
+                        CP_MASK_COMPARE_CLASS> CdcDataParser(this);
+
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcControlParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcDataParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                if(bNumEP > 1)
+                        break;
+        } // for
+
+        if(bNumEP < 4)
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+
+        USBTRACE2("Conf:", bConfNum);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+        // Set up features status
+        _enhanced_status = enhanced_features();
+        half_duplex(false);
+        autoflowRTS(false);
+        autoflowDSR(false);
+        autoflowXON(false);
+        wide(false); // Always false, because this is only available in custom mode.
+        rcode = pAsync->OnInit(this);
+
+        if(rcode)
+                goto FailOnInit;
+
+        USBTRACE("ACM configured\r\n");
+
+        ready = true;
+
+        //bPollEnable = true;
+
+        //USBTRACE("Poll enabled\r\n");
+        return 0;
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+        goto Fail;
+#endif
+
+FailOnInit:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("OnInit:");
+#endif
+
+#ifdef DEBUG_USB_HOST
+Fail:
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+void ACM::EndpointXtract(uint8_t conf, uint8_t iface __attribute__((unused)), uint8_t alt __attribute__((unused)), uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR *pep) {
+        //ErrorMessage<uint8_t > (PSTR("Conf.Val"), conf);
+        //ErrorMessage<uint8_t > (PSTR("Iface Num"), iface);
+        //ErrorMessage<uint8_t > (PSTR("Alt.Set"), alt);
+
+        bConfNum = conf;
+
+        uint8_t index;
+
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT && (pep->bEndpointAddress & 0x80) == 0x80)
+                index = epInterruptInIndex;
+        else if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_BULK)
+                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
+        else
+                return;
+
+        // Fill in the endpoint info structure
+        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+        epInfo[index].bmSndToggle = 0;
+        epInfo[index].bmRcvToggle = 0;
+
+        bNumEP++;
+
+        PrintEndpointDescriptor(pep);
+}
+
+uint8_t ACM::Release() {
+        ready = false;
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+
+        bControlIface = 0;
+        bDataIface = 0;
+        bNumEP = 1;
+
+        bAddress = 0;
+        qNextPollTime = 0;
+        bPollEnable = false;
+        return 0;
+}
+
+uint8_t ACM::Poll() {
+        //uint8_t rcode = 0;
+        //if(!bPollEnable)
+        //        return 0;
+        //return rcode;
+        return 0;
+}
+
+uint8_t ACM::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) {
+        uint8_t rv = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t ACM::SndData(uint16_t nbytes, uint8_t *dataptr) {
+        uint8_t rv = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t ACM::SetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr) {
+        uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, nbytes, nbytes, dataptr, NULL));
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t ACM::GetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr) {
+        uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCIN, CDC_GET_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, nbytes, nbytes, dataptr, NULL));
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t ACM::ClearCommFeature(uint16_t fid) {
+        uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_CLEAR_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, 0, 0, NULL, NULL));
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t ACM::SetLineCoding(const LINE_CODING *dataptr) {
+        uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_LINE_CODING, 0x00, 0x00, bControlIface, sizeof (LINE_CODING), sizeof (LINE_CODING), (uint8_t*)dataptr, NULL));
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t ACM::GetLineCoding(LINE_CODING *dataptr) {
+        uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCIN, CDC_GET_LINE_CODING, 0x00, 0x00, bControlIface, sizeof (LINE_CODING), sizeof (LINE_CODING), (uint8_t*)dataptr, NULL));
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t ACM::SetControlLineState(uint8_t state) {
+        uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_CONTROL_LINE_STATE, state, 0, bControlIface, 0, 0, NULL, NULL));
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t ACM::SendBreak(uint16_t duration) {
+        uint8_t rv = ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SEND_BREAK, (duration & 0xff), (duration >> 8), bControlIface, 0, 0, NULL, NULL));
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+void ACM::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {
+        Notify(PSTR("Endpoint descriptor:"), 0x80);
+        Notify(PSTR("\r\nLength:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
+        Notify(PSTR("\r\nType:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
+        Notify(PSTR("\r\nAddress:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
+        Notify(PSTR("\r\nAttributes:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
+        Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
+        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
+        Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
+        Notify(PSTR("\r\n"), 0x80);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/cdcacm.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,252 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__CDCACM_H__)
+#define __CDCACM_H__
+
+#include "Usb.h"
+
+#define bmREQ_CDCOUT                    USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+#define bmREQ_CDCIN                     USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+
+// CDC Subclass Constants
+#define CDC_SUBCLASS_DLCM               0x01    // Direct Line Control Model
+#define CDC_SUBCLASS_ACM                0x02    // Abstract Control Model
+#define CDC_SUBCLASS_TCM                0x03    // Telephone Control Model
+#define CDC_SUBCLASS_MCCM               0x04    // Multi Channel Control Model
+#define CDC_SUBCLASS_CAPI               0x05    // CAPI Control Model
+#define CDC_SUBCLASS_ETHERNET           0x06    // Ethernet Network Control Model
+#define CDC_SUBCLASS_ATM                0x07    // ATM Network Control Model
+#define CDC_SUBCLASS_WIRELESS_HANDSET   0x08    // Wireless Handset Control Model
+#define CDC_SUBCLASS_DEVICE_MANAGEMENT  0x09    // Device Management
+#define CDC_SUBCLASS_MOBILE_DIRECT_LINE 0x0A    // Mobile Direct Line Model
+#define CDC_SUBCLASS_OBEX               0x0B    // OBEX
+#define CDC_SUBCLASS_ETHERNET_EMU       0x0C    // Ethernet Emulation Model
+
+// Communication Interface Class Control Protocol Codes
+#define CDC_PROTOCOL_ITU_T_V_250        0x01    // AT Commands defined by ITU-T V.250
+#define CDC_PROTOCOL_PCCA_101           0x02    // AT Commands defined by PCCA-101
+#define CDC_PROTOCOL_PCCA_101_O         0x03    // AT Commands defined by PCCA-101 & Annex O
+#define CDC_PROTOCOL_GSM_7_07           0x04    // AT Commands defined by GSM 7.07
+#define CDC_PROTOCOL_3GPP_27_07         0x05    // AT Commands defined by 3GPP 27.007
+#define CDC_PROTOCOL_C_S0017_0          0x06    // AT Commands defined by TIA for CDMA
+#define CDC_PROTOCOL_USB_EEM            0x07    // Ethernet Emulation Model
+
+// CDC Commands defined by CDC 1.2
+#define CDC_SEND_ENCAPSULATED_COMMAND   0x00
+#define CDC_GET_ENCAPSULATED_RESPONSE   0x01
+
+// CDC Commands defined by PSTN 1.2
+#define CDC_SET_COMM_FEATURE            0x02
+#define CDC_GET_COMM_FEATURE            0x03
+#define CDC_CLEAR_COMM_FEATURE          0x04
+#define CDC_SET_AUX_LINE_STATE          0x10
+#define CDC_SET_HOOK_STATE              0x11
+#define CDC_PULSE_SETUP                 0x12
+#define CDC_SEND_PULSE                  0x13
+#define CDC_SET_PULSE_TIME              0x14
+#define CDC_RING_AUX_JACK               0x15
+#define CDC_SET_LINE_CODING             0x20
+#define CDC_GET_LINE_CODING             0x21
+#define CDC_SET_CONTROL_LINE_STATE      0x22
+#define CDC_SEND_BREAK                  0x23
+#define CDC_SET_RINGER_PARMS            0x30
+#define CDC_GET_RINGER_PARMS            0x31
+#define CDC_SET_OPERATION_PARMS         0x32
+#define CDC_GET_OPERATION_PARMS         0x33
+#define CDC_SET_LINE_PARMS              0x34
+#define CDC_GET_LINE_PARMS              0x35
+#define CDC_DIAL_DIGITS                 0x36
+
+//Class-Specific Notification Codes
+#define NETWORK_CONNECTION              0x00
+#define RESPONSE_AVAILABLE              0x01
+#define AUX_JACK_HOOK_STATE             0x08
+#define RING_DETECT                     0x09
+#define SERIAL_STATE                    0x20
+#define CALL_STATE_CHANGE               0x28
+#define LINE_STATE_CHANGE               0x29
+#define CONNECTION_SPEED_CHANGE         0x2a
+
+// CDC Functional Descriptor Structures
+
+typedef struct {
+        uint8_t bFunctionLength;
+        uint8_t bDescriptorType;
+        uint8_t bDescriptorSubtype;
+        uint8_t bmCapabilities;
+        uint8_t bDataInterface;
+} CALL_MGMNT_FUNC_DESCR;
+
+typedef struct {
+        uint8_t bFunctionLength;
+        uint8_t bDescriptorType;
+        uint8_t bDescriptorSubtype;
+        uint8_t bmCapabilities;
+} ACM_FUNC_DESCR, DLM_FUNC_DESCR, TEL_OPER_MODES_FUNC_DESCR,
+TEL_CALL_STATE_REP_CPBL_FUNC_DESCR;
+
+typedef struct {
+        uint8_t bFunctionLength;
+        uint8_t bDescriptorType;
+        uint8_t bDescriptorSubtype;
+        uint8_t bRingerVolSteps;
+        uint8_t bNumRingerPatterns;
+} TEL_RINGER_FUNC_DESCR;
+
+typedef struct {
+        uint32_t dwDTERate; // Data Terminal Rate in bits per second
+        uint8_t bCharFormat; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits
+        uint8_t bParityType; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space
+        uint8_t bDataBits; // Data bits (5, 6, 7, 8 or 16)
+} LINE_CODING;
+
+typedef struct {
+        uint8_t bmRequestType; // 0xa1 for class-specific notifications
+        uint8_t bNotification;
+        uint16_t wValue;
+        uint16_t wIndex;
+        uint16_t wLength;
+        uint16_t bmState; //UART state bitmap for SERIAL_STATE, other notifications variable length
+} CLASS_NOTIFICATION;
+
+class ACM;
+
+class CDCAsyncOper {
+public:
+
+        virtual uint8_t OnInit(ACM *pacm __attribute__((unused))) {
+                return 0;
+        };
+        //virtual void OnDataRcvd(ACM *pacm, uint8_t nbytes, uint8_t *dataptr) = 0;
+        //virtual void OnDisconnected(ACM *pacm) = 0;
+};
+
+/**
+ * This structure is used to report the extended capabilities of the connected device.
+ * It is also used to report the current status.
+ * Regular CDC-ACM reports all as false.
+ */
+typedef struct {
+
+        union {
+                uint8_t tty;
+
+                struct {
+                        bool enhanced : 1; // Do we have the ability to set/clear any features?
+                        // Status and 8th bit in data stream.
+                        // Presence only indicates feature is available, but this isn't used for CDC-ACM.
+                        bool wide : 1;
+                        bool autoflow_RTS : 1; // Has autoflow on RTS/CTS
+                        bool autoflow_DSR : 1; // Has autoflow on DTR/DSR
+                        bool autoflow_XON : 1; // Has autoflow  XON/XOFF
+                        bool half_duplex : 1;  // Has half-duplex capability.
+                } __attribute__((packed));
+        };
+} tty_features;
+
+#define ACM_MAX_ENDPOINTS               4
+
+class ACM : public USBDeviceConfig, public UsbConfigXtracter {
+protected:
+        USB *pUsb;
+        CDCAsyncOper *pAsync;
+        uint8_t bAddress;
+        uint8_t bConfNum; // configuration number
+        uint8_t bControlIface; // Control interface value
+        uint8_t bDataIface; // Data interface value
+        uint8_t bNumEP; // total number of EP in the configuration
+        uint32_t qNextPollTime; // next poll time
+        volatile bool bPollEnable; // poll enable flag
+        volatile bool ready; //device ready indicator
+        tty_features _enhanced_status; // current status
+
+        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
+
+public:
+        static const uint8_t epDataInIndex; // DataIn endpoint index
+        static const uint8_t epDataOutIndex; // DataOUT endpoint index
+        static const uint8_t epInterruptInIndex; // InterruptIN  endpoint index
+        EpInfo epInfo[ACM_MAX_ENDPOINTS];
+
+        ACM(USB *pusb, CDCAsyncOper *pasync);
+
+        uint8_t SetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr);
+        uint8_t GetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr);
+        uint8_t ClearCommFeature(uint16_t fid);
+        uint8_t SetLineCoding(const LINE_CODING *dataptr);
+        uint8_t GetLineCoding(LINE_CODING *dataptr);
+        uint8_t SetControlLineState(uint8_t state);
+        uint8_t SendBreak(uint16_t duration);
+        uint8_t GetNotif(uint16_t *bytes_rcvd, uint8_t *dataptr);
+
+        // Methods for receiving and sending data
+        uint8_t RcvData(uint16_t *nbytesptr, uint8_t *dataptr);
+        uint8_t SndData(uint16_t nbytes, uint8_t *dataptr);
+
+        // USBDeviceConfig implementation
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t Release();
+        uint8_t Poll();
+
+        bool available(void) {
+                return false;
+        };
+
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        virtual bool isReady() {
+                return ready;
+        };
+
+        virtual tty_features enhanced_status(void) {
+                return _enhanced_status;
+        };
+
+        virtual tty_features enhanced_features(void) {
+                tty_features rv;
+                rv.enhanced = false;
+                rv.autoflow_RTS = false;
+                rv.autoflow_DSR = false;
+                rv.autoflow_XON = false;
+                rv.half_duplex = false;
+                rv.wide = false;
+                return rv;
+        };
+
+        virtual void autoflowRTS(bool s __attribute__((unused))) {
+        };
+
+        virtual void autoflowDSR(bool s __attribute__((unused))) {
+        };
+
+        virtual void autoflowXON(bool s __attribute__((unused))) {
+        };
+
+        virtual void half_duplex(bool s __attribute__((unused))) {
+        };
+
+        virtual void wide(bool s __attribute__((unused))) {
+        };
+
+        // UsbConfigXtracter implementation
+        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+};
+
+#endif // __CDCACM_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/cdcftdi.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,409 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#include "cdcftdi.h"
+
+const uint8_t FTDI::epDataInIndex = 1;
+const uint8_t FTDI::epDataOutIndex = 2;
+const uint8_t FTDI::epInterruptInIndex = 3;
+
+FTDI::FTDI(USB *p, FTDIAsyncOper *pasync, uint16_t idProduct) :
+pAsync(pasync),
+pUsb(p),
+bAddress(0),
+bNumEP(1),
+wFTDIType(0),
+wIdProduct(idProduct) {
+        for(uint8_t i = 0; i < FTDI_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i == epDataInIndex) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+        if(pUsb)
+                pUsb->RegisterDeviceClass(this);
+}
+
+uint8_t FTDI::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+
+        uint8_t num_of_conf; // number of configurations
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        USBTRACE("FTDI Init\r\n");
+
+        if(bAddress) {
+                USBTRACE("FTDI CLASS IN USE??\r\n");
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p) {
+                USBTRACE("FTDI NO ADDRESS??\r\n");
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+        if(!p->epinfo) {
+                USBTRACE("epinfo\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), buf);
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode) {
+                goto FailGetDevDescr;
+        }
+        if(udd->idVendor != FTDI_VID || udd->idProduct != wIdProduct) {
+                USBTRACE("FTDI Init: Product not supported\r\n");
+                USBTRACE2("Expected VID:", FTDI_VID);
+                USBTRACE2("Found VID:", udd->idVendor);
+
+                USBTRACE2("Expected PID:", wIdProduct);
+                USBTRACE2("Found PID:", udd->idProduct);
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+        }
+
+        // Save type of FTDI chip
+        wFTDIType = udd->bcdDevice;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+        // Some devices set endpoint lengths to zero, which is incorrect.
+        // we should check them, and if zero, set them to 64.
+        if(epInfo[0].maxPktSize == 0) epInfo[0].maxPktSize = 64;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                USBTRACE2("setAddr:", rcode);
+                return rcode;
+        }
+
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        num_of_conf = udd->bNumConfigurations;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        USBTRACE2("NC:", num_of_conf);
+
+        for(uint8_t i = 0; i < num_of_conf; i++) {
+                ConfigDescParser < 0xFF, 0xFF, 0xFF, CP_MASK_COMPARE_ALL> confDescrParser(this);
+
+                // This interferes with serial output, and should be opt-in for debugging.
+                //HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;
+                //rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);
+                //if(rcode)
+                //        goto FailGetConfDescr;
+
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                if(bNumEP > 1)
+                        break;
+        } // for
+
+        if(bNumEP < 2)
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        USBTRACE2("NumEP:", bNumEP);
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+
+        USBTRACE2("Conf:", bConfNum);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+        // default latency is 16ms on-chip, reduce it to 1
+        rcode = SetLatency(1);
+        if(rcode)
+                goto FailOnLatency;
+
+
+        rcode = pAsync->OnInit(this);
+
+        if(rcode)
+                goto FailOnInit;
+
+        USBTRACE("FTDI configured\r\n");
+
+        ready = true;
+        return 0;
+
+FailOnLatency:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("SetLatency: ");
+        goto Fail;
+#endif
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+        goto Fail;
+#endif
+
+FailOnInit:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("OnInit:");
+
+Fail:
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+void FTDI::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR *pep) {
+        ErrorMessage<uint8_t > (PSTR("Conf.Val"), conf);
+        ErrorMessage<uint8_t > (PSTR("Iface Num"), iface);
+        ErrorMessage<uint8_t > (PSTR("Alt.Set"), alt);
+
+        bConfNum = conf;
+
+        uint8_t index;
+
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT && (pep->bEndpointAddress & 0x80) == 0x80)
+                index = epInterruptInIndex;
+        else if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_BULK)
+                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
+        else
+                return;
+
+        // Fill in the endpoint info structure
+        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+        epInfo[index].bmSndToggle = 0;
+        epInfo[index].bmRcvToggle = 0;
+        // Some device vendors set endpoint lengths to zero, which is incorrect.
+        // Check, and if zero, set to 64.
+        if(epInfo[index].maxPktSize == 0) epInfo[index].maxPktSize = 64;
+
+        bNumEP++;
+
+        PrintEndpointDescriptor(pep);
+}
+
+uint8_t FTDI::Release() {
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+
+        bAddress = 0;
+        bNumEP = 1;
+        qNextPollTime = 0;
+        bPollEnable = false;
+        ready = false;
+        return pAsync->OnRelease(this);
+}
+
+uint8_t FTDI::Poll() {
+        uint8_t rcode = 0;
+
+        //if (!bPollEnable)
+        //      return 0;
+
+        //if (qNextPollTime <= (uint32_t)millis())
+        //{
+        //      USB_HOST_SERIAL.println(bAddress, HEX);
+
+        //      qNextPollTime = (uint32_t)millis() + 100;
+        //}
+        return rcode;
+}
+
+uint8_t FTDI::SetBaudRate(uint32_t baud) {
+        uint16_t baud_value, baud_index = 0;
+        uint32_t divisor3;
+        divisor3 = 48000000 / 2 / baud; // divisor shifted 3 bits to the left
+
+        if(wFTDIType == FT232AM) {
+                if((divisor3 & 0x7) == 7)
+                        divisor3++; // round x.7/8 up to x+1
+
+                baud_value = divisor3 >> 3;
+                divisor3 &= 0x7;
+
+                if(divisor3 == 1) baud_value |= 0xc000;
+                else // 0.125
+                        if(divisor3 >= 4) baud_value |= 0x4000;
+                else // 0.5
+                        if(divisor3 != 0) baud_value |= 0x8000; // 0.25
+                if(baud_value == 1) baud_value = 0; /* special case for maximum baud rate */
+        } else {
+                static const uint8_t divfrac [8] = {0, 3, 2, 0, 1, 1, 2, 3};
+                static const uint8_t divindex[8] = {0, 0, 0, 1, 0, 1, 1, 1};
+
+                baud_value = divisor3 >> 3;
+                baud_value |= divfrac [divisor3 & 0x7] << 14;
+                baud_index = divindex[divisor3 & 0x7];
+
+                /* Deal with special cases for highest baud rates. */
+                if(baud_value == 1) baud_value = 0;
+                else // 1.0
+                        if(baud_value == 0x4001) baud_value = 1; // 1.5
+        }
+        USBTRACE2("baud_value:", baud_value);
+        USBTRACE2("baud_index:", baud_index);
+        uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_BAUD_RATE, baud_value & 0xff, baud_value >> 8, baud_index, 0, 0, NULL, NULL);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+// No docs on if this is 8 or 16 bit, so play it safe, make maximum 255ms
+
+uint8_t FTDI::SetLatency(uint8_t l) {
+        uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_LATENCY_TIMER, l, 0, 0, 0, 0, NULL, NULL);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+// No docs on if this is 8 or 16 bit, so play it safe, make maximum 255ms
+
+uint8_t FTDI::GetLatency(uint8_t *l) {
+        uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_GET_LATENCY_TIMER, 0, 0, 0, 0, 1, (uint8_t *)l, NULL);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t FTDI::SetModemControl(uint16_t signal) {
+        uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_MODEM_CTRL, signal & 0xff, signal >> 8, 0, 0, 0, NULL, NULL);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t FTDI::SetFlowControl(uint8_t protocol, uint8_t xon, uint8_t xoff) {
+        uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_FLOW_CTRL, xon, xoff, protocol << 8, 0, 0, NULL, NULL);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t FTDI::SetData(uint16_t databm) {
+        uint8_t rv = pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_DATA, databm & 0xff, databm >> 8, 0, 0, 0, NULL, NULL);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t FTDI::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) {
+        uint8_t rv = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+uint8_t FTDI::SndData(uint16_t nbytes, uint8_t *dataptr) {
+        uint8_t rv = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr);
+        if(rv && rv != hrNAK) {
+                Release();
+        }
+        return rv;
+}
+
+void FTDI::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {
+        Notify(PSTR("Endpoint descriptor:"), 0x80);
+        Notify(PSTR("\r\nLength:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
+        Notify(PSTR("\r\nType:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
+        Notify(PSTR("\r\nAddress:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
+        Notify(PSTR("\r\nAttributes:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
+        Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
+        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
+        Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
+        Notify(PSTR("\r\n"), 0x80);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/cdcftdi.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,155 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__CDCFTDI_H__)
+#define __CDCFTDI_H__
+
+#include "Usb.h"
+
+#define bmREQ_FTDI_OUT  0x40
+#define bmREQ_FTDI_IN   0xc0
+
+//#define bmREQ_FTDI_OUT                USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+//#define bmREQ_FTDI_IN         USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+
+#define FTDI_VID                        0x0403  // FTDI VID
+#define FTDI_PID                        0x6001  // FTDI PID
+
+#define FT232AM                         0x0200
+#define FT232BM                         0x0400
+#define FT2232                          0x0500
+#define FT232R                          0x0600
+
+// Commands
+#define FTDI_SIO_RESET                  0  /* Reset the port */
+#define FTDI_SIO_MODEM_CTRL             1  /* Set the modem control register */
+#define FTDI_SIO_SET_FLOW_CTRL          2  /* Set flow control register */
+#define FTDI_SIO_SET_BAUD_RATE          3  /* Set baud rate */
+#define FTDI_SIO_SET_DATA               4  /* Set the data characteristics of the port */
+#define FTDI_SIO_GET_MODEM_STATUS       5  /* Retrieve current value of modem status register */
+#define FTDI_SIO_SET_EVENT_CHAR         6  /* Set the event character */
+#define FTDI_SIO_SET_ERROR_CHAR         7  /* Set the error character */
+#define FTDI_SIO_SET_LATENCY_TIMER      9  /* Set the latency timer */
+#define FTDI_SIO_GET_LATENCY_TIMER      10 /* Get the latency timer */
+
+#define FTDI_SIO_RESET_SIO              0
+#define FTDI_SIO_RESET_PURGE_RX         1
+#define FTDI_SIO_RESET_PURGE_TX         2
+
+#define FTDI_SIO_SET_DATA_PARITY_NONE   (0x0 << 8 )
+#define FTDI_SIO_SET_DATA_PARITY_ODD    (0x1 << 8 )
+#define FTDI_SIO_SET_DATA_PARITY_EVEN   (0x2 << 8 )
+#define FTDI_SIO_SET_DATA_PARITY_MARK   (0x3 << 8 )
+#define FTDI_SIO_SET_DATA_PARITY_SPACE  (0x4 << 8 )
+#define FTDI_SIO_SET_DATA_STOP_BITS_1   (0x0 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_15  (0x1 << 11)
+#define FTDI_SIO_SET_DATA_STOP_BITS_2   (0x2 << 11)
+#define FTDI_SIO_SET_BREAK              (0x1 << 14)
+
+#define FTDI_SIO_SET_DTR_MASK           0x1
+#define FTDI_SIO_SET_DTR_HIGH           ( 1 | ( FTDI_SIO_SET_DTR_MASK  << 8))
+#define FTDI_SIO_SET_DTR_LOW            ( 0 | ( FTDI_SIO_SET_DTR_MASK  << 8))
+#define FTDI_SIO_SET_RTS_MASK           0x2
+#define FTDI_SIO_SET_RTS_HIGH           ( 2 | ( FTDI_SIO_SET_RTS_MASK << 8 ))
+#define FTDI_SIO_SET_RTS_LOW            ( 0 | ( FTDI_SIO_SET_RTS_MASK << 8 ))
+
+  #define FTDI_SIO_DISABLE_FLOW_CTRL      0x0
+#define FTDI_SIO_RTS_CTS_HS             (0x1 << 8)
+#define FTDI_SIO_DTR_DSR_HS             (0x2 << 8)
+#define FTDI_SIO_XON_XOFF_HS            (0x4 << 8)
+
+#define FTDI_SIO_CTS_MASK               0x10
+#define FTDI_SIO_DSR_MASK               0x20
+#define FTDI_SIO_RI_MASK                0x40
+#define FTDI_SIO_RLSD_MASK              0x80
+
+class FTDI;
+
+class FTDIAsyncOper {
+public:
+
+        virtual uint8_t OnInit(FTDI *pftdi __attribute__((unused))) {
+                return 0;
+        };
+
+        virtual uint8_t OnRelease(FTDI *pftdi __attribute__((unused))) {
+                return 0;
+        };
+};
+
+
+// Only single port chips are currently supported by the library,
+//              so only three endpoints are allocated.
+#define FTDI_MAX_ENDPOINTS              3
+
+class FTDI : public USBDeviceConfig, public UsbConfigXtracter {
+        static const uint8_t epDataInIndex; // DataIn endpoint index
+        static const uint8_t epDataOutIndex; // DataOUT endpoint index
+        static const uint8_t epInterruptInIndex; // InterruptIN  endpoint index
+
+        FTDIAsyncOper *pAsync;
+        USB *pUsb;
+        uint8_t bAddress;
+        uint8_t bConfNum; // configuration number
+        uint8_t bNumIface; // number of interfaces in the configuration
+        uint8_t bNumEP; // total number of EP in the configuration
+        uint32_t qNextPollTime; // next poll time
+        volatile bool bPollEnable; // poll enable flag
+        volatile bool ready; //device ready indicator
+        uint16_t wFTDIType; // Type of FTDI chip
+        uint16_t wIdProduct; // expected PID
+
+        EpInfo epInfo[FTDI_MAX_ENDPOINTS];
+
+        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
+
+public:
+        FTDI(USB *pusb, FTDIAsyncOper *pasync, uint16_t idProduct = FTDI_PID);
+
+        uint8_t SetBaudRate(uint32_t baud);
+        uint8_t SetModemControl(uint16_t control);
+        uint8_t SetFlowControl(uint8_t protocol, uint8_t xon = 0x11, uint8_t xoff = 0x13);
+        uint8_t SetData(uint16_t databm);
+        uint8_t SetLatency(uint8_t l);
+        uint8_t GetLatency(uint8_t *l);
+
+        // Methods for receiving and sending data
+        uint8_t RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr);
+        uint8_t SndData(uint16_t nbytes, uint8_t *dataptr);
+
+        // USBDeviceConfig implementation
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t Release();
+        uint8_t Poll();
+
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        // UsbConfigXtracter implementation
+        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+
+        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {
+                return (vid == FTDI_VID && pid == FTDI_PID);
+        }
+        virtual bool isReady() {
+                return ready;
+        };
+
+};
+
+#endif // __CDCFTDI_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/cdcprolific.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,248 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#include "cdcprolific.h"
+
+PL2303::PL2303(USB *p, CDCAsyncOper *pasync) :
+ACM(p, pasync),
+wPLType(0) {
+}
+
+uint8_t PL2303::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint8_t num_of_conf; // number of configurations
+#ifdef PL2303_COMPAT
+        enum pl2303_type pltype = unknown;
+#endif
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        USBTRACE("PL Init\r\n");
+
+        if(bAddress)
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        if(!p->epinfo) {
+                USBTRACE("epinfo\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        if(udd->idVendor != PL_VID && CHECK_PID(udd->idProduct))
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        /* determine chip variant */
+#ifdef PL2303_COMPAT
+        if(udd->bDeviceClass == 0x02 )
+                pltype = type_0;
+        else if(udd->bMaxPacketSize0 == 0x40 )
+                pltype = rev_HX;
+        else if(udd->bDeviceClass == 0x00)
+                pltype = type_1;
+        else if(udd->bDeviceClass == 0xff)
+                pltype = type_1;
+#endif
+
+        // Save type of PL chip
+        wPLType = udd->bcdDevice;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                USBTRACE2("setAddr:", rcode);
+                return rcode;
+        }
+
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        num_of_conf = udd->bNumConfigurations;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        USBTRACE2("NC:", num_of_conf);
+
+        for(uint8_t i = 0; i < num_of_conf; i++) {
+                HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;
+                ConfigDescParser < 0xFF, 0, 0, CP_MASK_COMPARE_CLASS> confDescrParser(this);
+
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                if(bNumEP > 1)
+                        break;
+        } // for
+
+        if(bNumEP < 2)
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+
+        USBTRACE2("Conf:", bConfNum);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+#ifdef PL2303_COMPAT
+        /* Shamanic dance - sending Prolific init data as-is */
+        vendorRead( 0x84, 0x84, 0, buf );
+        vendorWrite( 0x04, 0x04, 0 );
+        vendorRead( 0x84, 0x84, 0, buf );
+        vendorRead( 0x83, 0x83, 0, buf );
+        vendorRead( 0x84, 0x84, 0, buf );
+        vendorWrite( 0x04, 0x04, 1 );
+        vendorRead( 0x84, 0x84, 0, buf);
+        vendorRead( 0x83, 0x83, 0, buf);
+        vendorWrite( 0, 0, 1 );
+        vendorWrite( 1, 0, 0 );
+        if( pltype == rev_HX ) {
+                vendorWrite( 2, 0, 0x44 );
+                vendorWrite( 0x06, 0x06, 0 ); // From W7 init
+        }
+        else {
+                vendorWrite( 2, 0, 0x24 );
+        }
+        /* Shamanic dance end */
+#endif
+        /* Calling post-init callback */
+        rcode = pAsync->OnInit(this);
+
+        if(rcode)
+                goto FailOnInit;
+
+        USBTRACE("PL configured\r\n");
+
+        //bPollEnable = true;
+        ready = true;
+        return 0;
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+        goto Fail;
+#endif
+
+FailOnInit:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("OnInit:");
+#endif
+
+#ifdef DEBUG_USB_HOST
+Fail:
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+//uint8_t PL::Poll()
+//{
+//      uint8_t rcode = 0;
+//
+//      //if (!bPollEnable)
+//      //      return 0;
+//
+//      //if (qNextPollTime <= (uint32_t)millis())
+//      //{
+//      //      USB_HOST_SERIAL.println(bAddress, HEX);
+//
+//      //      qNextPollTime = (uint32_t)millis() + 100;
+//      //}
+//      return rcode;
+//}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/cdcprolific.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,160 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__CDCPROLIFIC_H__)
+#define __CDCPROLIFIC_H__
+
+#include "cdcacm.h"
+
+//#define PL2303_COMPAT // Uncomment it if you have compatibility problems
+
+#define PL_VID                                  0x067B
+#define CHECK_PID(pid)                          ( pid != 0x2303 && pid != 0x0609 )
+
+//#define PL_PID                                0x0609
+
+#define PROLIFIC_REV_H                          0x0202
+#define PROLIFIC_REV_X                          0x0300
+#define PROLIFIC_REV_HX_CHIP_D                  0x0400
+#define PROLIFIC_REV_1                          0x0001
+
+#define kXOnChar                                '\x11'
+#define kXOffChar                               '\x13'
+
+#define SPECIAL_SHIFT                           (5)
+#define SPECIAL_MASK                            ((1<<SPECIAL_SHIFT) - 1)
+#define STATE_ALL                               ( PD_RS232_S_MASK | PD_S_MASK )
+#define FLOW_RX_AUTO                            ( PD_RS232_A_RFR | PD_RS232_A_DTR | PD_RS232_A_RXO )
+#define FLOW_TX_AUTO                            ( PD_RS232_A_CTS | PD_RS232_A_DSR | PD_RS232_A_TXO | PD_RS232_A_DCD )
+#define CAN_BE_AUTO                             ( FLOW_RX_AUTO | FLOW_TX_AUTO )
+#define CAN_NOTIFY                              ( PD_RS232_N_MASK )
+#define EXTERNAL_MASK                           ( PD_S_MASK | (PD_RS232_S_MASK & ~PD_RS232_S_LOOP) )
+#define INTERNAL_DELAY                          ( PD_RS232_S_LOOP )
+#define DEFAULT_AUTO                            ( PD_RS232_A_DTR | PD_RS232_A_RFR | PD_RS232_A_CTS | PD_RS232_A_DSR )
+#define DEFAULT_NOTIFY                          0x00
+#define DEFAULT_STATE                           ( PD_S_TX_ENABLE | PD_S_RX_ENABLE | PD_RS232_A_TXO | PD_RS232_A_RXO )
+
+#define CONTINUE_SEND                           1
+#define PAUSE_SEND                              2
+
+#define kRxAutoFlow                             ((UInt32)( PD_RS232_A_RFR | PD_RS232_A_DTR | PD_RS232_A_RXO ))
+#define kTxAutoFlow                             ((UInt32)( PD_RS232_A_CTS | PD_RS232_A_DSR | PD_RS232_A_TXO | PD_RS232_A_DCD ))
+#define kControl_StateMask                      ((UInt32)( PD_RS232_S_CTS | PD_RS232_S_DSR | PD_RS232_S_CAR | PD_RS232_S_RI  ))
+#define kRxQueueState                           ((UInt32)( PD_S_RXQ_EMPTY | PD_S_RXQ_LOW_WATER | PD_S_RXQ_HIGH_WATER | PD_S_RXQ_FULL ))
+#define kTxQueueState                           ((UInt32)( PD_S_TXQ_EMPTY | PD_S_TXQ_LOW_WATER | PD_S_TXQ_HIGH_WATER | PD_S_TXQ_FULL ))
+
+#define kCONTROL_DTR                            0x01
+#define kCONTROL_RTS                            0x02
+
+#define kStateTransientMask                     0x74
+#define kBreakError                             0x04
+#define kFrameError                             0x10
+#define kParityError                            0x20
+#define kOverrunError                           0x40
+
+#define kCTS                                    0x80
+#define kDSR                                    0x02
+#define kRI                                     0x08
+#define kDCD                                    0x01
+#define kHandshakeInMask                        ((UInt32)( PD_RS232_S_CTS | PD_RS232_S_DSR | PD_RS232_S_CAR | PD_RS232_S_RI  ))
+
+#define VENDOR_WRITE_REQUEST_TYPE               0x40
+#define VENDOR_WRITE_REQUEST                    0x01
+
+#define VENDOR_READ_REQUEST_TYPE                0xc0
+#define VENDOR_READ_REQUEST                     0x01
+
+// Device Configuration Registers (DCR0, DCR1, DCR2)
+#define SET_DCR0                                0x00
+#define GET_DCR0                                0x80
+#define DCR0_INIT                               0x01
+#define DCR0_INIT_H                             0x41
+#define DCR0_INIT_X                             0x61
+
+#define SET_DCR1                                0x01
+#define GET_DCR1                                0x81
+#define DCR1_INIT_H                             0x80
+#define DCR1_INIT_X                             0x00
+
+#define SET_DCR2                                0x02
+#define GET_DCR2                                0x82
+#define DCR2_INIT_H                             0x24
+#define DCR2_INIT_X                             0x44
+
+// On-chip Data Buffers:
+#define RESET_DOWNSTREAM_DATA_PIPE              0x08
+#define RESET_UPSTREAM_DATA_PIPE                0x09
+
+
+#define PL_MAX_ENDPOINTS                        4
+
+enum tXO_State {
+        kXOnSent = -2,
+        kXOffSent = -1,
+        kXO_Idle = 0,
+        kXOffNeeded = 1,
+        kXOnNeeded = 2
+};
+
+enum pl2303_type {
+        unknown,
+        type_0, /* don't know the difference between type 0 and */
+        type_1, /* type 1, until someone from prolific tells us... */
+        rev_X,
+        rev_HX, /* HX version of the pl2303 chip */
+        rev_H
+};
+
+
+class PL2303 : public ACM {
+        uint16_t wPLType; // Type of chip
+
+public:
+        PL2303(USB *pusb, CDCAsyncOper *pasync);
+
+        // USBDeviceConfig implementation
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        //virtual uint8_t Release();
+        //virtual uint8_t Poll();
+        //virtual uint8_t GetAddress() { return bAddress; };
+
+        //// UsbConfigXtracter implementation
+        //virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+
+#ifdef PL2303_COMPAT
+private:
+        /* Prolific proprietary requests */
+        uint8_t vendorRead( uint8_t val_lo, uint8_t val_hi, uint16_t index, uint8_t* buf );
+        uint8_t vendorWrite( uint8_t val_lo, uint8_t val_hi, uint8_t index );
+#endif
+};
+
+#ifdef PL2303_COMPAT
+/* vendor read request */
+inline uint8_t PL2303::vendorRead( uint8_t val_lo, uint8_t val_hi, uint16_t index, uint8_t* buf )
+{
+        return( pUsb->ctrlReq(bAddress, 0, VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, val_lo, val_hi, index, 1, 1, buf, NULL ));
+}
+
+/* vendor write request */
+inline uint8_t PL2303::vendorWrite( uint8_t val_lo, uint8_t val_hi, uint8_t index )
+{
+        return( pUsb->ctrlReq(bAddress, 0, VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, val_lo, val_hi, index, 0, 0, NULL, NULL ));
+}
+#endif
+
+#endif // __CDCPROLIFIC_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/confdescparser.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,218 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(_usb_h_) || defined(__CONFDESCPARSER_H__)
+#error "Never include confdescparser.h directly; include Usb.h instead"
+#else
+
+#define __CONFDESCPARSER_H__
+
+class UsbConfigXtracter {
+public:
+        //virtual void ConfigXtract(const USB_CONFIGURATION_DESCRIPTOR *conf) = 0;
+        //virtual void InterfaceXtract(uint8_t conf, const USB_INTERFACE_DESCRIPTOR *iface) = 0;
+
+        virtual void EndpointXtract(uint8_t conf __attribute__((unused)), uint8_t iface __attribute__((unused)), uint8_t alt __attribute__((unused)), uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR *ep __attribute__((unused))) {
+        };
+};
+
+#define CP_MASK_COMPARE_CLASS                   1
+#define CP_MASK_COMPARE_SUBCLASS                2
+#define CP_MASK_COMPARE_PROTOCOL                4
+#define CP_MASK_COMPARE_ALL                     7
+
+// Configuration Descriptor Parser Class Template
+
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+class ConfigDescParser : public USBReadParser {
+        UsbConfigXtracter *theXtractor;
+        MultiValueBuffer theBuffer;
+        MultiByteValueParser valParser;
+        ByteSkipper theSkipper;
+        uint8_t varBuffer[16 /*sizeof(USB_CONFIGURATION_DESCRIPTOR)*/];
+
+        uint8_t stateParseDescr; // ParseDescriptor state
+
+        uint8_t dscrLen; // Descriptor length
+        uint8_t dscrType; // Descriptor type
+
+        bool isGoodInterface; // Apropriate interface flag
+        uint8_t confValue; // Configuration value
+        uint8_t protoValue; // Protocol value
+        uint8_t ifaceNumber; // Interface number
+        uint8_t ifaceAltSet; // Interface alternate settings
+
+        bool UseOr;
+        bool ParseDescriptor(uint8_t **pp, uint16_t *pcntdn);
+        void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc);
+
+public:
+
+        void SetOR(void) {
+                UseOr = true;
+        }
+        ConfigDescParser(UsbConfigXtracter *xtractor);
+        void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset);
+};
+
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ConfigDescParser(UsbConfigXtracter *xtractor) :
+theXtractor(xtractor),
+stateParseDescr(0),
+dscrLen(0),
+dscrType(0),
+UseOr(false) {
+        theBuffer.pValue = varBuffer;
+        valParser.Initialize(&theBuffer);
+        theSkipper.Initialize(&theBuffer);
+};
+
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset __attribute__((unused))) {
+        uint16_t cntdn = (uint16_t)len;
+        uint8_t *p = (uint8_t*)pbuf;
+
+        while(cntdn)
+                if(!ParseDescriptor(&p, &cntdn))
+                        return;
+}
+
+/* Parser for the configuration descriptor. Takes values for class, subclass, protocol fields in interface descriptor and
+  compare masks for them. When the match is found, calls EndpointXtract passing buffer containing endpoint descriptor */
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+bool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor(uint8_t **pp, uint16_t *pcntdn) {
+        USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR*>(varBuffer);
+        USB_INTERFACE_DESCRIPTOR* uid = reinterpret_cast<USB_INTERFACE_DESCRIPTOR*>(varBuffer);
+        switch(stateParseDescr) {
+                case 0:
+                        theBuffer.valueSize = 2;
+                        valParser.Initialize(&theBuffer);
+                        stateParseDescr = 1;
+                case 1:
+                        if(!valParser.Parse(pp, pcntdn))
+                                return false;
+                        dscrLen = *((uint8_t*)theBuffer.pValue);
+                        dscrType = *((uint8_t*)theBuffer.pValue + 1);
+                        stateParseDescr = 2;
+                case 2:
+                        // This is a sort of hack. Assuming that two bytes are all ready in the buffer
+                        //      the pointer is positioned two bytes ahead in order for the rest of descriptor
+                        //      to be read right after the size and the type fields.
+                        // This should be used carefully. varBuffer should be used directly to handle data
+                        //      in the buffer.
+                        theBuffer.pValue = varBuffer + 2;
+                        stateParseDescr = 3;
+                case 3:
+                        switch(dscrType) {
+                                case USB_DESCRIPTOR_INTERFACE:
+                                        isGoodInterface = false;
+                                        break;
+                                case USB_DESCRIPTOR_CONFIGURATION:
+                                case USB_DESCRIPTOR_ENDPOINT:
+                                case HID_DESCRIPTOR_HID:
+                                        break;
+                        }
+                        theBuffer.valueSize = dscrLen - 2;
+                        valParser.Initialize(&theBuffer);
+                        stateParseDescr = 4;
+                case 4:
+                        switch(dscrType) {
+                                case USB_DESCRIPTOR_CONFIGURATION:
+                                        if(!valParser.Parse(pp, pcntdn))
+                                                return false;
+                                        confValue = ucd->bConfigurationValue;
+                                        break;
+                                case USB_DESCRIPTOR_INTERFACE:
+                                        if(!valParser.Parse(pp, pcntdn))
+                                                return false;
+                                        if((MASK & CP_MASK_COMPARE_CLASS) && uid->bInterfaceClass != CLASS_ID)
+                                                break;
+                                        if((MASK & CP_MASK_COMPARE_SUBCLASS) && uid->bInterfaceSubClass != SUBCLASS_ID)
+                                                break;
+                                        if(UseOr) {
+                                                if((!((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol)))
+                                                        break;
+                                        } else {
+                                                if((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol != PROTOCOL_ID)
+                                                        break;
+                                        }
+                                        isGoodInterface = true;
+                                        ifaceNumber = uid->bInterfaceNumber;
+                                        ifaceAltSet = uid->bAlternateSetting;
+                                        protoValue = uid->bInterfaceProtocol;
+                                        break;
+                                case USB_DESCRIPTOR_ENDPOINT:
+                                        if(!valParser.Parse(pp, pcntdn))
+                                                return false;
+                                        if(isGoodInterface)
+                                                if(theXtractor)
+                                                        theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer);
+                                        break;
+                                        //case HID_DESCRIPTOR_HID:
+                                        //      if (!valParser.Parse(pp, pcntdn))
+                                        //              return false;
+                                        //      PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer);
+                                        //      break;
+                                default:
+                                        if(!theSkipper.Skip(pp, pcntdn, dscrLen - 2))
+                                                return false;
+                        }
+                        theBuffer.pValue = varBuffer;
+                        stateParseDescr = 0;
+        }
+        return true;
+}
+
+template <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>
+void ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) {
+        Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"), 0x80);
+        Notify(PSTR("bDescLength:\t\t"), 0x80);
+        PrintHex<uint8_t > (pDesc->bLength, 0x80);
+
+        Notify(PSTR("\r\nbDescriptorType:\t"), 0x80);
+        PrintHex<uint8_t > (pDesc->bDescriptorType, 0x80);
+
+        Notify(PSTR("\r\nbcdHID:\t\t\t"), 0x80);
+        PrintHex<uint16_t > (pDesc->bcdHID, 0x80);
+
+        Notify(PSTR("\r\nbCountryCode:\t\t"), 0x80);
+        PrintHex<uint8_t > (pDesc->bCountryCode, 0x80);
+
+        Notify(PSTR("\r\nbNumDescriptors:\t"), 0x80);
+        PrintHex<uint8_t > (pDesc->bNumDescriptors, 0x80);
+
+        for(uint8_t i = 0; i < pDesc->bNumDescriptors; i++) {
+                HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType);
+
+                Notify(PSTR("\r\nbDescrType:\t\t"), 0x80);
+                PrintHex<uint8_t > (pLT[i].bDescrType, 0x80);
+
+                Notify(PSTR("\r\nwDescriptorLength:\t"), 0x80);
+                PrintHex<uint16_t > (pLT[i].wDescriptorLength, 0x80);
+        }
+        Notify(PSTR("\r\n"), 0x80);
+}
+
+
+#endif // __CONFDESCPARSER_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/controllerEnums.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,211 @@
+/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _controllerenums_h
+#define _controllerenums_h
+
+#if defined(ESP32)
+#undef PS
+#endif
+
+/**
+ * This header file is used to store different enums for the controllers,
+ * This is necessary so all the different libraries can be used at once.
+ */
+
+/** Enum used to turn on the LEDs on the different controllers. */
+enum LEDEnum {
+        OFF = 0,
+#ifndef RBL_NRF51822
+        CONTROLLER_LED1 = 1,
+        CONTROLLER_LED2 = 2,
+        CONTROLLER_LED3 = 3,
+        CONTROLLER_LED4 = 4,
+#endif
+        CONTROLLER_LED5 = 5,
+        CONTROLLER_LED6 = 6,
+        CONTROLLER_LED7 = 7,
+        CONTROLLER_LED8 = 8,
+        CONTROLLER_LED9 = 9,
+        CONTROLLER_LED10 = 10,
+        /** Used to blink all LEDs on the Xbox controller */
+        ALL = 5,
+};
+
+/** Used to set the colors of the Move and PS4 controller. */
+enum ColorsEnum {
+        /** r = 255, g = 0, b = 0 */
+        Red = 0xFF0000,
+        /** r = 0, g = 255, b = 0 */
+        Green = 0xFF00,
+        /** r = 0, g = 0, b = 255 */
+        Blue = 0xFF,
+
+        /** r = 255, g = 235, b = 4 */
+        Yellow = 0xFFEB04,
+        /** r = 0, g = 255, b = 255 */
+        Lightblue = 0xFFFF,
+        /** r = 255, g = 0, b = 255 */
+        Purple = 0xFF00FF,
+        Purble = 0xFF00FF,
+
+        /** r = 255, g = 255, b = 255 */
+        White = 0xFFFFFF,
+        /** r = 0, g = 0, b = 0 */
+        Off = 0x00,
+};
+
+enum RumbleEnum {
+        RumbleHigh = 0x10,
+        RumbleLow = 0x20,
+};
+
+/** This enum is used to read all the different buttons on the different controllers */
+enum ButtonEnum {
+        /**@{*/
+        /** These buttons are available on all the the controllers */
+        UP = 0,
+        RIGHT = 1,
+        DOWN = 2,
+        LEFT = 3,
+        /**@}*/
+
+        /**@{*/
+        /** Wii buttons */
+        PLUS = 5,
+        TWO = 6,
+        ONE = 7,
+        MINUS = 8,
+        HOME = 9,
+        Z = 10,
+        C = 11,
+        B = 12,
+        A = 13,
+        /**@}*/
+
+        /**@{*/
+        /** These are only available on the Wii U Pro Controller */
+        L = 16,
+        R = 17,
+        ZL = 18,
+        ZR = 19,
+        /**@}*/
+
+        /**@{*/
+        /** PS3 controllers buttons */
+        SELECT = 4,
+        START = 5,
+        L3 = 6,
+        R3 = 7,
+
+        L2 = 8,
+        R2 = 9,
+        L1 = 10,
+        R1 = 11,
+        TRIANGLE = 12,
+        CIRCLE = 13,
+        CROSS = 14,
+        SQUARE = 15,
+
+        PS = 16,
+
+        MOVE = 17, // Covers 12 bits - we only need to read the top 8
+        T = 18, // Covers 12 bits - we only need to read the top 8
+        /**@}*/
+
+        /** PS4 controllers buttons - SHARE and OPTIONS are present instead of SELECT and START */
+        SHARE = 4,
+        OPTIONS = 5,
+        TOUCHPAD = 17,
+        /**@}*/
+
+        /**@{*/
+        /** Xbox buttons */
+        BACK = 4,
+        X = 14,
+        Y = 15,
+        XBOX = 16,
+        SYNC = 17,
+        BLACK = 8, // Available on the original Xbox controller
+        WHITE = 9, // Available on the original Xbox controller
+        /**@}*/
+
+        /** PS Buzz controllers */
+        RED = 0,
+        YELLOW = 1,
+        GREEN = 2,
+        ORANGE = 3,
+        BLUE = 4,
+        /**@}*/
+};
+
+/** Joysticks on the PS3 and Xbox controllers. */
+enum AnalogHatEnum {
+        /** Left joystick x-axis */
+        LeftHatX = 0,
+        /** Left joystick y-axis */
+        LeftHatY = 1,
+        /** Right joystick x-axis */
+        RightHatX = 2,
+        /** Right joystick y-axis */
+        RightHatY = 3,
+};
+
+/**
+ * Sensors inside the Sixaxis Dualshock 3, Move controller and PS4 controller.
+ * <B>Note:</B> that the location is shifted 9 when it's connected via USB on the PS3 controller.
+ */
+enum SensorEnum {
+        /** Accelerometer values */
+        aX = 50, aY = 52, aZ = 54,
+        /** Gyro z-axis */
+        gZ = 56,
+        gX, gY, // These are not available on the PS3 controller
+
+        /** Accelerometer x-axis */
+        aXmove = 28,
+        /** Accelerometer z-axis */
+        aZmove = 30,
+        /** Accelerometer y-axis */
+        aYmove = 32,
+
+        /** Gyro x-axis */
+        gXmove = 40,
+        /** Gyro z-axis */
+        gZmove = 42,
+        /** Gyro y-axis */
+        gYmove = 44,
+
+        /** Temperature sensor */
+        tempMove = 46,
+
+        /** Magnetometer x-axis */
+        mXmove = 47,
+        /** Magnetometer z-axis */
+        mZmove = 49,
+        /** Magnetometer y-axis */
+        mYmove = 50,
+};
+
+/** Used to get the angle calculated using the PS3 controller and PS4 controller. */
+enum AngleEnum {
+        Pitch = 0x01,
+        Roll = 0x02,
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hexdump.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,70 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(_usb_h_) || defined(__HEXDUMP_H__)
+#error "Never include hexdump.h directly; include Usb.h instead"
+#else
+#define __HEXDUMP_H__
+
+extern int UsbDEBUGlvl;
+
+template <class BASE_CLASS, class LEN_TYPE, class OFFSET_TYPE>
+class HexDumper : public BASE_CLASS {
+        uint8_t byteCount;
+        OFFSET_TYPE byteTotal;
+
+public:
+
+        HexDumper() : byteCount(0), byteTotal(0) {
+        };
+
+        void Initialize() {
+                byteCount = 0;
+                byteTotal = 0;
+        };
+
+        void Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset);
+};
+
+template <class BASE_CLASS, class LEN_TYPE, class OFFSET_TYPE>
+void HexDumper<BASE_CLASS, LEN_TYPE, OFFSET_TYPE>::Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset __attribute__((unused))) {
+        if(UsbDEBUGlvl >= 0x80) { // Fully bypass this block of code if we do not debug.
+                for(LEN_TYPE j = 0; j < len; j++, byteCount++, byteTotal++) {
+                        if(!byteCount) {
+                                PrintHex<OFFSET_TYPE > (byteTotal, 0x80);
+                                E_Notify(PSTR(": "), 0x80);
+                        }
+                        PrintHex<uint8_t > (pbuf[j], 0x80);
+                        E_Notify(PSTR(" "), 0x80);
+
+                        if(byteCount == 15) {
+                                E_Notify(PSTR("\r\n"), 0x80);
+                                byteCount = 0xFF;
+                        }
+                }
+        }
+}
+
+#endif // __HEXDUMP_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hidboot.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,202 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#include "hidboot.h"
+
+void MouseReportParser::Parse(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len __attribute__((unused)), uint8_t *buf) {
+        MOUSEINFO *pmi = (MOUSEINFO*)buf;
+        // Future:
+        // bool event;
+
+#if 0
+        if (prevState.mouseInfo.bmLeftButton == 0 && pmi->bmLeftButton == 1)
+                OnLeftButtonDown(pmi);
+
+        if (prevState.mouseInfo.bmLeftButton == 1 && pmi->bmLeftButton == 0)
+                OnLeftButtonUp(pmi);
+
+        if (prevState.mouseInfo.bmRightButton == 0 && pmi->bmRightButton == 1)
+                OnRightButtonDown(pmi);
+
+        if (prevState.mouseInfo.bmRightButton == 1 && pmi->bmRightButton == 0)
+                OnRightButtonUp(pmi);
+
+        if (prevState.mouseInfo.bmMiddleButton == 0 && pmi->bmMiddleButton == 1)
+                OnMiddleButtonDown(pmi);
+
+        if (prevState.mouseInfo.bmMiddleButton == 1 && pmi->bmMiddleButton == 0)
+                OnMiddleButtonUp(pmi);
+
+        if (prevState.mouseInfo.dX != pmi->dX || prevState.mouseInfo.dY != pmi->dY)
+                OnMouseMove(pmi);
+
+        if (len > sizeof (MOUSEINFO))
+                for (uint8_t i = 0; i<sizeof (MOUSEINFO); i++)
+                        prevState.bInfo[i] = buf[i];
+#else
+        //
+        // Optimization idea:
+        //
+        // 1: Don't pass the structure on every event. Buttons would not need it.
+        // 2: Only pass x/y values in the movement routine.
+        //
+        // These two changes (with the ones I have made) will save extra flash.
+        // The only "bad" thing is that it could break old code.
+        //
+        // Future thoughts:
+        //
+        // The extra space gained can be used for a generic mouse event that can be called
+        // when there are _ANY_ changes. This one you _MAY_ want to pass everything, however the
+        // sketch could already have noted these facts to support drag/drop scroll wheel stuff, etc.
+        //
+
+        // Why do we need to pass the structure for buttons?
+        // The function call not enough of a hint for what is happening?
+        if(prevState.mouseInfo.bmLeftButton != pmi->bmLeftButton ) {
+                if(pmi->bmLeftButton) {
+                        OnLeftButtonDown(pmi);
+                } else {
+                        OnLeftButtonUp(pmi);
+                }
+                // Future:
+                // event = true;
+        }
+
+        if(prevState.mouseInfo.bmRightButton != pmi->bmRightButton) {
+                if(pmi->bmRightButton) {
+                        OnRightButtonDown(pmi);
+                } else {
+                        OnRightButtonUp(pmi);
+                }
+                // Future:
+                // event = true;
+        }
+
+        if(prevState.mouseInfo.bmMiddleButton != pmi->bmMiddleButton) {
+                if(pmi->bmMiddleButton) {
+                        OnMiddleButtonDown(pmi);
+                } else {
+                        OnMiddleButtonUp(pmi);
+                }
+                // Future:
+                // event = true;
+        }
+
+        //
+        // Scroll wheel(s), are not part of the spec, but we could support it.
+        // Logitech wireless keyboard and mouse combo reports scroll wheel in byte 4
+        // We wouldn't even need to save this information.
+        //if(len > 3) {
+        //}
+        //
+
+        // Mice only report motion when they actually move!
+        // Why not just pass the x/y values to simplify things??
+        if(pmi->dX || pmi->dY) {
+                OnMouseMove(pmi);
+                // Future:
+                // event = true;
+        }
+
+        //
+        // Future:
+        // Provide a callback that operates on the gathered events from above.
+        //
+        // if(event) OnMouse();
+        //
+
+        // Only the first byte matters (buttons). We do NOT need to save position info.
+        prevState.bInfo[0] = buf[0];
+#endif
+
+};
+
+void KeyboardReportParser::Parse(USBHID *hid, bool is_rpt_id __attribute__((unused)), uint8_t len __attribute__((unused)), uint8_t *buf) {
+        // On error - return
+        if (buf[2] == 1)
+                return;
+
+        //KBDINFO       *pki = (KBDINFO*)buf;
+
+        // provide event for changed control key state
+        if (prevState.bInfo[0x00] != buf[0x00]) {
+                OnControlKeysChanged(prevState.bInfo[0x00], buf[0x00]);
+        }
+
+        for (uint8_t i = 2; i < 8; i++) {
+                bool down = false;
+                bool up = false;
+
+                for (uint8_t j = 2; j < 8; j++) {
+                        if (buf[i] == prevState.bInfo[j] && buf[i] != 1)
+                                down = true;
+                        if (buf[j] == prevState.bInfo[i] && prevState.bInfo[i] != 1)
+                                up = true;
+                }
+                if (!down) {
+                        HandleLockingKeys(hid, buf[i]);
+                        OnKeyDown(*buf, buf[i]);
+                }
+                if (!up)
+                        OnKeyUp(prevState.bInfo[0], prevState.bInfo[i]);
+        }
+        for (uint8_t i = 0; i < 8; i++)
+                prevState.bInfo[i] = buf[i];
+};
+
+const uint8_t KeyboardReportParser::numKeys[10] PROGMEM = {'!', '@', '#', '$', '%', '^', '&', '*', '(', ')'};
+const uint8_t KeyboardReportParser::symKeysUp[12] PROGMEM = {'_', '+', '{', '}', '|', '~', ':', '"', '~', '<', '>', '?'};
+const uint8_t KeyboardReportParser::symKeysLo[12] PROGMEM = {'-', '=', '[', ']', '\\', ' ', ';', '\'', '`', ',', '.', '/'};
+const uint8_t KeyboardReportParser::padKeys[5] PROGMEM = {'/', '*', '-', '+', '\r'};
+
+uint8_t KeyboardReportParser::OemToAscii(uint8_t mod, uint8_t key) {
+        uint8_t shift = (mod & 0x22);
+
+        // [a-z]
+        if (VALUE_WITHIN(key, 0x04, 0x1d)) {
+                // Upper case letters
+                if ((kbdLockingKeys.kbdLeds.bmCapsLock == 0 && shift) ||
+                        (kbdLockingKeys.kbdLeds.bmCapsLock == 1 && shift == 0))
+                        return (key - 4 + 'A');
+
+                        // Lower case letters
+                else
+                        return (key - 4 + 'a');
+        }// Numbers
+        else if (VALUE_WITHIN(key, 0x1e, 0x27)) {
+                if (shift)
+                        return ((uint8_t)pgm_read_byte(&getNumKeys()[key - 0x1e]));
+                else
+                        return ((key == UHS_HID_BOOT_KEY_ZERO) ? '0' : key - 0x1e + '1');
+        }// Keypad Numbers
+        else if(VALUE_WITHIN(key, 0x59, 0x61)) {
+                if(kbdLockingKeys.kbdLeds.bmNumLock == 1)
+                        return (key - 0x59 + '1');
+        } else if(VALUE_WITHIN(key, 0x2d, 0x38))
+                return ((shift) ? (uint8_t)pgm_read_byte(&getSymKeysUp()[key - 0x2d]) : (uint8_t)pgm_read_byte(&getSymKeysLo()[key - 0x2d]));
+        else if(VALUE_WITHIN(key, 0x54, 0x58))
+                return (uint8_t)pgm_read_byte(&getPadKeys()[key - 0x54]);
+        else {
+                switch(key) {
+                        case UHS_HID_BOOT_KEY_SPACE: return (0x20);
+                        case UHS_HID_BOOT_KEY_ENTER: return ('\r'); // Carriage return (0x0D)
+                        case UHS_HID_BOOT_KEY_ZERO2: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '0': 0);
+                        case UHS_HID_BOOT_KEY_PERIOD: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '.': 0);
+                }
+        }
+        return ( 0);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hidboot.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,628 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__HIDBOOT_H__)
+#define __HIDBOOT_H__
+
+#include "usbhid.h"
+
+#define UHS_HID_BOOT_KEY_ZERO           0x27
+#define UHS_HID_BOOT_KEY_ENTER          0x28
+#define UHS_HID_BOOT_KEY_SPACE          0x2c
+#define UHS_HID_BOOT_KEY_CAPS_LOCK      0x39
+#define UHS_HID_BOOT_KEY_SCROLL_LOCK    0x47
+#define UHS_HID_BOOT_KEY_NUM_LOCK       0x53
+#define UHS_HID_BOOT_KEY_ZERO2          0x62
+#define UHS_HID_BOOT_KEY_PERIOD         0x63
+
+// Don't worry, GCC will optimize the result to a final value.
+#define bitsEndpoints(p) ((((p) & USB_HID_PROTOCOL_KEYBOARD)? 2 : 0) | (((p) & USB_HID_PROTOCOL_MOUSE)? 1 : 0))
+#define totalEndpoints(p) ((bitsEndpoints(p) == 3) ? 3 : 2)
+#define epMUL(p) ((((p) & USB_HID_PROTOCOL_KEYBOARD)? 1 : 0) + (((p) & USB_HID_PROTOCOL_MOUSE)? 1 : 0))
+
+// Already defined in hid.h
+// #define HID_MAX_HID_CLASS_DESCRIPTORS 5
+
+struct MOUSEINFO {
+
+        struct {
+                uint8_t bmLeftButton : 1;
+                uint8_t bmRightButton : 1;
+                uint8_t bmMiddleButton : 1;
+                uint8_t bmDummy : 5;
+        };
+        int8_t dX;
+        int8_t dY;
+};
+
+class MouseReportParser : public HIDReportParser {
+
+        union {
+                MOUSEINFO mouseInfo;
+                uint8_t bInfo[sizeof (MOUSEINFO)];
+        } prevState;
+
+public:
+        void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
+
+protected:
+
+        virtual void OnMouseMove(MOUSEINFO *mi __attribute__((unused))) {
+        };
+
+        virtual void OnLeftButtonUp(MOUSEINFO *mi __attribute__((unused))) {
+        };
+
+        virtual void OnLeftButtonDown(MOUSEINFO *mi __attribute__((unused))) {
+        };
+
+        virtual void OnRightButtonUp(MOUSEINFO *mi __attribute__((unused))) {
+        };
+
+        virtual void OnRightButtonDown(MOUSEINFO *mi __attribute__((unused))) {
+        };
+
+        virtual void OnMiddleButtonUp(MOUSEINFO *mi __attribute__((unused))) {
+        };
+
+        virtual void OnMiddleButtonDown(MOUSEINFO *mi __attribute__((unused))) {
+        };
+};
+
+struct MODIFIERKEYS {
+        uint8_t bmLeftCtrl : 1;
+        uint8_t bmLeftShift : 1;
+        uint8_t bmLeftAlt : 1;
+        uint8_t bmLeftGUI : 1;
+        uint8_t bmRightCtrl : 1;
+        uint8_t bmRightShift : 1;
+        uint8_t bmRightAlt : 1;
+        uint8_t bmRightGUI : 1;
+};
+
+struct KBDINFO {
+
+        struct {
+                uint8_t bmLeftCtrl : 1;
+                uint8_t bmLeftShift : 1;
+                uint8_t bmLeftAlt : 1;
+                uint8_t bmLeftGUI : 1;
+                uint8_t bmRightCtrl : 1;
+                uint8_t bmRightShift : 1;
+                uint8_t bmRightAlt : 1;
+                uint8_t bmRightGUI : 1;
+        };
+        uint8_t bReserved;
+        uint8_t Keys[6];
+};
+
+struct KBDLEDS {
+        uint8_t bmNumLock : 1;
+        uint8_t bmCapsLock : 1;
+        uint8_t bmScrollLock : 1;
+        uint8_t bmCompose : 1;
+        uint8_t bmKana : 1;
+        uint8_t bmReserved : 3;
+};
+
+class KeyboardReportParser : public HIDReportParser {
+        static const uint8_t numKeys[10];
+        static const uint8_t symKeysUp[12];
+        static const uint8_t symKeysLo[12];
+        static const uint8_t padKeys[5];
+
+protected:
+
+        union {
+                KBDINFO kbdInfo;
+                uint8_t bInfo[sizeof (KBDINFO)];
+        } prevState;
+
+        union {
+                KBDLEDS kbdLeds;
+                uint8_t bLeds;
+        } kbdLockingKeys;
+
+        uint8_t OemToAscii(uint8_t mod, uint8_t key);
+
+public:
+
+        KeyboardReportParser() {
+                kbdLockingKeys.bLeds = 0;
+        };
+
+        void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
+
+protected:
+
+        virtual uint8_t HandleLockingKeys(USBHID* hid, uint8_t key) {
+                uint8_t old_keys = kbdLockingKeys.bLeds;
+
+                switch(key) {
+                        case UHS_HID_BOOT_KEY_NUM_LOCK:
+                                kbdLockingKeys.kbdLeds.bmNumLock = ~kbdLockingKeys.kbdLeds.bmNumLock;
+                                break;
+                        case UHS_HID_BOOT_KEY_CAPS_LOCK:
+                                kbdLockingKeys.kbdLeds.bmCapsLock = ~kbdLockingKeys.kbdLeds.bmCapsLock;
+                                break;
+                        case UHS_HID_BOOT_KEY_SCROLL_LOCK:
+                                kbdLockingKeys.kbdLeds.bmScrollLock = ~kbdLockingKeys.kbdLeds.bmScrollLock;
+                                break;
+                }
+
+                if(old_keys != kbdLockingKeys.bLeds && hid) {
+                        uint8_t lockLeds = kbdLockingKeys.bLeds;
+                        return (hid->SetReport(0, 0/*hid->GetIface()*/, 2, 0, 1, &lockLeds));
+                }
+
+                return 0;
+        };
+
+        virtual void OnControlKeysChanged(uint8_t before __attribute__((unused)), uint8_t after __attribute__((unused))) {
+        };
+
+        virtual void OnKeyDown(uint8_t mod __attribute__((unused)), uint8_t key __attribute__((unused))) {
+        };
+
+        virtual void OnKeyUp(uint8_t mod __attribute__((unused)), uint8_t key __attribute__((unused))) {
+        };
+
+        virtual const uint8_t *getNumKeys() {
+                return numKeys;
+        };
+
+        virtual const uint8_t *getSymKeysUp() {
+                return symKeysUp;
+        };
+
+        virtual const uint8_t *getSymKeysLo() {
+                return symKeysLo;
+        };
+
+        virtual const uint8_t *getPadKeys() {
+                return padKeys;
+        };
+};
+
+template <const uint8_t BOOT_PROTOCOL>
+class HIDBoot : public USBHID //public USBDeviceConfig, public UsbConfigXtracter
+{
+        EpInfo epInfo[totalEndpoints(BOOT_PROTOCOL)];
+        HIDReportParser *pRptParser[epMUL(BOOT_PROTOCOL)];
+
+        uint8_t bConfNum; // configuration number
+        uint8_t bIfaceNum; // Interface Number
+        uint8_t bNumIface; // number of interfaces in the configuration
+        uint8_t bNumEP; // total number of EP in the configuration
+        uint32_t qNextPollTime; // next poll time
+        bool bPollEnable; // poll enable flag
+        uint8_t bInterval; // largest interval
+        bool bRptProtoEnable; // Report Protocol enable flag
+
+        void Initialize();
+
+        virtual HIDReportParser* GetReportParser(uint8_t id) {
+                return pRptParser[id];
+        };
+
+public:
+        HIDBoot(USB *p, bool bRptProtoEnable = false);
+
+        virtual bool SetReportParser(uint8_t id, HIDReportParser *prs) {
+                pRptParser[id] = prs;
+                return true;
+        };
+
+        // USBDeviceConfig implementation
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t Release();
+        uint8_t Poll();
+
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        // UsbConfigXtracter implementation
+        // Method should be defined here if virtual.
+        virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+
+        virtual bool DEVCLASSOK(uint8_t klass) {
+                return (klass == USB_CLASS_HID);
+        }
+
+        virtual bool DEVSUBCLASSOK(uint8_t subklass) {
+                return (subklass == BOOT_PROTOCOL);
+        }
+};
+
+template <const uint8_t BOOT_PROTOCOL>
+HIDBoot<BOOT_PROTOCOL>::HIDBoot(USB *p, bool bRptProtoEnable/* = false*/) :
+USBHID(p),
+qNextPollTime(0),
+bPollEnable(false),
+bRptProtoEnable(bRptProtoEnable) {
+        Initialize();
+
+        for(int i = 0; i < epMUL(BOOT_PROTOCOL); i++) {
+                pRptParser[i] = NULL;
+        }
+        if(pUsb)
+                pUsb->RegisterDeviceClass(this);
+}
+
+template <const uint8_t BOOT_PROTOCOL>
+void HIDBoot<BOOT_PROTOCOL>::Initialize() {
+        for(int i = 0; i < totalEndpoints(BOOT_PROTOCOL); i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+        bNumEP = 1;
+        bNumIface = 0;
+        bConfNum = 0;
+}
+
+template <const uint8_t BOOT_PROTOCOL>
+uint8_t HIDBoot<BOOT_PROTOCOL>::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR* device;
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint8_t len = 0;
+        //uint16_t cd_len = 0;
+
+        uint8_t num_of_conf; // number of configurations
+        //uint8_t num_of_intf; // number of interfaces
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        USBTRACE("BM Init\r\n");
+        //USBTRACE2("totalEndpoints:", (uint8_t) (totalEndpoints(BOOT_PROTOCOL)));
+        //USBTRACE2("epMUL:", epMUL(BOOT_PROTOCOL));
+
+        if(bAddress)
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+        bInterval = 0;
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        if(!p->epinfo) {
+                USBTRACE("epinfo\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf);
+
+        if(!rcode)
+                len = (buf[0] > constBufSize) ? constBufSize : buf[0];
+
+        device = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+
+        if(rcode) {
+                // Restore p->epinfo
+                p->epinfo = oldep_ptr;
+
+                goto FailGetDevDescr;
+        }
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = (uint8_t)(device->bMaxPacketSize0);
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                USBTRACE2("setAddr:", rcode);
+                return rcode;
+        }
+        //delay(2); //per USB 2.0 sect.9.2.6.3
+
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        if(len)
+                rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf);
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        num_of_conf = device->bNumConfigurations;
+
+        USBTRACE2("NC:", num_of_conf);
+
+        // GCC will optimize unused stuff away.
+        if((BOOT_PROTOCOL & (USB_HID_PROTOCOL_KEYBOARD | USB_HID_PROTOCOL_MOUSE)) == (USB_HID_PROTOCOL_KEYBOARD | USB_HID_PROTOCOL_MOUSE)) {
+                USBTRACE("HID_PROTOCOL_KEYBOARD AND MOUSE\r\n");
+                ConfigDescParser<
+                        USB_CLASS_HID,
+                        HID_BOOT_INTF_SUBCLASS,
+                        USB_HID_PROTOCOL_KEYBOARD | USB_HID_PROTOCOL_MOUSE,
+                        CP_MASK_COMPARE_ALL > confDescrParser(this);
+                confDescrParser.SetOR(); // Use the OR variant.
+                for(uint8_t i = 0; i < num_of_conf; i++) {
+                        pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+                        if(bNumEP == (uint8_t)(totalEndpoints(BOOT_PROTOCOL)))
+                                break;
+                }
+        } else {
+                // GCC will optimize unused stuff away.
+                if(BOOT_PROTOCOL & USB_HID_PROTOCOL_KEYBOARD) {
+                        USBTRACE("HID_PROTOCOL_KEYBOARD\r\n");
+                        for(uint8_t i = 0; i < num_of_conf; i++) {
+                                ConfigDescParser<
+                                        USB_CLASS_HID,
+                                        HID_BOOT_INTF_SUBCLASS,
+                                        USB_HID_PROTOCOL_KEYBOARD,
+                                        CP_MASK_COMPARE_ALL> confDescrParserA(this);
+
+                                pUsb->getConfDescr(bAddress, 0, i, &confDescrParserA);
+                                if(bNumEP == (uint8_t)(totalEndpoints(BOOT_PROTOCOL)))
+                                        break;
+                        }
+                }
+
+                // GCC will optimize unused stuff away.
+                if(BOOT_PROTOCOL & USB_HID_PROTOCOL_MOUSE) {
+                        USBTRACE("HID_PROTOCOL_MOUSE\r\n");
+                        for(uint8_t i = 0; i < num_of_conf; i++) {
+                                ConfigDescParser<
+                                        USB_CLASS_HID,
+                                        HID_BOOT_INTF_SUBCLASS,
+                                        USB_HID_PROTOCOL_MOUSE,
+                                        CP_MASK_COMPARE_ALL> confDescrParserB(this);
+
+                                pUsb->getConfDescr(bAddress, 0, i, &confDescrParserB);
+                                if(bNumEP == ((uint8_t)(totalEndpoints(BOOT_PROTOCOL))))
+                                        break;
+
+                        }
+                }
+        }
+        USBTRACE2("bNumEP:", bNumEP);
+
+        if(bNumEP != (uint8_t)(totalEndpoints(BOOT_PROTOCOL))) {
+                rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+                goto Fail;
+        }
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+        //USBTRACE2("setEpInfoEntry returned ", rcode);
+        USBTRACE2("Cnf:", bConfNum);
+
+        delay(1000);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+        delay(1000);
+
+        USBTRACE2("bIfaceNum:", bIfaceNum);
+        USBTRACE2("bNumIface:", bNumIface);
+
+        // Yes, mouse wants SetProtocol and SetIdle too!
+        for(uint8_t i = 0; i < epMUL(BOOT_PROTOCOL); i++) {
+                USBTRACE2("\r\nInterface:", i);
+                rcode = SetProtocol(i, bRptProtoEnable ? HID_RPT_PROTOCOL : USB_HID_BOOT_PROTOCOL);
+                if(rcode) goto FailSetProtocol;
+                USBTRACE2("PROTOCOL SET HID_BOOT rcode:", rcode);
+                rcode = SetIdle(i, 0, 0);
+                USBTRACE2("SET_IDLE rcode:", rcode);
+                // if(rcode) goto FailSetIdle; This can fail.
+                // Get the RPIPE and just throw it away.
+                SinkParser<USBReadParser, uint16_t, uint16_t> sink;
+                rcode = GetReportDescr(i, &sink);
+                USBTRACE2("RPIPE rcode:", rcode);
+        }
+
+        // Get RPIPE and throw it away.
+
+        if(BOOT_PROTOCOL & USB_HID_PROTOCOL_KEYBOARD) {
+                // Wake keyboard interface by twinkling up to 5 LEDs that are in the spec.
+                // kana, compose, scroll, caps, num
+                rcode = 0x20; // Reuse rcode.
+                while(rcode) {
+                        rcode >>= 1;
+                        // Ignore any error returned, we don't care if LED is not supported
+                        SetReport(0, 0, 2, 0, 1, &rcode); // Eventually becomes zero (All off)
+                        delay(25);
+                }
+        }
+        USBTRACE("BM configured\r\n");
+
+        bPollEnable = true;
+        return 0;
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+        //FailSetDevTblEntry:
+        //#ifdef DEBUG_USB_HOST
+        //        NotifyFailSetDevTblEntry();
+        //        goto Fail;
+        //#endif
+
+        //FailGetConfDescr:
+        //#ifdef DEBUG_USB_HOST
+        //        NotifyFailGetConfDescr();
+        //        goto Fail;
+        //#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+        goto Fail;
+#endif
+
+FailSetProtocol:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("SetProto:");
+        goto Fail;
+#endif
+
+        //FailSetIdle:
+        //#ifdef DEBUG_USB_HOST
+        //        USBTRACE("SetIdle:");
+        //#endif
+
+Fail:
+#ifdef DEBUG_USB_HOST
+        NotifyFail(rcode);
+#endif
+        Release();
+
+        return rcode;
+}
+
+template <const uint8_t BOOT_PROTOCOL>
+void HIDBoot<BOOT_PROTOCOL>::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {
+
+        // If the first configuration satisfies, the others are not considered.
+        //if(bNumEP > 1 && conf != bConfNum)
+        if(bNumEP == totalEndpoints(BOOT_PROTOCOL))
+                return;
+
+        bConfNum = conf;
+        bIfaceNum = iface;
+
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT && (pep->bEndpointAddress & 0x80) == 0x80) {
+                if(pep->bInterval > bInterval) bInterval = pep->bInterval;
+
+                // Fill in the endpoint info structure
+                epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F);
+                epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+                epInfo[bNumEP].bmSndToggle = 0;
+                epInfo[bNumEP].bmRcvToggle = 0;
+                epInfo[bNumEP].bmNakPower = USB_NAK_NOWAIT;
+                bNumEP++;
+
+        }
+}
+
+template <const uint8_t BOOT_PROTOCOL>
+uint8_t HIDBoot<BOOT_PROTOCOL>::Release() {
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+
+        bConfNum = 0;
+        bIfaceNum = 0;
+        bNumEP = 1;
+        bAddress = 0;
+        qNextPollTime = 0;
+        bPollEnable = false;
+
+        return 0;
+}
+
+template <const uint8_t BOOT_PROTOCOL>
+uint8_t HIDBoot<BOOT_PROTOCOL>::Poll() {
+        uint8_t rcode = 0;
+
+        if(bPollEnable && ((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L)) {
+
+                // To-do: optimize manually, using the for loop only if needed.
+                for(int i = 0; i < epMUL(BOOT_PROTOCOL); i++) {
+                        const uint16_t const_buff_len = 16;
+                        uint8_t buf[const_buff_len];
+
+                        USBTRACE3("(hidboot.h) i=", i, 0x81);
+                        USBTRACE3("(hidboot.h) epInfo[epInterruptInIndex + i].epAddr=", epInfo[epInterruptInIndex + i].epAddr, 0x81);
+                        USBTRACE3("(hidboot.h) epInfo[epInterruptInIndex + i].maxPktSize=", epInfo[epInterruptInIndex + i].maxPktSize, 0x81);
+                        uint16_t read = (uint16_t)epInfo[epInterruptInIndex + i].maxPktSize;
+
+                        rcode = pUsb->inTransfer(bAddress, epInfo[epInterruptInIndex + i].epAddr, &read, buf);
+                        // SOME buggy dongles report extra keys (like sleep) using a 2 byte packet on the wrong endpoint.
+                        // Since keyboard and mice must report at least 3 bytes, we ignore the extra data.
+                        if(!rcode && read > 2) {
+                                if(pRptParser[i])
+                                        pRptParser[i]->Parse((USBHID*)this, 0, (uint8_t)read, buf);
+#ifdef DEBUG_USB_HOST
+                                // We really don't care about errors and anomalies unless we are debugging.
+                        } else {
+                                if(rcode != hrNAK) {
+                                        USBTRACE3("(hidboot.h) Poll:", rcode, 0x81);
+                                }
+                                if(!rcode && read) {
+                                        USBTRACE3("(hidboot.h) Strange read count: ", read, 0x80);
+                                        USBTRACE3("(hidboot.h) Interface:", i, 0x80);
+                                }
+                        }
+
+                        if(!rcode && read && (UsbDEBUGlvl > 0x7f)) {
+                                for(uint8_t i = 0; i < read; i++) {
+                                        PrintHex<uint8_t > (buf[i], 0x80);
+                                        USBTRACE1(" ", 0x80);
+                                }
+                                if(read)
+                                        USBTRACE1("\r\n", 0x80);
+#endif
+                        }
+
+                }
+                qNextPollTime = (uint32_t)millis() + bInterval;
+        }
+        return rcode;
+}
+
+#endif // __HIDBOOTMOUSE_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hidcomposite.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,416 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#include "hidcomposite.h"
+
+HIDComposite::HIDComposite(USB *p) :
+USBHID(p),
+qNextPollTime(0),
+pollInterval(0),
+bPollEnable(false),
+bHasReportId(false) {
+        Initialize();
+
+        if(pUsb)
+                pUsb->RegisterDeviceClass(this);
+}
+
+uint16_t HIDComposite::GetHidClassDescrLen(uint8_t type, uint8_t num) {
+        for(uint8_t i = 0, n = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) {
+                if(descrInfo[i].bDescrType == type) {
+                        if(n == num)
+                                return descrInfo[i].wDescriptorLength;
+                        n++;
+                }
+        }
+        return 0;
+}
+
+void HIDComposite::Initialize() {
+        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {
+                rptParsers[i].rptId = 0;
+                rptParsers[i].rptParser = NULL;
+        }
+        for(uint8_t i = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) {
+                descrInfo[i].bDescrType = 0;
+                descrInfo[i].wDescriptorLength = 0;
+        }
+        for(uint8_t i = 0; i < maxHidInterfaces; i++) {
+                hidInterfaces[i].bmInterface = 0;
+                hidInterfaces[i].bmProtocol = 0;
+
+                for(uint8_t j = 0; j < maxEpPerInterface; j++)
+                        hidInterfaces[i].epIndex[j] = 0;
+        }
+        for(uint8_t i = 0; i < totalEndpoints; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+        bNumEP = 1;
+        bNumIface = 0;
+        bConfNum = 0;
+        pollInterval = 0;
+}
+
+bool HIDComposite::SetReportParser(uint8_t id, HIDReportParser *prs) {
+        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {
+                if(rptParsers[i].rptId == 0 && rptParsers[i].rptParser == NULL) {
+                        rptParsers[i].rptId = id;
+                        rptParsers[i].rptParser = prs;
+                        return true;
+                }
+        }
+        return false;
+}
+
+HIDReportParser* HIDComposite::GetReportParser(uint8_t id) {
+        if(!bHasReportId)
+                return ((rptParsers[0].rptParser) ? rptParsers[0].rptParser : NULL);
+
+        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {
+                if(rptParsers[i].rptId == id)
+                        return rptParsers[i].rptParser;
+        }
+        return NULL;
+}
+
+uint8_t HIDComposite::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint8_t len = 0;
+
+        uint8_t num_of_conf; // number of configurations
+        //uint8_t num_of_intf; // number of interfaces
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        USBTRACE("HU Init\r\n");
+
+        if(bAddress)
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        if(!p->epinfo) {
+                USBTRACE("epinfo\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf);
+
+        if(!rcode)
+                len = (buf[0] > constBufSize) ? constBufSize : buf[0];
+
+        if(rcode) {
+                // Restore p->epinfo
+                p->epinfo = oldep_ptr;
+
+                goto FailGetDevDescr;
+        }
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                USBTRACE2("setAddr:", rcode);
+                return rcode;
+        }
+
+        //delay(2); //per USB 2.0 sect.9.2.6.3
+
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        if(len)
+                rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf);
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        VID = udd->idVendor; // Can be used by classes that inherits this class to check the VID and PID of the connected device
+        PID = udd->idProduct;
+
+        num_of_conf = udd->bNumConfigurations;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        USBTRACE2("NC:", num_of_conf);
+
+        for(uint8_t i = 0; i < num_of_conf; i++) {
+                //HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;
+                ConfigDescParser<USB_CLASS_HID, 0, 0,
+                        CP_MASK_COMPARE_CLASS> confDescrParser(this);
+
+                //rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                if(bNumEP > 1)
+                        break;
+        } // for
+
+        if(bNumEP < 2)
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+
+        USBTRACE2("Cnf:", bConfNum);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+        USBTRACE2("NumIface:", bNumIface);
+
+        for(uint8_t i = 0; i < bNumIface; i++) {
+                if(hidInterfaces[i].epIndex[epInterruptInIndex] == 0)
+                        continue;
+
+                USBTRACE2("SetIdle:", hidInterfaces[i].bmInterface);
+
+                rcode = SetIdle(hidInterfaces[i].bmInterface, 0, 0);
+
+                if(rcode && rcode != hrSTALL)
+                        goto FailSetIdle;
+        }
+
+        USBTRACE("HU configured\r\n");
+
+        OnInitSuccessful();
+
+        bPollEnable = true;
+        return 0;
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+        goto Fail;
+#endif
+
+
+FailSetIdle:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("SetIdle:");
+#endif
+
+#ifdef DEBUG_USB_HOST
+Fail:
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+HIDComposite::HIDInterface* HIDComposite::FindInterface(uint8_t iface, uint8_t alt, uint8_t proto) {
+        for(uint8_t i = 0; i < bNumIface && i < maxHidInterfaces; i++)
+                if(hidInterfaces[i].bmInterface == iface && hidInterfaces[i].bmAltSet == alt
+                        && hidInterfaces[i].bmProtocol == proto)
+                        return hidInterfaces + i;
+        return NULL;
+}
+
+void HIDComposite::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {
+        //ErrorMessage<uint8_t>(PSTR("\r\nConf.Val"), conf);
+        //ErrorMessage<uint8_t>(PSTR("Iface Num"), iface);
+        //ErrorMessage<uint8_t>(PSTR("Alt.Set"), alt);
+
+        bConfNum = conf;
+
+        uint8_t index = 0;
+        HIDInterface *piface = FindInterface(iface, alt, proto);
+
+        // Fill in interface structure in case of new interface
+        if(!piface) {
+                piface = hidInterfaces + bNumIface;
+                piface->bmInterface = iface;
+                piface->bmAltSet = alt;
+                piface->bmProtocol = proto;
+                bNumIface++;
+        }
+
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT)
+                index = (pep->bEndpointAddress & 0x80) == 0x80 ? epInterruptInIndex : epInterruptOutIndex;
+
+        if(!SelectInterface(iface, proto))
+                index = 0;
+
+        if(index) {
+                // Fill in the endpoint info structure
+                epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F);
+                epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+                epInfo[bNumEP].bmSndToggle = 0;
+                epInfo[bNumEP].bmRcvToggle = 0;
+                epInfo[bNumEP].bmNakPower = USB_NAK_NOWAIT;
+
+                // Fill in the endpoint index list
+                piface->epIndex[index] = bNumEP; //(pep->bEndpointAddress & 0x0F);
+
+                if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints
+                        pollInterval = pep->bInterval;
+
+                bNumEP++;
+        }
+}
+
+uint8_t HIDComposite::Release() {
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+
+        bNumEP = 1;
+        bAddress = 0;
+        qNextPollTime = 0;
+        bPollEnable = false;
+        return 0;
+}
+
+void HIDComposite::ZeroMemory(uint8_t len, uint8_t *buf) {
+        for(uint8_t i = 0; i < len; i++)
+                buf[i] = 0;
+}
+
+uint8_t HIDComposite::Poll() {
+        uint8_t rcode = 0;
+
+        if(!bPollEnable)
+                return 0;
+
+        if((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) {
+                qNextPollTime = (uint32_t)millis() + pollInterval;
+
+                uint8_t buf[constBuffLen];
+
+                for(uint8_t i = 0; i < bNumIface; i++) {
+                        uint8_t index = hidInterfaces[i].epIndex[epInterruptInIndex];
+
+                        if (index == 0)
+                            continue;
+
+                        uint16_t read = (uint16_t)epInfo[index].maxPktSize;
+
+                        ZeroMemory(constBuffLen, buf);
+
+                        uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[index].epAddr, &read, buf);
+
+                        if(rcode) {
+                                if(rcode != hrNAK)
+                                        USBTRACE3("(hidcomposite.h) Poll:", rcode, 0x81);
+                                continue;
+                        }
+
+                        if(read == 0)
+                            continue;
+
+                        if(read > constBuffLen)
+                                read = constBuffLen;
+
+#if 0
+                        Notify(PSTR("\r\nBuf: "), 0x80);
+
+                        for(uint8_t i = 0; i < read; i++) {
+                                D_PrintHex<uint8_t > (buf[i], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                        }
+
+                        Notify(PSTR("\r\n"), 0x80);
+#endif
+                        ParseHIDData(this, epInfo[index].epAddr, bHasReportId, (uint8_t)read, buf);
+
+                        HIDReportParser *prs = GetReportParser(((bHasReportId) ? *buf : 0));
+
+                        if(prs)
+                                prs->Parse(this, bHasReportId, (uint8_t)read, buf);
+                    }
+
+        }
+        return rcode;
+}
+
+// Send a report to interrupt out endpoint. This is NOT SetReport() request!
+uint8_t HIDComposite::SndRpt(uint16_t nbytes, uint8_t *dataptr) {
+        return pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, nbytes, dataptr);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hidcomposite.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,109 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(__HIDCOMPOSITE_H__)
+#define __HIDCOMPOSITE_H__
+
+#include "usbhid.h"
+//#include "hidescriptorparser.h"
+
+class HIDComposite : public USBHID {
+
+        struct ReportParser {
+                uint8_t rptId;
+                HIDReportParser *rptParser;
+        } rptParsers[MAX_REPORT_PARSERS];
+
+        // HID class specific descriptor type and length info obtained from HID descriptor
+        HID_CLASS_DESCRIPTOR_LEN_AND_TYPE descrInfo[HID_MAX_HID_CLASS_DESCRIPTORS];
+
+        // Returns HID class specific descriptor length by its type and order number
+        uint16_t GetHidClassDescrLen(uint8_t type, uint8_t num);
+
+        struct HIDInterface {
+                struct {
+                        uint8_t bmInterface : 3;
+                        uint8_t bmAltSet : 3;
+                        uint8_t bmProtocol : 2;
+                };
+                uint8_t epIndex[maxEpPerInterface];
+        };
+
+        uint8_t bConfNum; // configuration number
+        uint8_t bNumIface; // number of interfaces in the configuration
+        uint8_t bNumEP; // total number of EP in the configuration
+        uint32_t qNextPollTime; // next poll time
+        uint8_t pollInterval;
+        bool bPollEnable; // poll enable flag
+
+        static const uint16_t constBuffLen = 64; // event buffer length
+
+        void Initialize();
+        HIDInterface* FindInterface(uint8_t iface, uint8_t alt, uint8_t proto);
+
+        void ZeroMemory(uint8_t len, uint8_t *buf);
+
+protected:
+        EpInfo epInfo[totalEndpoints];
+        HIDInterface hidInterfaces[maxHidInterfaces];
+
+        bool bHasReportId;
+
+        uint16_t PID, VID; // PID and VID of connected device
+
+        // HID implementation
+        HIDReportParser* GetReportParser(uint8_t id);
+
+        virtual uint8_t OnInitSuccessful() {
+                return 0;
+        };
+
+        virtual void ParseHIDData(USBHID *hid __attribute__((unused)), uint8_t ep __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len __attribute__((unused)), uint8_t *buf __attribute__((unused))) {
+                return;
+        };
+
+public:
+        HIDComposite(USB *p);
+
+        // HID implementation
+        bool SetReportParser(uint8_t id, HIDReportParser *prs);
+
+        // USBDeviceConfig implementation
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t Release();
+        uint8_t Poll();
+
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        // UsbConfigXtracter implementation
+        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+
+        // Send report - do not mix with SetReport()!
+        uint8_t SndRpt(uint16_t nbytes, uint8_t *dataptr);
+
+        // Returns true if we should listen on an interface, false if not
+        virtual bool SelectInterface(uint8_t iface, uint8_t proto) = 0;
+};
+
+#endif // __HIDCOMPOSITE_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hidescriptorparser.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,1589 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#include "hidescriptorparser.h"
+
+const char * const ReportDescParserBase::usagePageTitles0[] PROGMEM = {
+        pstrUsagePageGenericDesktopControls,
+        pstrUsagePageSimulationControls,
+        pstrUsagePageVRControls,
+        pstrUsagePageSportControls,
+        pstrUsagePageGameControls,
+        pstrUsagePageGenericDeviceControls,
+        pstrUsagePageKeyboardKeypad,
+        pstrUsagePageLEDs,
+        pstrUsagePageButton,
+        pstrUsagePageOrdinal,
+        pstrUsagePageTelephone,
+        pstrUsagePageConsumer,
+        pstrUsagePageDigitizer,
+        pstrUsagePagePID,
+        pstrUsagePageUnicode
+};
+
+const char * const ReportDescParserBase::usagePageTitles1[] PROGMEM = {
+        pstrUsagePageBarCodeScanner,
+        pstrUsagePageScale,
+        pstrUsagePageMSRDevices,
+        pstrUsagePagePointOfSale,
+        pstrUsagePageCameraControl,
+        pstrUsagePageArcade
+};
+const char * const ReportDescParserBase::genDesktopTitles0[] PROGMEM = {
+        pstrUsagePointer,
+        pstrUsageMouse,
+        pstrUsageJoystick,
+        pstrUsageGamePad,
+        pstrUsageKeyboard,
+        pstrUsageKeypad,
+        pstrUsageMultiAxisController,
+        pstrUsageTabletPCSystemControls
+
+};
+const char * const ReportDescParserBase::genDesktopTitles1[] PROGMEM = {
+        pstrUsageX,
+        pstrUsageY,
+        pstrUsageZ,
+        pstrUsageRx,
+        pstrUsageRy,
+        pstrUsageRz,
+        pstrUsageSlider,
+        pstrUsageDial,
+        pstrUsageWheel,
+        pstrUsageHatSwitch,
+        pstrUsageCountedBuffer,
+        pstrUsageByteCount,
+        pstrUsageMotionWakeup,
+        pstrUsageStart,
+        pstrUsageSelect,
+        pstrUsagePageReserved,
+        pstrUsageVx,
+        pstrUsageVy,
+        pstrUsageVz,
+        pstrUsageVbrx,
+        pstrUsageVbry,
+        pstrUsageVbrz,
+        pstrUsageVno,
+        pstrUsageFeatureNotification,
+        pstrUsageResolutionMultiplier
+};
+const char * const ReportDescParserBase::genDesktopTitles2[] PROGMEM = {
+        pstrUsageSystemControl,
+        pstrUsageSystemPowerDown,
+        pstrUsageSystemSleep,
+        pstrUsageSystemWakeup,
+        pstrUsageSystemContextMenu,
+        pstrUsageSystemMainMenu,
+        pstrUsageSystemAppMenu,
+        pstrUsageSystemMenuHelp,
+        pstrUsageSystemMenuExit,
+        pstrUsageSystemMenuSelect,
+        pstrUsageSystemMenuRight,
+        pstrUsageSystemMenuLeft,
+        pstrUsageSystemMenuUp,
+        pstrUsageSystemMenuDown,
+        pstrUsageSystemColdRestart,
+        pstrUsageSystemWarmRestart,
+        pstrUsageDPadUp,
+        pstrUsageDPadDown,
+        pstrUsageDPadRight,
+        pstrUsageDPadLeft
+};
+const char * const ReportDescParserBase::genDesktopTitles3[] PROGMEM = {
+        pstrUsageSystemDock,
+        pstrUsageSystemUndock,
+        pstrUsageSystemSetup,
+        pstrUsageSystemBreak,
+        pstrUsageSystemDebuggerBreak,
+        pstrUsageApplicationBreak,
+        pstrUsageApplicationDebuggerBreak,
+        pstrUsageSystemSpeakerMute,
+        pstrUsageSystemHibernate
+};
+const char * const ReportDescParserBase::genDesktopTitles4[] PROGMEM = {
+        pstrUsageSystemDisplayInvert,
+        pstrUsageSystemDisplayInternal,
+        pstrUsageSystemDisplayExternal,
+        pstrUsageSystemDisplayBoth,
+        pstrUsageSystemDisplayDual,
+        pstrUsageSystemDisplayToggleIntExt,
+        pstrUsageSystemDisplaySwapPriSec,
+        pstrUsageSystemDisplayLCDAutoscale
+};
+const char * const ReportDescParserBase::simuTitles0[] PROGMEM = {
+        pstrUsageFlightSimulationDevice,
+        pstrUsageAutomobileSimulationDevice,
+        pstrUsageTankSimulationDevice,
+        pstrUsageSpaceshipSimulationDevice,
+        pstrUsageSubmarineSimulationDevice,
+        pstrUsageSailingSimulationDevice,
+        pstrUsageMotocicleSimulationDevice,
+        pstrUsageSportsSimulationDevice,
+        pstrUsageAirplaneSimulationDevice,
+        pstrUsageHelicopterSimulationDevice,
+        pstrUsageMagicCarpetSimulationDevice,
+        pstrUsageBicycleSimulationDevice
+};
+const char * const ReportDescParserBase::simuTitles1[] PROGMEM = {
+        pstrUsageFlightControlStick,
+        pstrUsageFlightStick,
+        pstrUsageCyclicControl,
+        pstrUsageCyclicTrim,
+        pstrUsageFlightYoke,
+        pstrUsageTrackControl
+};
+const char * const ReportDescParserBase::simuTitles2[] PROGMEM = {
+        pstrUsageAileron,
+        pstrUsageAileronTrim,
+        pstrUsageAntiTorqueControl,
+        pstrUsageAutopilotEnable,
+        pstrUsageChaffRelease,
+        pstrUsageCollectiveControl,
+        pstrUsageDiveBrake,
+        pstrUsageElectronicCountermeasures,
+        pstrUsageElevator,
+        pstrUsageElevatorTrim,
+        pstrUsageRudder,
+        pstrUsageThrottle,
+        pstrUsageFlightCommunications,
+        pstrUsageFlareRelease,
+        pstrUsageLandingGear,
+        pstrUsageToeBrake,
+        pstrUsageTrigger,
+        pstrUsageWeaponsArm,
+        pstrUsageWeaponsSelect,
+        pstrUsageWingFlaps,
+        pstrUsageAccelerator,
+        pstrUsageBrake,
+        pstrUsageClutch,
+        pstrUsageShifter,
+        pstrUsageSteering,
+        pstrUsageTurretDirection,
+        pstrUsageBarrelElevation,
+        pstrUsageDivePlane,
+        pstrUsageBallast,
+        pstrUsageBicycleCrank,
+        pstrUsageHandleBars,
+        pstrUsageFrontBrake,
+        pstrUsageRearBrake
+};
+const char * const ReportDescParserBase::vrTitles0[] PROGMEM = {
+        pstrUsageBelt,
+        pstrUsageBodySuit,
+        pstrUsageFlexor,
+        pstrUsageGlove,
+        pstrUsageHeadTracker,
+        pstrUsageHeadMountedDisplay,
+        pstrUsageHandTracker,
+        pstrUsageOculometer,
+        pstrUsageVest,
+        pstrUsageAnimatronicDevice
+};
+const char * const ReportDescParserBase::vrTitles1[] PROGMEM = {
+        pstrUsageStereoEnable,
+        pstrUsageDisplayEnable
+};
+const char * const ReportDescParserBase::sportsCtrlTitles0[] PROGMEM = {
+        pstrUsageBaseballBat,
+        pstrUsageGolfClub,
+        pstrUsageRowingMachine,
+        pstrUsageTreadmill
+};
+const char * const ReportDescParserBase::sportsCtrlTitles1[] PROGMEM = {
+        pstrUsageOar,
+        pstrUsageSlope,
+        pstrUsageRate,
+        pstrUsageStickSpeed,
+        pstrUsageStickFaceAngle,
+        pstrUsageStickHeelToe,
+        pstrUsageStickFollowThough,
+        pstrUsageStickTempo,
+        pstrUsageStickType,
+        pstrUsageStickHeight
+};
+const char * const ReportDescParserBase::sportsCtrlTitles2[] PROGMEM = {
+        pstrUsagePutter,
+        pstrUsage1Iron,
+        pstrUsage2Iron,
+        pstrUsage3Iron,
+        pstrUsage4Iron,
+        pstrUsage5Iron,
+        pstrUsage6Iron,
+        pstrUsage7Iron,
+        pstrUsage8Iron,
+        pstrUsage9Iron,
+        pstrUsage10Iron,
+        pstrUsage11Iron,
+        pstrUsageSandWedge,
+        pstrUsageLoftWedge,
+        pstrUsagePowerWedge,
+        pstrUsage1Wood,
+        pstrUsage3Wood,
+        pstrUsage5Wood,
+        pstrUsage7Wood,
+        pstrUsage9Wood
+};
+const char * const ReportDescParserBase::gameTitles0[] PROGMEM = {
+        pstrUsage3DGameController,
+        pstrUsagePinballDevice,
+        pstrUsageGunDevice
+};
+const char * const ReportDescParserBase::gameTitles1[] PROGMEM = {
+        pstrUsagePointOfView,
+        pstrUsageTurnRightLeft,
+        pstrUsagePitchForwardBackward,
+        pstrUsageRollRightLeft,
+        pstrUsageMoveRightLeft,
+        pstrUsageMoveForwardBackward,
+        pstrUsageMoveUpDown,
+        pstrUsageLeanRightLeft,
+        pstrUsageLeanForwardBackward,
+        pstrUsageHeightOfPOV,
+        pstrUsageFlipper,
+        pstrUsageSecondaryFlipper,
+        pstrUsageBump,
+        pstrUsageNewGame,
+        pstrUsageShootBall,
+        pstrUsagePlayer,
+        pstrUsageGunBolt,
+        pstrUsageGunClip,
+        pstrUsageGunSelector,
+        pstrUsageGunSingleShot,
+        pstrUsageGunBurst,
+        pstrUsageGunAutomatic,
+        pstrUsageGunSafety,
+        pstrUsageGamepadFireJump,
+        pstrUsageGamepadTrigger
+};
+const char * const ReportDescParserBase::genDevCtrlTitles[] PROGMEM = {
+        pstrUsageBatteryStrength,
+        pstrUsageWirelessChannel,
+        pstrUsageWirelessID,
+        pstrUsageDiscoverWirelessControl,
+        pstrUsageSecurityCodeCharEntered,
+        pstrUsageSecurityCodeCharErased,
+        pstrUsageSecurityCodeCleared
+};
+const char * const ReportDescParserBase::ledTitles[] PROGMEM = {
+        pstrUsageNumLock,
+        pstrUsageCapsLock,
+        pstrUsageScrollLock,
+        pstrUsageCompose,
+        pstrUsageKana,
+        pstrUsagePower,
+        pstrUsageShift,
+        pstrUsageDoNotDisturb,
+        pstrUsageMute,
+        pstrUsageToneEnable,
+        pstrUsageHighCutFilter,
+        pstrUsageLowCutFilter,
+        pstrUsageEqualizerEnable,
+        pstrUsageSoundFieldOn,
+        pstrUsageSurroundOn,
+        pstrUsageRepeat,
+        pstrUsageStereo,
+        pstrUsageSamplingRateDetect,
+        pstrUsageSpinning,
+        pstrUsageCAV,
+        pstrUsageCLV,
+        pstrUsageRecordingFormatDetect,
+        pstrUsageOffHook,
+        pstrUsageRing,
+        pstrUsageMessageWaiting,
+        pstrUsageDataMode,
+        pstrUsageBatteryOperation,
+        pstrUsageBatteryOK,
+        pstrUsageBatteryLow,
+        pstrUsageSpeaker,
+        pstrUsageHeadSet,
+        pstrUsageHold,
+        pstrUsageMicrophone,
+        pstrUsageCoverage,
+        pstrUsageNightMode,
+        pstrUsageSendCalls,
+        pstrUsageCallPickup,
+        pstrUsageConference,
+        pstrUsageStandBy,
+        pstrUsageCameraOn,
+        pstrUsageCameraOff,
+        pstrUsageOnLine,
+        pstrUsageOffLine,
+        pstrUsageBusy,
+        pstrUsageReady,
+        pstrUsagePaperOut,
+        pstrUsagePaperJam,
+        pstrUsageRemote,
+        pstrUsageForward,
+        pstrUsageReverse,
+        pstrUsageStop,
+        pstrUsageRewind,
+        pstrUsageFastForward,
+        pstrUsagePlay,
+        pstrUsagePause,
+        pstrUsageRecord,
+        pstrUsageError,
+        pstrUsageSelectedIndicator,
+        pstrUsageInUseIndicator,
+        pstrUsageMultiModeIndicator,
+        pstrUsageIndicatorOn,
+        pstrUsageIndicatorFlash,
+        pstrUsageIndicatorSlowBlink,
+        pstrUsageIndicatorFastBlink,
+        pstrUsageIndicatorOff,
+        pstrUsageFlashOnTime,
+        pstrUsageSlowBlinkOnTime,
+        pstrUsageSlowBlinkOffTime,
+        pstrUsageFastBlinkOnTime,
+        pstrUsageFastBlinkOffTime,
+        pstrUsageIndicatorColor,
+        pstrUsageIndicatorRed,
+        pstrUsageIndicatorGreen,
+        pstrUsageIndicatorAmber,
+        pstrUsageGenericIndicator,
+        pstrUsageSystemSuspend,
+        pstrUsageExternalPowerConnected
+};
+const char * const ReportDescParserBase::telTitles0 [] PROGMEM = {
+        pstrUsagePhone,
+        pstrUsageAnsweringMachine,
+        pstrUsageMessageControls,
+        pstrUsageHandset,
+        pstrUsageHeadset,
+        pstrUsageTelephonyKeyPad,
+        pstrUsageProgrammableButton
+};
+const char * const ReportDescParserBase::telTitles1 [] PROGMEM = {
+        pstrUsageHookSwitch,
+        pstrUsageFlash,
+        pstrUsageFeature,
+        pstrUsageHold,
+        pstrUsageRedial,
+        pstrUsageTransfer,
+        pstrUsageDrop,
+        pstrUsagePark,
+        pstrUsageForwardCalls,
+        pstrUsageAlternateFunction,
+        pstrUsageLine,
+        pstrUsageSpeakerPhone,
+        pstrUsageConference,
+        pstrUsageRingEnable,
+        pstrUsageRingSelect,
+        pstrUsagePhoneMute,
+        pstrUsageCallerID,
+        pstrUsageSend
+};
+const char * const ReportDescParserBase::telTitles2 [] PROGMEM = {
+        pstrUsageSpeedDial,
+        pstrUsageStoreNumber,
+        pstrUsageRecallNumber,
+        pstrUsagePhoneDirectory
+};
+const char * const ReportDescParserBase::telTitles3 [] PROGMEM = {
+        pstrUsageVoiceMail,
+        pstrUsageScreenCalls,
+        pstrUsageDoNotDisturb,
+        pstrUsageMessage,
+        pstrUsageAnswerOnOff
+};
+const char * const ReportDescParserBase::telTitles4 [] PROGMEM = {
+        pstrUsageInsideDialTone,
+        pstrUsageOutsideDialTone,
+        pstrUsageInsideRingTone,
+        pstrUsageOutsideRingTone,
+        pstrUsagePriorityRingTone,
+        pstrUsageInsideRingback,
+        pstrUsagePriorityRingback,
+        pstrUsageLineBusyTone,
+        pstrUsageReorderTone,
+        pstrUsageCallWaitingTone,
+        pstrUsageConfirmationTone1,
+        pstrUsageConfirmationTone2,
+        pstrUsageTonesOff,
+        pstrUsageOutsideRingback,
+        pstrUsageRinger
+};
+const char * const ReportDescParserBase::telTitles5 [] PROGMEM = {
+        pstrUsagePhoneKey0,
+        pstrUsagePhoneKey1,
+        pstrUsagePhoneKey2,
+        pstrUsagePhoneKey3,
+        pstrUsagePhoneKey4,
+        pstrUsagePhoneKey5,
+        pstrUsagePhoneKey6,
+        pstrUsagePhoneKey7,
+        pstrUsagePhoneKey8,
+        pstrUsagePhoneKey9,
+        pstrUsagePhoneKeyStar,
+        pstrUsagePhoneKeyPound,
+        pstrUsagePhoneKeyA,
+        pstrUsagePhoneKeyB,
+        pstrUsagePhoneKeyC,
+        pstrUsagePhoneKeyD
+};
+const char * const ReportDescParserBase::consTitles0[] PROGMEM = {
+        pstrUsageConsumerControl,
+        pstrUsageNumericKeyPad,
+        pstrUsageProgrammableButton,
+        pstrUsageMicrophone,
+        pstrUsageHeadphone,
+        pstrUsageGraphicEqualizer
+};
+const char * const ReportDescParserBase::consTitles1[] PROGMEM = {
+        pstrUsagePlus10,
+        pstrUsagePlus100,
+        pstrUsageAMPM
+};
+const char * const ReportDescParserBase::consTitles2[] PROGMEM = {
+        pstrUsagePower,
+        pstrUsageReset,
+        pstrUsageSleep,
+        pstrUsageSleepAfter,
+        pstrUsageSleepMode,
+        pstrUsageIllumination,
+        pstrUsageFunctionButtons
+
+};
+const char * const ReportDescParserBase::consTitles3[] PROGMEM = {
+        pstrUsageMenu,
+        pstrUsageMenuPick,
+        pstrUsageMenuUp,
+        pstrUsageMenuDown,
+        pstrUsageMenuLeft,
+        pstrUsageMenuRight,
+        pstrUsageMenuEscape,
+        pstrUsageMenuValueIncrease,
+        pstrUsageMenuValueDecrease
+};
+const char * const ReportDescParserBase::consTitles4[] PROGMEM = {
+        pstrUsageDataOnScreen,
+        pstrUsageClosedCaption,
+        pstrUsageClosedCaptionSelect,
+        pstrUsageVCRTV,
+        pstrUsageBroadcastMode,
+        pstrUsageSnapshot,
+        pstrUsageStill
+};
+const char * const ReportDescParserBase::consTitles5[] PROGMEM = {
+        pstrUsageSelection,
+        pstrUsageAssignSelection,
+        pstrUsageModeStep,
+        pstrUsageRecallLast,
+        pstrUsageEnterChannel,
+        pstrUsageOrderMovie,
+        pstrUsageChannel,
+        pstrUsageMediaSelection,
+        pstrUsageMediaSelectComputer,
+        pstrUsageMediaSelectTV,
+        pstrUsageMediaSelectWWW,
+        pstrUsageMediaSelectDVD,
+        pstrUsageMediaSelectTelephone,
+        pstrUsageMediaSelectProgramGuide,
+        pstrUsageMediaSelectVideoPhone,
+        pstrUsageMediaSelectGames,
+        pstrUsageMediaSelectMessages,
+        pstrUsageMediaSelectCD,
+        pstrUsageMediaSelectVCR,
+        pstrUsageMediaSelectTuner,
+        pstrUsageQuit,
+        pstrUsageHelp,
+        pstrUsageMediaSelectTape,
+        pstrUsageMediaSelectCable,
+        pstrUsageMediaSelectSatellite,
+        pstrUsageMediaSelectSecurity,
+        pstrUsageMediaSelectHome,
+        pstrUsageMediaSelectCall,
+        pstrUsageChannelIncrement,
+        pstrUsageChannelDecrement,
+        pstrUsageMediaSelectSAP,
+        pstrUsagePageReserved,
+        pstrUsageVCRPlus,
+        pstrUsageOnce,
+        pstrUsageDaily,
+        pstrUsageWeekly,
+        pstrUsageMonthly
+};
+const char * const ReportDescParserBase::consTitles6[] PROGMEM = {
+        pstrUsagePlay,
+        pstrUsagePause,
+        pstrUsageRecord,
+        pstrUsageFastForward,
+        pstrUsageRewind,
+        pstrUsageScanNextTrack,
+        pstrUsageScanPreviousTrack,
+        pstrUsageStop,
+        pstrUsageEject,
+        pstrUsageRandomPlay,
+        pstrUsageSelectDisk,
+        pstrUsageEnterDisk,
+        pstrUsageRepeat,
+        pstrUsageTracking,
+        pstrUsageTrackNormal,
+        pstrUsageSlowTracking,
+        pstrUsageFrameForward,
+        pstrUsageFrameBackwards,
+        pstrUsageMark,
+        pstrUsageClearMark,
+        pstrUsageRepeatFromMark,
+        pstrUsageReturnToMark,
+        pstrUsageSearchMarkForward,
+        pstrUsageSearchMarkBackwards,
+        pstrUsageCounterReset,
+        pstrUsageShowCounter,
+        pstrUsageTrackingIncrement,
+        pstrUsageTrackingDecrement,
+        pstrUsageStopEject,
+        pstrUsagePlayPause,
+        pstrUsagePlaySkip
+};
+const char * const ReportDescParserBase::consTitles7[] PROGMEM = {
+        pstrUsageVolume,
+        pstrUsageBalance,
+        pstrUsageMute,
+        pstrUsageBass,
+        pstrUsageTreble,
+        pstrUsageBassBoost,
+        pstrUsageSurroundMode,
+        pstrUsageLoudness,
+        pstrUsageMPX,
+        pstrUsageVolumeIncrement,
+        pstrUsageVolumeDecrement
+};
+const char * const ReportDescParserBase::consTitles8[] PROGMEM = {
+        pstrUsageSpeedSelect,
+        pstrUsagePlaybackSpeed,
+        pstrUsageStandardPlay,
+        pstrUsageLongPlay,
+        pstrUsageExtendedPlay,
+        pstrUsageSlow
+};
+const char * const ReportDescParserBase::consTitles9[] PROGMEM = {
+        pstrUsageFanEnable,
+        pstrUsageFanSpeed,
+        pstrUsageLightEnable,
+        pstrUsageLightIlluminationLevel,
+        pstrUsageClimateControlEnable,
+        pstrUsageRoomTemperature,
+        pstrUsageSecurityEnable,
+        pstrUsageFireAlarm,
+        pstrUsagePoliceAlarm,
+        pstrUsageProximity,
+        pstrUsageMotion,
+        pstrUsageDuresAlarm,
+        pstrUsageHoldupAlarm,
+        pstrUsageMedicalAlarm
+};
+const char * const ReportDescParserBase::consTitlesA[] PROGMEM = {
+        pstrUsageBalanceRight,
+        pstrUsageBalanceLeft,
+        pstrUsageBassIncrement,
+        pstrUsageBassDecrement,
+        pstrUsageTrebleIncrement,
+        pstrUsageTrebleDecrement
+};
+const char * const ReportDescParserBase::consTitlesB[] PROGMEM = {
+        pstrUsageSpeakerSystem,
+        pstrUsageChannelLeft,
+        pstrUsageChannelRight,
+        pstrUsageChannelCenter,
+        pstrUsageChannelFront,
+        pstrUsageChannelCenterFront,
+        pstrUsageChannelSide,
+        pstrUsageChannelSurround,
+        pstrUsageChannelLowFreqEnhancement,
+        pstrUsageChannelTop,
+        pstrUsageChannelUnknown
+};
+const char * const ReportDescParserBase::consTitlesC[] PROGMEM = {
+        pstrUsageSubChannel,
+        pstrUsageSubChannelIncrement,
+        pstrUsageSubChannelDecrement,
+        pstrUsageAlternateAudioIncrement,
+        pstrUsageAlternateAudioDecrement
+};
+const char * const ReportDescParserBase::consTitlesD[] PROGMEM = {
+        pstrUsageApplicationLaunchButtons,
+        pstrUsageALLaunchButtonConfigTool,
+        pstrUsageALProgrammableButton,
+        pstrUsageALConsumerControlConfig,
+        pstrUsageALWordProcessor,
+        pstrUsageALTextEditor,
+        pstrUsageALSpreadsheet,
+        pstrUsageALGraphicsEditor,
+        pstrUsageALPresentationApp,
+        pstrUsageALDatabaseApp,
+        pstrUsageALEmailReader,
+        pstrUsageALNewsreader,
+        pstrUsageALVoicemail,
+        pstrUsageALContactsAddressBook,
+        pstrUsageALCalendarSchedule,
+        pstrUsageALTaskProjectManager,
+        pstrUsageALLogJournalTimecard,
+        pstrUsageALCheckbookFinance,
+        pstrUsageALCalculator,
+        pstrUsageALAVCapturePlayback,
+        pstrUsageALLocalMachineBrowser,
+        pstrUsageALLANWANBrow,
+        pstrUsageALInternetBrowser,
+        pstrUsageALRemoteNetISPConnect,
+        pstrUsageALNetworkConference,
+        pstrUsageALNetworkChat,
+        pstrUsageALTelephonyDialer,
+        pstrUsageALLogon,
+        pstrUsageALLogoff,
+        pstrUsageALLogonLogoff,
+        pstrUsageALTermLockScrSav,
+        pstrUsageALControlPannel,
+        pstrUsageALCommandLineProcessorRun,
+        pstrUsageALProcessTaskManager,
+        pstrUsageALSelectTaskApplication,
+        pstrUsageALNextTaskApplication,
+        pstrUsageALPreviousTaskApplication,
+        pstrUsageALPreemptiveHaltTaskApp,
+        pstrUsageALIntegratedHelpCenter,
+        pstrUsageALDocuments,
+        pstrUsageALThesaurus,
+        pstrUsageALDictionary,
+        pstrUsageALDesktop,
+        pstrUsageALSpellCheck,
+        pstrUsageALGrammarCheck,
+        pstrUsageALWirelessStatus,
+        pstrUsageALKeyboardLayout,
+        pstrUsageALVirusProtection,
+        pstrUsageALEncryption,
+        pstrUsageALScreenSaver,
+        pstrUsageALAlarms,
+        pstrUsageALClock,
+        pstrUsageALFileBrowser,
+        pstrUsageALPowerStatus,
+        pstrUsageALImageBrowser,
+        pstrUsageALAudioBrowser,
+        pstrUsageALMovieBrowser,
+        pstrUsageALDigitalRightsManager,
+        pstrUsageALDigitalWallet,
+        pstrUsagePageReserved,
+        pstrUsageALInstantMessaging,
+        pstrUsageALOEMFeaturesBrowser,
+        pstrUsageALOEMHelp,
+        pstrUsageALOnlineCommunity,
+        pstrUsageALEntertainmentContentBrow,
+        pstrUsageALOnlineShoppingBrowser,
+        pstrUsageALSmartCardInfoHelp,
+        pstrUsageALMarketMonitorFinBrowser,
+        pstrUsageALCustomCorpNewsBrowser,
+        pstrUsageALOnlineActivityBrowser,
+        pstrUsageALResearchSearchBrowser,
+        pstrUsageALAudioPlayer
+};
+const char * const ReportDescParserBase::consTitlesE[] PROGMEM = {
+        pstrUsageGenericGUIAppControls,
+        pstrUsageACNew,
+        pstrUsageACOpen,
+        pstrUsageACClose,
+        pstrUsageACExit,
+        pstrUsageACMaximize,
+        pstrUsageACMinimize,
+        pstrUsageACSave,
+        pstrUsageACPrint,
+        pstrUsageACProperties,
+        pstrUsageACUndo,
+        pstrUsageACCopy,
+        pstrUsageACCut,
+        pstrUsageACPaste,
+        pstrUsageACSelectAll,
+        pstrUsageACFind,
+        pstrUsageACFindAndReplace,
+        pstrUsageACSearch,
+        pstrUsageACGoto,
+        pstrUsageACHome,
+        pstrUsageACBack,
+        pstrUsageACForward,
+        pstrUsageACStop,
+        pstrUsageACRefresh,
+        pstrUsageACPreviousLink,
+        pstrUsageACNextLink,
+        pstrUsageACBookmarks,
+        pstrUsageACHistory,
+        pstrUsageACSubscriptions,
+        pstrUsageACZoomIn,
+        pstrUsageACZoomOut,
+        pstrUsageACZoom,
+        pstrUsageACFullScreenView,
+        pstrUsageACNormalView,
+        pstrUsageACViewToggle,
+        pstrUsageACScrollUp,
+        pstrUsageACScrollDown,
+        pstrUsageACScroll,
+        pstrUsageACPanLeft,
+        pstrUsageACPanRight,
+        pstrUsageACPan,
+        pstrUsageACNewWindow,
+        pstrUsageACTileHoriz,
+        pstrUsageACTileVert,
+        pstrUsageACFormat,
+        pstrUsageACEdit,
+        pstrUsageACBold,
+        pstrUsageACItalics,
+        pstrUsageACUnderline,
+        pstrUsageACStrikethrough,
+        pstrUsageACSubscript,
+        pstrUsageACSuperscript,
+        pstrUsageACAllCaps,
+        pstrUsageACRotate,
+        pstrUsageACResize,
+        pstrUsageACFlipHorizontal,
+        pstrUsageACFlipVertical,
+        pstrUsageACMirrorHorizontal,
+        pstrUsageACMirrorVertical,
+        pstrUsageACFontSelect,
+        pstrUsageACFontColor,
+        pstrUsageACFontSize,
+        pstrUsageACJustifyLeft,
+        pstrUsageACJustifyCenterH,
+        pstrUsageACJustifyRight,
+        pstrUsageACJustifyBlockH,
+        pstrUsageACJustifyTop,
+        pstrUsageACJustifyCenterV,
+        pstrUsageACJustifyBottom,
+        pstrUsageACJustifyBlockV,
+        pstrUsageACIndentDecrease,
+        pstrUsageACIndentIncrease,
+        pstrUsageACNumberedList,
+        pstrUsageACRestartNumbering,
+        pstrUsageACBulletedList,
+        pstrUsageACPromote,
+        pstrUsageACDemote,
+        pstrUsageACYes,
+        pstrUsageACNo,
+        pstrUsageACCancel,
+        pstrUsageACCatalog,
+        pstrUsageACBuyChkout,
+        pstrUsageACAddToCart,
+        pstrUsageACExpand,
+        pstrUsageACExpandAll,
+        pstrUsageACCollapse,
+        pstrUsageACCollapseAll,
+        pstrUsageACPrintPreview,
+        pstrUsageACPasteSpecial,
+        pstrUsageACInsertMode,
+        pstrUsageACDelete,
+        pstrUsageACLock,
+        pstrUsageACUnlock,
+        pstrUsageACProtect,
+        pstrUsageACUnprotect,
+        pstrUsageACAttachComment,
+        pstrUsageACDeleteComment,
+        pstrUsageACViewComment,
+        pstrUsageACSelectWord,
+        pstrUsageACSelectSentence,
+        pstrUsageACSelectParagraph,
+        pstrUsageACSelectColumn,
+        pstrUsageACSelectRow,
+        pstrUsageACSelectTable,
+        pstrUsageACSelectObject,
+        pstrUsageACRedoRepeat,
+        pstrUsageACSort,
+        pstrUsageACSortAscending,
+        pstrUsageACSortDescending,
+        pstrUsageACFilter,
+        pstrUsageACSetClock,
+        pstrUsageACViewClock,
+        pstrUsageACSelectTimeZone,
+        pstrUsageACEditTimeZone,
+        pstrUsageACSetAlarm,
+        pstrUsageACClearAlarm,
+        pstrUsageACSnoozeAlarm,
+        pstrUsageACResetAlarm,
+        pstrUsageACSyncronize,
+        pstrUsageACSendReceive,
+        pstrUsageACSendTo,
+        pstrUsageACReply,
+        pstrUsageACReplyAll,
+        pstrUsageACForwardMessage,
+        pstrUsageACSend,
+        pstrUsageACAttachFile,
+        pstrUsageACUpload,
+        pstrUsageACDownload,
+        pstrUsageACSetBorders,
+        pstrUsageACInsertRow,
+        pstrUsageACInsertColumn,
+        pstrUsageACInsertFile,
+        pstrUsageACInsertPicture,
+        pstrUsageACInsertObject,
+        pstrUsageACInsertSymbol,
+        pstrUsageACSaveAndClose,
+        pstrUsageACRename,
+        pstrUsageACMerge,
+        pstrUsageACSplit,
+        pstrUsageACDistributeHorizontaly,
+        pstrUsageACDistributeVerticaly
+};
+const char * const ReportDescParserBase::digitTitles0[] PROGMEM = {
+        pstrUsageDigitizer,
+        pstrUsagePen,
+        pstrUsageLightPen,
+        pstrUsageTouchScreen,
+        pstrUsageTouchPad,
+        pstrUsageWhiteBoard,
+        pstrUsageCoordinateMeasuringMachine,
+        pstrUsage3DDigitizer,
+        pstrUsageStereoPlotter,
+        pstrUsageArticulatedArm,
+        pstrUsageArmature,
+        pstrUsageMultiplePointDigitizer,
+        pstrUsageFreeSpaceWand
+};
+const char * const ReportDescParserBase::digitTitles1[] PROGMEM = {
+        pstrUsageStylus,
+        pstrUsagePuck,
+        pstrUsageFinger
+
+};
+const char * const ReportDescParserBase::digitTitles2[] PROGMEM = {
+        pstrUsageTipPressure,
+        pstrUsageBarrelPressure,
+        pstrUsageInRange,
+        pstrUsageTouch,
+        pstrUsageUntouch,
+        pstrUsageTap,
+        pstrUsageQuality,
+        pstrUsageDataValid,
+        pstrUsageTransducerIndex,
+        pstrUsageTabletFunctionKeys,
+        pstrUsageProgramChangeKeys,
+        pstrUsageBatteryStrength,
+        pstrUsageInvert,
+        pstrUsageXTilt,
+        pstrUsageYTilt,
+        pstrUsageAzimuth,
+        pstrUsageAltitude,
+        pstrUsageTwist,
+        pstrUsageTipSwitch,
+        pstrUsageSecondaryTipSwitch,
+        pstrUsageBarrelSwitch,
+        pstrUsageEraser,
+        pstrUsageTabletPick
+};
+const char * const ReportDescParserBase::aplphanumTitles0[] PROGMEM = {
+        pstrUsageAlphanumericDisplay,
+        pstrUsageBitmappedDisplay
+};
+const char * const ReportDescParserBase::aplphanumTitles1[] PROGMEM = {
+        pstrUsageDisplayAttributesReport,
+        pstrUsageASCIICharacterSet,
+        pstrUsageDataReadBack,
+        pstrUsageFontReadBack,
+        pstrUsageDisplayControlReport,
+        pstrUsageClearDisplay,
+        pstrUsageDisplayEnable,
+        pstrUsageScreenSaverDelay,
+        pstrUsageScreenSaverEnable,
+        pstrUsageVerticalScroll,
+        pstrUsageHorizontalScroll,
+        pstrUsageCharacterReport,
+        pstrUsageDisplayData,
+        pstrUsageDisplayStatus,
+        pstrUsageStatusNotReady,
+        pstrUsageStatusReady,
+        pstrUsageErrorNotALoadableCharacter,
+        pstrUsageErrorFotDataCanNotBeRead,
+        pstrUsageCursorPositionReport,
+        pstrUsageRow,
+        pstrUsageColumn,
+        pstrUsageRows,
+        pstrUsageColumns,
+        pstrUsageCursorPixelPosition,
+        pstrUsageCursorMode,
+        pstrUsageCursorEnable,
+        pstrUsageCursorBlink,
+        pstrUsageFontReport,
+        pstrUsageFontData,
+        pstrUsageCharacterWidth,
+        pstrUsageCharacterHeight,
+        pstrUsageCharacterSpacingHorizontal,
+        pstrUsageCharacterSpacingVertical,
+        pstrUsageUnicodeCharset,
+        pstrUsageFont7Segment,
+        pstrUsage7SegmentDirectMap,
+        pstrUsageFont14Segment,
+        pstrUsage14SegmentDirectMap,
+        pstrUsageDisplayBrightness,
+        pstrUsageDisplayContrast,
+        pstrUsageCharacterAttribute,
+        pstrUsageAttributeReadback,
+        pstrUsageAttributeData,
+        pstrUsageCharAttributeEnhance,
+        pstrUsageCharAttributeUnderline,
+        pstrUsageCharAttributeBlink
+};
+const char * const ReportDescParserBase::aplphanumTitles2[] PROGMEM = {
+        pstrUsageBitmapSizeX,
+        pstrUsageBitmapSizeY,
+        pstrUsagePageReserved,
+        pstrUsageBitDepthFormat,
+        pstrUsageDisplayOrientation,
+        pstrUsagePaletteReport,
+        pstrUsagePaletteDataSize,
+        pstrUsagePaletteDataOffset,
+        pstrUsagePaletteData,
+        pstrUsageBlitReport,
+        pstrUsageBlitRectangleX1,
+        pstrUsageBlitRectangleY1,
+        pstrUsageBlitRectangleX2,
+        pstrUsageBlitRectangleY2,
+        pstrUsageBlitData,
+        pstrUsageSoftButton,
+        pstrUsageSoftButtonID,
+        pstrUsageSoftButtonSide,
+        pstrUsageSoftButtonOffset1,
+        pstrUsageSoftButtonOffset2,
+        pstrUsageSoftButtonReport
+};
+const char * const ReportDescParserBase::medInstrTitles0[] PROGMEM = {
+        pstrUsageVCRAcquisition,
+        pstrUsageFreezeThaw,
+        pstrUsageClipStore,
+        pstrUsageUpdate,
+        pstrUsageNext,
+        pstrUsageSave,
+        pstrUsagePrint,
+        pstrUsageMicrophoneEnable
+};
+const char * const ReportDescParserBase::medInstrTitles1[] PROGMEM = {
+        pstrUsageCine,
+        pstrUsageTransmitPower,
+        pstrUsageVolume,
+        pstrUsageFocus,
+        pstrUsageDepth
+};
+const char * const ReportDescParserBase::medInstrTitles2[] PROGMEM = {
+        pstrUsageSoftStepPrimary,
+        pstrUsageSoftStepSecondary
+};
+const char * const ReportDescParserBase::medInstrTitles3[] PROGMEM = {
+        pstrUsageZoomSelect,
+        pstrUsageZoomAdjust,
+        pstrUsageSpectralDopplerModeSelect,
+        pstrUsageSpectralDopplerModeAdjust,
+        pstrUsageColorDopplerModeSelect,
+        pstrUsageColorDopplerModeAdjust,
+        pstrUsageMotionModeSelect,
+        pstrUsageMotionModeAdjust,
+        pstrUsage2DModeSelect,
+        pstrUsage2DModeAdjust
+};
+const char * const ReportDescParserBase::medInstrTitles4[] PROGMEM = {
+        pstrUsageSoftControlSelect,
+        pstrUsageSoftControlAdjust
+};
+
+void ReportDescParserBase::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset __attribute__((unused))) {
+        uint16_t cntdn = (uint16_t)len;
+        uint8_t *p = (uint8_t*)pbuf;
+
+
+        totalSize = 0;
+
+        while(cntdn) {
+                //USB_HOST_SERIAL.println("");
+                //PrintHex<uint16_t>(offset + len - cntdn);
+                //USB_HOST_SERIAL.print(":");
+
+                ParseItem(&p, &cntdn);
+
+                //if (ParseItem(&p, &cntdn))
+                //        return;
+        }
+        //USBTRACE2("Total:", totalSize);
+}
+
+void ReportDescParserBase::PrintValue(uint8_t *p, uint8_t len) {
+        E_Notify(PSTR("("), 0x80);
+        for(; len; p++, len--)
+                PrintHex<uint8_t > (*p, 0x80);
+        E_Notify(PSTR(")"), 0x80);
+}
+
+void ReportDescParserBase::PrintByteValue(uint8_t data) {
+        E_Notify(PSTR("("), 0x80);
+        PrintHex<uint8_t > (data, 0x80);
+        E_Notify(PSTR(")"), 0x80);
+}
+
+void ReportDescParserBase::PrintItemTitle(uint8_t prefix) {
+        switch(prefix & (TYPE_MASK | TAG_MASK)) {
+                case (TYPE_GLOBAL | TAG_GLOBAL_PUSH):
+                        E_Notify(PSTR("\r\nPush"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_POP):
+                        E_Notify(PSTR("\r\nPop"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE):
+                        E_Notify(PSTR("\r\nUsage Page"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMIN):
+                        E_Notify(PSTR("\r\nLogical Min"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMAX):
+                        E_Notify(PSTR("\r\nLogical Max"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMIN):
+                        E_Notify(PSTR("\r\nPhysical Min"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMAX):
+                        E_Notify(PSTR("\r\nPhysical Max"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_UNITEXP):
+                        E_Notify(PSTR("\r\nUnit Exp"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_UNIT):
+                        E_Notify(PSTR("\r\nUnit"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE):
+                        E_Notify(PSTR("\r\nReport Size"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT):
+                        E_Notify(PSTR("\r\nReport Count"), 0x80);
+                        break;
+                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID):
+                        E_Notify(PSTR("\r\nReport Id"), 0x80);
+                        break;
+                case (TYPE_LOCAL | TAG_LOCAL_USAGE):
+                        E_Notify(PSTR("\r\nUsage"), 0x80);
+                        break;
+                case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN):
+                        E_Notify(PSTR("\r\nUsage Min"), 0x80);
+                        break;
+                case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX):
+                        E_Notify(PSTR("\r\nUsage Max"), 0x80);
+                        break;
+                case (TYPE_MAIN | TAG_MAIN_COLLECTION):
+                        E_Notify(PSTR("\r\nCollection"), 0x80);
+                        break;
+                case (TYPE_MAIN | TAG_MAIN_ENDCOLLECTION):
+                        E_Notify(PSTR("\r\nEnd Collection"), 0x80);
+                        break;
+                case (TYPE_MAIN | TAG_MAIN_INPUT):
+                        E_Notify(PSTR("\r\nInput"), 0x80);
+                        break;
+                case (TYPE_MAIN | TAG_MAIN_OUTPUT):
+                        E_Notify(PSTR("\r\nOutput"), 0x80);
+                        break;
+                case (TYPE_MAIN | TAG_MAIN_FEATURE):
+                        E_Notify(PSTR("\r\nFeature"), 0x80);
+                        break;
+        } // switch (**pp & (TYPE_MASK | TAG_MASK))
+}
+
+uint8_t ReportDescParserBase::ParseItem(uint8_t **pp, uint16_t *pcntdn) {
+        //uint8_t ret = enErrorSuccess;
+        //reinterpret_cast<>(varBuffer);
+        switch(itemParseState) {
+                case 0:
+                        if(**pp == HID_LONG_ITEM_PREFIX)
+                                USBTRACE("\r\nLONG\r\n");
+                        else {
+                                uint8_t size = ((**pp) & DATA_SIZE_MASK);
+
+                                itemPrefix = (**pp);
+                                itemSize = 1 + ((size == DATA_SIZE_4) ? 4 : size);
+
+                                PrintItemTitle(itemPrefix);
+                        }
+                        (*pp)++;
+                        (*pcntdn)--;
+                        itemSize--;
+                        itemParseState = 1;
+
+                        if(!itemSize)
+                                break;
+
+                        if(!pcntdn)
+                                return enErrorIncomplete;
+                case 1:
+                        //USBTRACE2("\r\niSz:",itemSize);
+
+                        theBuffer.valueSize = itemSize;
+                        valParser.Initialize(&theBuffer);
+                        itemParseState = 2;
+                case 2:
+                        if(!valParser.Parse(pp, pcntdn))
+                                return enErrorIncomplete;
+                        itemParseState = 3;
+                case 3:
+                {
+                        uint8_t data = *((uint8_t*)varBuffer);
+
+                        switch(itemPrefix & (TYPE_MASK | TAG_MASK)) {
+                                case (TYPE_LOCAL | TAG_LOCAL_USAGE):
+                                        if(pfUsage) {
+                                                if(theBuffer.valueSize > 1) {
+                                                        uint16_t* ui16 = reinterpret_cast<uint16_t *>(varBuffer);
+                                                        pfUsage(*ui16);
+                                                } else
+                                                        pfUsage(data);
+                                        }
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE):
+                                        rptSize = data;
+                                        PrintByteValue(data);
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT):
+                                        rptCount = data;
+                                        PrintByteValue(data);
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMIN):
+                                case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMAX):
+                                case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMIN):
+                                case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMAX):
+                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID):
+                                case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN):
+                                case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX):
+                                case (TYPE_GLOBAL | TAG_GLOBAL_UNITEXP):
+                                case (TYPE_GLOBAL | TAG_GLOBAL_UNIT):
+                                        PrintValue(varBuffer, theBuffer.valueSize);
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_PUSH):
+                                case (TYPE_GLOBAL | TAG_GLOBAL_POP):
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE):
+                                        SetUsagePage(data);
+                                        PrintUsagePage(data);
+                                        PrintByteValue(data);
+                                        break;
+                                case (TYPE_MAIN | TAG_MAIN_COLLECTION):
+                                case (TYPE_MAIN | TAG_MAIN_ENDCOLLECTION):
+                                        switch(data) {
+                                                case 0x00:
+                                                        E_Notify(PSTR(" Physical"), 0x80);
+                                                        break;
+                                                case 0x01:
+                                                        E_Notify(PSTR(" Application"), 0x80);
+                                                        break;
+                                                case 0x02:
+                                                        E_Notify(PSTR(" Logical"), 0x80);
+                                                        break;
+                                                case 0x03:
+                                                        E_Notify(PSTR(" Report"), 0x80);
+                                                        break;
+                                                case 0x04:
+                                                        E_Notify(PSTR(" Named Array"), 0x80);
+                                                        break;
+                                                case 0x05:
+                                                        E_Notify(PSTR(" Usage Switch"), 0x80);
+                                                        break;
+                                                case 0x06:
+                                                        E_Notify(PSTR(" Usage Modifier"), 0x80);
+                                                        break;
+                                                default:
+                                                        E_Notify(PSTR(" Vendor Defined("), 0x80);
+                                                        PrintHex<uint8_t > (data, 0x80);
+                                                        E_Notify(PSTR(")"), 0x80);
+                                        }
+                                        break;
+                                case (TYPE_MAIN | TAG_MAIN_INPUT):
+                                case (TYPE_MAIN | TAG_MAIN_OUTPUT):
+                                case (TYPE_MAIN | TAG_MAIN_FEATURE):
+                                        totalSize += (uint16_t)rptSize * (uint16_t)rptCount;
+                                        rptSize = 0;
+                                        rptCount = 0;
+                                        E_Notify(PSTR("("), 0x80);
+                                        PrintBin<uint8_t > (data, 0x80);
+                                        E_Notify(PSTR(")"), 0x80);
+                                        break;
+                        } // switch (**pp & (TYPE_MASK | TAG_MASK))
+                }
+        } // switch (itemParseState)
+        itemParseState = 0;
+        return enErrorSuccess;
+}
+
+ReportDescParserBase::UsagePageFunc ReportDescParserBase::usagePageFunctions[] /*PROGMEM*/ = {
+        &ReportDescParserBase::PrintGenericDesktopPageUsage,
+        &ReportDescParserBase::PrintSimulationControlsPageUsage,
+        &ReportDescParserBase::PrintVRControlsPageUsage,
+        &ReportDescParserBase::PrintSportsControlsPageUsage,
+        &ReportDescParserBase::PrintGameControlsPageUsage,
+        &ReportDescParserBase::PrintGenericDeviceControlsPageUsage,
+        NULL, // Keyboard/Keypad
+        &ReportDescParserBase::PrintLEDPageUsage,
+        &ReportDescParserBase::PrintButtonPageUsage,
+        &ReportDescParserBase::PrintOrdinalPageUsage,
+        &ReportDescParserBase::PrintTelephonyPageUsage,
+        &ReportDescParserBase::PrintConsumerPageUsage,
+        &ReportDescParserBase::PrintDigitizerPageUsage,
+        NULL, // Reserved
+        NULL, // PID
+        NULL // Unicode
+};
+
+void ReportDescParserBase::SetUsagePage(uint16_t page) {
+        pfUsage = NULL;
+
+        if(VALUE_BETWEEN(page, 0x00, 0x11)) {
+                pfUsage = (usagePageFunctions[page - 1]);
+
+        } else {
+                switch(page) {
+                        case 0x14:
+                                pfUsage = &ReportDescParserBase::PrintAlphanumDisplayPageUsage;
+                                break;
+                        case 0x40:
+                                pfUsage = &ReportDescParserBase::PrintMedicalInstrumentPageUsage;
+                                break;
+                }
+        }
+}
+
+void ReportDescParserBase::PrintUsagePage(uint16_t page) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(page, 0x00, 0x11, w, E_Notify, usagePageTitles0, 0x80)
+        else output_if_between(page, 0x8b, 0x92, w, E_Notify, usagePageTitles1, 0x80)
+        else if(VALUE_BETWEEN(page, 0x7f, 0x84))
+                E_Notify(pstrUsagePageMonitor, 0x80);
+        else if(VALUE_BETWEEN(page, 0x83, 0x8c))
+                E_Notify(pstrUsagePagePower, 0x80);
+        else if(page > 0xfeff /* && page <= 0xffff */)
+                E_Notify(pstrUsagePageVendorDefined, 0x80);
+        else
+                switch(page) {
+                        case 0x14:
+                                E_Notify(pstrUsagePageAlphaNumericDisplay, 0x80);
+                                break;
+                        case 0x40:
+                                E_Notify(pstrUsagePageMedicalInstruments, 0x80);
+                                break;
+                        default:
+                                E_Notify(pstrUsagePageUndefined, 0x80);
+                }
+}
+
+void ReportDescParserBase::PrintButtonPageUsage(uint16_t usage) {
+        E_Notify(pstrSpace, 0x80);
+        E_Notify(PSTR("Btn"), 0x80);
+        PrintHex<uint16_t > (usage, 0x80);
+        E_Notify(PSTR("\r\n"), 0x80);
+        //USB_HOST_SERIAL.print(usage, HEX);
+}
+
+void ReportDescParserBase::PrintOrdinalPageUsage(uint16_t usage) {
+        E_Notify(pstrSpace, 0x80);
+        E_Notify(PSTR("Inst"), 0x80);
+        // Sorry, HEX for now...
+        PrintHex<uint16_t > (usage, 0x80);
+        E_Notify(PSTR("\r\n"), 0x80);
+        //USB_HOST_SERIAL.print(usage, DEC);
+}
+
+void ReportDescParserBase::PrintGenericDesktopPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x0a, w, E_Notify, genDesktopTitles0, 0x80)
+        else output_if_between(usage, 0x2f, 0x49, w, E_Notify, genDesktopTitles1, 0x80)
+        else output_if_between(usage, 0x7f, 0x94, w, E_Notify, genDesktopTitles2, 0x80)
+        else output_if_between(usage, 0x9f, 0xa9, w, E_Notify, genDesktopTitles3, 0x80)
+        else output_if_between(usage, 0xaf, 0xb8, w, E_Notify, genDesktopTitles4, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintSimulationControlsPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x0d, w, E_Notify, simuTitles0, 0x80)
+        else output_if_between(usage, 0x1f, 0x26, w, E_Notify, simuTitles1, 0x80)
+        else output_if_between(usage, 0xaf, 0xd1, w, E_Notify, simuTitles2, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintVRControlsPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x0b, w, E_Notify, vrTitles0, 0x80)
+        else output_if_between(usage, 0x1f, 0x22, w, E_Notify, vrTitles1, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintSportsControlsPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x05, w, E_Notify, sportsCtrlTitles0, 0x80)
+        else output_if_between(usage, 0x2f, 0x3a, w, E_Notify, sportsCtrlTitles1, 0x80)
+        else output_if_between(usage, 0x4f, 0x64, w, E_Notify, sportsCtrlTitles2, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintGameControlsPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x04, w, E_Notify, gameTitles0, 0x80)
+        else output_if_between(usage, 0x1f, 0x3a, w, E_Notify, gameTitles1, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintGenericDeviceControlsPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x1f, 0x27, w, E_Notify, genDevCtrlTitles, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintLEDPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x4e, w, E_Notify, ledTitles, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintTelephonyPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x08, w, E_Notify, telTitles0, 0x80)
+        else output_if_between(usage, 0x1f, 0x32, w, E_Notify, telTitles1, 0x80)
+        else output_if_between(usage, 0x4f, 0x54, w, E_Notify, telTitles2, 0x80)
+        else output_if_between(usage, 0x6f, 0x75, w, E_Notify, telTitles3, 0x80)
+        else output_if_between(usage, 0x8f, 0x9f, w, E_Notify, telTitles4, 0x80)
+        else output_if_between(usage, 0xaf, 0xc0, w, E_Notify, telTitles5, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintConsumerPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x07, w, E_Notify, consTitles0, 0x80)
+        else output_if_between(usage, 0x1f, 0x23, w, E_Notify, consTitles1, 0x80)
+        else output_if_between(usage, 0x2f, 0x37, w, E_Notify, consTitles2, 0x80)
+        else output_if_between(usage, 0x3f, 0x49, w, E_Notify, consTitles3, 0x80)
+        else output_if_between(usage, 0x5f, 0x67, w, E_Notify, consTitles4, 0x80)
+        else output_if_between(usage, 0x7f, 0xa5, w, E_Notify, consTitles5, 0x80)
+        else output_if_between(usage, 0xaf, 0xcf, w, E_Notify, consTitles6, 0x80)
+        else output_if_between(usage, 0xdf, 0xeb, w, E_Notify, consTitles7, 0x80)
+        else output_if_between(usage, 0xef, 0xf6, w, E_Notify, consTitles8, 0x80)
+        else output_if_between(usage, 0xff, 0x10e, w, E_Notify, consTitles9, 0x80)
+        else output_if_between(usage, 0x14f, 0x156, w, E_Notify, consTitlesA, 0x80)
+        else output_if_between(usage, 0x15f, 0x16b, w, E_Notify, consTitlesB, 0x80)
+        else output_if_between(usage, 0x16f, 0x175, w, E_Notify, consTitlesC, 0x80)
+        else output_if_between(usage, 0x17f, 0x1c8, w, E_Notify, consTitlesD, 0x80)
+        else output_if_between(usage, 0x1ff, 0x29d, w, E_Notify, consTitlesE, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintDigitizerPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x0e, w, E_Notify, digitTitles0, 0x80)
+        else output_if_between(usage, 0x1f, 0x23, w, E_Notify, digitTitles1, 0x80)
+        else output_if_between(usage, 0x2f, 0x47, w, E_Notify, digitTitles2, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintAlphanumDisplayPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        output_if_between(usage, 0x00, 0x03, w, E_Notify, aplphanumTitles0, 0x80)
+        else output_if_between(usage, 0x1f, 0x4e, w, E_Notify, aplphanumTitles1, 0x80)
+        else output_if_between(usage, 0x7f, 0x96, w, E_Notify, digitTitles2, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+void ReportDescParserBase::PrintMedicalInstrumentPageUsage(uint16_t usage) {
+        const char * const * w;
+        E_Notify(pstrSpace, 0x80);
+
+        if(usage == 1) E_Notify(pstrUsageMedicalUltrasound, 0x80);
+        else if(usage == 0x70)
+                E_Notify(pstrUsageDepthGainCompensation, 0x80);
+        else output_if_between(usage, 0x1f, 0x28, w, E_Notify, medInstrTitles0, 0x80)
+        else output_if_between(usage, 0x3f, 0x45, w, E_Notify, medInstrTitles1, 0x80)
+        else output_if_between(usage, 0x5f, 0x62, w, E_Notify, medInstrTitles2, 0x80)
+        else output_if_between(usage, 0x7f, 0x8a, w, E_Notify, medInstrTitles3, 0x80)
+        else output_if_between(usage, 0x9f, 0xa2, w, E_Notify, medInstrTitles4, 0x80)
+        else E_Notify(pstrUsagePageUndefined, 0x80);
+}
+
+uint8_t ReportDescParser2::ParseItem(uint8_t **pp, uint16_t *pcntdn) {
+        //uint8_t ret = enErrorSuccess;
+
+        switch(itemParseState) {
+                case 0:
+                        if(**pp == HID_LONG_ITEM_PREFIX)
+                                USBTRACE("\r\nLONG\r\n");
+                        else {
+                                uint8_t size = ((**pp) & DATA_SIZE_MASK);
+                                itemPrefix = (**pp);
+                                itemSize = 1 + ((size == DATA_SIZE_4) ? 4 : size);
+                        }
+                        (*pp)++;
+                        (*pcntdn)--;
+                        itemSize--;
+                        itemParseState = 1;
+
+                        if(!itemSize)
+                                break;
+
+                        if(!pcntdn)
+                                return enErrorIncomplete;
+                case 1:
+                        theBuffer.valueSize = itemSize;
+                        valParser.Initialize(&theBuffer);
+                        itemParseState = 2;
+                case 2:
+                        if(!valParser.Parse(pp, pcntdn))
+                                return enErrorIncomplete;
+                        itemParseState = 3;
+                case 3:
+                {
+                        uint8_t data = *((uint8_t*)varBuffer);
+
+                        switch(itemPrefix & (TYPE_MASK | TAG_MASK)) {
+                                case (TYPE_LOCAL | TAG_LOCAL_USAGE):
+                                        if(pfUsage) {
+                                                if(theBuffer.valueSize > 1) {
+                                                        uint16_t* ui16 = reinterpret_cast<uint16_t *>(varBuffer);
+                                                        pfUsage(*ui16);
+                                                } else
+                                                        pfUsage(data);
+                                        }
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE):
+                                        rptSize = data;
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT):
+                                        rptCount = data;
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID):
+                                        rptId = data;
+                                        break;
+                                case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN):
+                                        useMin = data;
+                                        break;
+                                case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX):
+                                        useMax = data;
+                                        break;
+                                case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE):
+                                        SetUsagePage(data);
+                                        break;
+                                case (TYPE_MAIN | TAG_MAIN_OUTPUT):
+                                case (TYPE_MAIN | TAG_MAIN_FEATURE):
+                                        rptSize = 0;
+                                        rptCount = 0;
+                                        useMin = 0;
+                                        useMax = 0;
+                                        break;
+                                case (TYPE_MAIN | TAG_MAIN_INPUT):
+                                        OnInputItem(data);
+
+                                        totalSize += (uint16_t)rptSize * (uint16_t)rptCount;
+
+                                        rptSize = 0;
+                                        rptCount = 0;
+                                        useMin = 0;
+                                        useMax = 0;
+                                        break;
+                        } // switch (**pp & (TYPE_MASK | TAG_MASK))
+                }
+        } // switch (itemParseState)
+        itemParseState = 0;
+        return enErrorSuccess;
+}
+
+void ReportDescParser2::OnInputItem(uint8_t itm) {
+        uint8_t byte_offset = (totalSize >> 3); // calculate offset to the next unhandled byte i = (int)(totalCount / 8);
+        uint32_t tmp = (byte_offset << 3);
+        uint8_t bit_offset = totalSize - tmp; // number of bits in the current byte already handled
+        uint8_t *p = pBuf + byte_offset; // current byte pointer
+
+        if(bit_offset)
+                *p >>= bit_offset;
+
+        uint8_t usage = useMin;
+
+        bool print_usemin_usemax = ((useMin < useMax) && ((itm & 3) == 2) && pfUsage) ? true : false;
+
+        uint8_t bits_of_byte = 8;
+
+        // for each field in field array defined by rptCount
+        for(uint8_t field = 0; field < rptCount; field++, usage++) {
+
+                union {
+                        uint8_t bResult[4];
+                        uint16_t wResult[2];
+                        uint32_t dwResult;
+                } result;
+
+                result.dwResult = 0;
+                uint8_t mask = 0;
+
+                if(print_usemin_usemax)
+                        pfUsage(usage);
+
+                // bits_left            - number of bits in the field(array of fields, depending on Report Count) left to process
+                // bits_of_byte         - number of bits in current byte left to process
+                // bits_to_copy         - number of bits to copy to result buffer
+
+                // for each bit in a field
+                for(uint8_t bits_left = rptSize, bits_to_copy = 0; bits_left;
+                        bits_left -= bits_to_copy) {
+                        bits_to_copy = (bits_left > bits_of_byte) ? bits_of_byte : bits_left;
+
+                        result.dwResult <<= bits_to_copy; // Result buffer is shifted by the number of bits to be copied into it
+
+                        uint8_t val = *p;
+
+                        val >>= (8 - bits_of_byte); // Shift by the number of bits already processed
+
+                        mask = 0;
+
+                        for(uint8_t j = bits_to_copy; j; j--) {
+                                mask <<= 1;
+                                mask |= 1;
+                        }
+
+                        result.bResult[0] = (result.bResult[0] | (val & mask));
+
+                        bits_of_byte -= bits_to_copy;
+
+                        if(bits_of_byte < 1) {
+                                bits_of_byte = 8;
+                                p++;
+                        }
+                }
+                PrintByteValue(result.dwResult);
+        }
+        E_Notify(PSTR("\r\n"), 0x80);
+}
+
+void UniversalReportParser::Parse(USBHID *hid, bool is_rpt_id __attribute__((unused)), uint8_t len, uint8_t *buf) {
+        ReportDescParser2 prs(len, buf);
+
+        uint8_t ret = hid->GetReportDescr(0, &prs);
+
+        if(ret)
+                ErrorMessage<uint8_t > (PSTR("GetReportDescr-2"), ret);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hidescriptorparser.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,177 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__HIDDESCRIPTORPARSER_H__)
+#define __HIDDESCRIPTORPARSER_H__
+
+#include "usbhid.h"
+
+class ReportDescParserBase : public USBReadParser {
+public:
+        typedef void (*UsagePageFunc)(uint16_t usage);
+
+        static void PrintGenericDesktopPageUsage(uint16_t usage);
+        static void PrintSimulationControlsPageUsage(uint16_t usage);
+        static void PrintVRControlsPageUsage(uint16_t usage);
+        static void PrintSportsControlsPageUsage(uint16_t usage);
+        static void PrintGameControlsPageUsage(uint16_t usage);
+        static void PrintGenericDeviceControlsPageUsage(uint16_t usage);
+        static void PrintLEDPageUsage(uint16_t usage);
+        static void PrintButtonPageUsage(uint16_t usage);
+        static void PrintOrdinalPageUsage(uint16_t usage);
+        static void PrintTelephonyPageUsage(uint16_t usage);
+        static void PrintConsumerPageUsage(uint16_t usage);
+        static void PrintDigitizerPageUsage(uint16_t usage);
+        static void PrintAlphanumDisplayPageUsage(uint16_t usage);
+        static void PrintMedicalInstrumentPageUsage(uint16_t usage);
+
+        static void PrintValue(uint8_t *p, uint8_t len);
+        static void PrintByteValue(uint8_t data);
+
+        static void PrintItemTitle(uint8_t prefix);
+
+        static const char * const usagePageTitles0[];
+        static const char * const usagePageTitles1[];
+        static const char * const genDesktopTitles0[];
+        static const char * const genDesktopTitles1[];
+        static const char * const genDesktopTitles2[];
+        static const char * const genDesktopTitles3[];
+        static const char * const genDesktopTitles4[];
+        static const char * const simuTitles0[];
+        static const char * const simuTitles1[];
+        static const char * const simuTitles2[];
+        static const char * const vrTitles0[];
+        static const char * const vrTitles1[];
+        static const char * const sportsCtrlTitles0[];
+        static const char * const sportsCtrlTitles1[];
+        static const char * const sportsCtrlTitles2[];
+        static const char * const gameTitles0[];
+        static const char * const gameTitles1[];
+        static const char * const genDevCtrlTitles[];
+        static const char * const ledTitles[];
+        static const char * const telTitles0[];
+        static const char * const telTitles1[];
+        static const char * const telTitles2[];
+        static const char * const telTitles3[];
+        static const char * const telTitles4[];
+        static const char * const telTitles5[];
+        static const char * const consTitles0[];
+        static const char * const consTitles1[];
+        static const char * const consTitles2[];
+        static const char * const consTitles3[];
+        static const char * const consTitles4[];
+        static const char * const consTitles5[];
+        static const char * const consTitles6[];
+        static const char * const consTitles7[];
+        static const char * const consTitles8[];
+        static const char * const consTitles9[];
+        static const char * const consTitlesA[];
+        static const char * const consTitlesB[];
+        static const char * const consTitlesC[];
+        static const char * const consTitlesD[];
+        static const char * const consTitlesE[];
+        static const char * const digitTitles0[];
+        static const char * const digitTitles1[];
+        static const char * const digitTitles2[];
+        static const char * const aplphanumTitles0[];
+        static const char * const aplphanumTitles1[];
+        static const char * const aplphanumTitles2[];
+        static const char * const medInstrTitles0[];
+        static const char * const medInstrTitles1[];
+        static const char * const medInstrTitles2[];
+        static const char * const medInstrTitles3[];
+        static const char * const medInstrTitles4[];
+
+protected:
+        static UsagePageFunc usagePageFunctions[];
+
+        MultiValueBuffer theBuffer;
+        MultiByteValueParser valParser;
+        ByteSkipper theSkipper;
+        uint8_t varBuffer[sizeof (USB_CONFIGURATION_DESCRIPTOR)];
+
+        uint8_t itemParseState; // Item parser state variable
+        uint8_t itemSize; // Item size
+        uint8_t itemPrefix; // Item prefix (first byte)
+        uint8_t rptSize; // Report Size
+        uint8_t rptCount; // Report Count
+
+        uint16_t totalSize; // Report size in bits
+
+        // Method should be defined here if virtual.
+        virtual uint8_t ParseItem(uint8_t **pp, uint16_t *pcntdn);
+
+        UsagePageFunc pfUsage;
+
+        static void PrintUsagePage(uint16_t page);
+        void SetUsagePage(uint16_t page);
+
+public:
+
+        ReportDescParserBase() :
+        itemParseState(0),
+        itemSize(0),
+        itemPrefix(0),
+        rptSize(0),
+        rptCount(0),
+        pfUsage(NULL) {
+                theBuffer.pValue = varBuffer;
+                valParser.Initialize(&theBuffer);
+                theSkipper.Initialize(&theBuffer);
+        };
+
+        void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset);
+
+        enum {
+                enErrorSuccess = 0
+                , enErrorIncomplete // value or record is partialy read in buffer
+                , enErrorBufferTooSmall
+        };
+};
+
+class ReportDescParser : public ReportDescParserBase {
+};
+
+class ReportDescParser2 : public ReportDescParserBase {
+        uint8_t rptId; // Report ID
+        uint8_t useMin; // Usage Minimum
+        uint8_t useMax; // Usage Maximum
+        uint8_t fieldCount; // Number of field being currently processed
+
+        void OnInputItem(uint8_t itm); // Method which is called every time Input item is found
+
+        uint8_t *pBuf; // Report buffer pointer
+        uint8_t bLen; // Report length
+
+protected:
+        // Method should be defined here if virtual.
+        virtual uint8_t ParseItem(uint8_t **pp, uint16_t *pcntdn);
+
+public:
+
+        ReportDescParser2(uint16_t len, uint8_t *pbuf) :
+        ReportDescParserBase(), rptId(0), useMin(0), useMax(0), fieldCount(0), pBuf(pbuf), bLen(len) {
+        };
+};
+
+class UniversalReportParser : public HIDReportParser {
+public:
+        // Method should be defined here if virtual.
+        virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);
+};
+
+#endif // __HIDDESCRIPTORPARSER_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hiduniversal.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,426 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#include "hiduniversal.h"
+
+HIDUniversal::HIDUniversal(USB *p) :
+USBHID(p),
+qNextPollTime(0),
+pollInterval(0),
+bPollEnable(false),
+bHasReportId(false) {
+        Initialize();
+
+        if(pUsb)
+                pUsb->RegisterDeviceClass(this);
+}
+
+uint16_t HIDUniversal::GetHidClassDescrLen(uint8_t type, uint8_t num) {
+        for(uint8_t i = 0, n = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) {
+                if(descrInfo[i].bDescrType == type) {
+                        if(n == num)
+                                return descrInfo[i].wDescriptorLength;
+                        n++;
+                }
+        }
+        return 0;
+}
+
+void HIDUniversal::Initialize() {
+        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {
+                rptParsers[i].rptId = 0;
+                rptParsers[i].rptParser = NULL;
+        }
+        for(uint8_t i = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) {
+                descrInfo[i].bDescrType = 0;
+                descrInfo[i].wDescriptorLength = 0;
+        }
+        for(uint8_t i = 0; i < maxHidInterfaces; i++) {
+                hidInterfaces[i].bmInterface = 0;
+                hidInterfaces[i].bmProtocol = 0;
+
+                for(uint8_t j = 0; j < maxEpPerInterface; j++)
+                        hidInterfaces[i].epIndex[j] = 0;
+        }
+        for(uint8_t i = 0; i < totalEndpoints; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+        }
+        bNumEP = 1;
+        bNumIface = 0;
+        bConfNum = 0;
+        pollInterval = 0;
+
+        ZeroMemory(constBuffLen, prevBuf);
+}
+
+bool HIDUniversal::SetReportParser(uint8_t id, HIDReportParser *prs) {
+        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {
+                if(rptParsers[i].rptId == 0 && rptParsers[i].rptParser == NULL) {
+                        rptParsers[i].rptId = id;
+                        rptParsers[i].rptParser = prs;
+                        return true;
+                }
+        }
+        return false;
+}
+
+HIDReportParser* HIDUniversal::GetReportParser(uint8_t id) {
+        if(!bHasReportId)
+                return ((rptParsers[0].rptParser) ? rptParsers[0].rptParser : NULL);
+
+        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {
+                if(rptParsers[i].rptId == id)
+                        return rptParsers[i].rptParser;
+        }
+        return NULL;
+}
+
+uint8_t HIDUniversal::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint8_t len = 0;
+
+        uint8_t num_of_conf; // number of configurations
+        //uint8_t num_of_intf; // number of interfaces
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        USBTRACE("HU Init\r\n");
+
+        if(bAddress)
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        if(!p->epinfo) {
+                USBTRACE("epinfo\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf);
+
+        if(!rcode)
+                len = (buf[0] > constBufSize) ? constBufSize : buf[0];
+
+        if(rcode) {
+                // Restore p->epinfo
+                p->epinfo = oldep_ptr;
+
+                goto FailGetDevDescr;
+        }
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                USBTRACE2("setAddr:", rcode);
+                return rcode;
+        }
+
+        //delay(2); //per USB 2.0 sect.9.2.6.3
+
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        if(len)
+                rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf);
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        VID = udd->idVendor; // Can be used by classes that inherits this class to check the VID and PID of the connected device
+        PID = udd->idProduct;
+
+        num_of_conf = udd->bNumConfigurations;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        USBTRACE2("NC:", num_of_conf);
+
+        for(uint8_t i = 0; i < num_of_conf; i++) {
+                //HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;
+                ConfigDescParser<USB_CLASS_HID, 0, 0,
+                        CP_MASK_COMPARE_CLASS> confDescrParser(this);
+
+                //rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                if(bNumEP > 1)
+                        break;
+        } // for
+
+        if(bNumEP < 2)
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+
+        USBTRACE2("Cnf:", bConfNum);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+        for(uint8_t i = 0; i < bNumIface; i++) {
+                if(hidInterfaces[i].epIndex[epInterruptInIndex] == 0)
+                        continue;
+
+                rcode = SetIdle(hidInterfaces[i].bmInterface, 0, 0);
+
+                if(rcode && rcode != hrSTALL)
+                        goto FailSetIdle;
+        }
+
+        USBTRACE("HU configured\r\n");
+
+        OnInitSuccessful();
+
+        bPollEnable = true;
+        return 0;
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr();
+        goto Fail;
+#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+        goto Fail;
+#endif
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+        goto Fail;
+#endif
+
+
+FailSetIdle:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("SetIdle:");
+#endif
+
+#ifdef DEBUG_USB_HOST
+Fail:
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+HIDUniversal::HIDInterface* HIDUniversal::FindInterface(uint8_t iface, uint8_t alt, uint8_t proto) {
+        for(uint8_t i = 0; i < bNumIface && i < maxHidInterfaces; i++)
+                if(hidInterfaces[i].bmInterface == iface && hidInterfaces[i].bmAltSet == alt
+                        && hidInterfaces[i].bmProtocol == proto)
+                        return hidInterfaces + i;
+        return NULL;
+}
+
+void HIDUniversal::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {
+        // If the first configuration satisfies, the others are not concidered.
+        if(bNumEP > 1 && conf != bConfNum)
+                return;
+
+        //ErrorMessage<uint8_t>(PSTR("\r\nConf.Val"), conf);
+        //ErrorMessage<uint8_t>(PSTR("Iface Num"), iface);
+        //ErrorMessage<uint8_t>(PSTR("Alt.Set"), alt);
+
+        bConfNum = conf;
+
+        uint8_t index = 0;
+        HIDInterface *piface = FindInterface(iface, alt, proto);
+
+        // Fill in interface structure in case of new interface
+        if(!piface) {
+                piface = hidInterfaces + bNumIface;
+                piface->bmInterface = iface;
+                piface->bmAltSet = alt;
+                piface->bmProtocol = proto;
+                bNumIface++;
+        }
+
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT)
+                index = (pep->bEndpointAddress & 0x80) == 0x80 ? epInterruptInIndex : epInterruptOutIndex;
+
+        if(index) {
+                // Fill in the endpoint info structure
+                epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F);
+                epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+                epInfo[bNumEP].bmSndToggle = 0;
+                epInfo[bNumEP].bmRcvToggle = 0;
+                epInfo[bNumEP].bmNakPower = USB_NAK_NOWAIT;
+
+                // Fill in the endpoint index list
+                piface->epIndex[index] = bNumEP; //(pep->bEndpointAddress & 0x0F);
+
+                if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints
+                        pollInterval = pep->bInterval;
+
+                bNumEP++;
+        }
+        //PrintEndpointDescriptor(pep);
+}
+
+uint8_t HIDUniversal::Release() {
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+
+        bNumEP = 1;
+        bAddress = 0;
+        qNextPollTime = 0;
+        bPollEnable = false;
+        return 0;
+}
+
+bool HIDUniversal::BuffersIdentical(uint8_t len, uint8_t *buf1, uint8_t *buf2) {
+        for(uint8_t i = 0; i < len; i++)
+                if(buf1[i] != buf2[i])
+                        return false;
+        return true;
+}
+
+void HIDUniversal::ZeroMemory(uint8_t len, uint8_t *buf) {
+        for(uint8_t i = 0; i < len; i++)
+                buf[i] = 0;
+}
+
+void HIDUniversal::SaveBuffer(uint8_t len, uint8_t *src, uint8_t *dest) {
+        for(uint8_t i = 0; i < len; i++)
+                dest[i] = src[i];
+}
+
+uint8_t HIDUniversal::Poll() {
+        uint8_t rcode = 0;
+
+        if(!bPollEnable)
+                return 0;
+
+        if((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) {
+                qNextPollTime = (uint32_t)millis() + pollInterval;
+
+                uint8_t buf[constBuffLen];
+
+                for(uint8_t i = 0; i < bNumIface; i++) {
+                        uint8_t index = hidInterfaces[i].epIndex[epInterruptInIndex];
+                        uint16_t read = (uint16_t)epInfo[index].maxPktSize;
+
+                        ZeroMemory(constBuffLen, buf);
+
+                        uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[index].epAddr, &read, buf);
+
+                        if(rcode) {
+                                if(rcode != hrNAK)
+                                        USBTRACE3("(hiduniversal.h) Poll:", rcode, 0x81);
+                                return rcode;
+                        }
+
+                        if(read > constBuffLen)
+                                read = constBuffLen;
+
+                        bool identical = BuffersIdentical(read, buf, prevBuf);
+
+                        SaveBuffer(read, buf, prevBuf);
+
+                        if(identical)
+                                return 0;
+#if 0
+                        Notify(PSTR("\r\nBuf: "), 0x80);
+
+                        for(uint8_t i = 0; i < read; i++) {
+                                D_PrintHex<uint8_t > (buf[i], 0x80);
+                                Notify(PSTR(" "), 0x80);
+                        }
+
+                        Notify(PSTR("\r\n"), 0x80);
+#endif
+                        ParseHIDData(this, bHasReportId, (uint8_t)read, buf);
+
+                        HIDReportParser *prs = GetReportParser(((bHasReportId) ? *buf : 0));
+
+                        if(prs)
+                                prs->Parse(this, bHasReportId, (uint8_t)read, buf);
+                }
+        }
+        return rcode;
+}
+
+// Send a report to interrupt out endpoint. This is NOT SetReport() request!
+uint8_t HIDUniversal::SndRpt(uint16_t nbytes, uint8_t *dataptr) {
+        return pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, nbytes, dataptr);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hiduniversal.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,109 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(__HIDUNIVERSAL_H__)
+#define __HIDUNIVERSAL_H__
+
+#include "usbhid.h"
+//#include "hidescriptorparser.h"
+
+class HIDUniversal : public USBHID {
+
+        struct ReportParser {
+                uint8_t rptId;
+                HIDReportParser *rptParser;
+        } rptParsers[MAX_REPORT_PARSERS];
+
+        // HID class specific descriptor type and length info obtained from HID descriptor
+        HID_CLASS_DESCRIPTOR_LEN_AND_TYPE descrInfo[HID_MAX_HID_CLASS_DESCRIPTORS];
+
+        // Returns HID class specific descriptor length by its type and order number
+        uint16_t GetHidClassDescrLen(uint8_t type, uint8_t num);
+
+        struct HIDInterface {
+                struct {
+                        uint8_t bmInterface : 3;
+                        uint8_t bmAltSet : 3;
+                        uint8_t bmProtocol : 2;
+                };
+                uint8_t epIndex[maxEpPerInterface + 1]; // We need to make room for the control endpoint as well
+        };
+
+        uint8_t bConfNum; // configuration number
+        uint8_t bNumIface; // number of interfaces in the configuration
+        uint8_t bNumEP; // total number of EP in the configuration
+        uint32_t qNextPollTime; // next poll time
+        uint8_t pollInterval;
+        bool bPollEnable; // poll enable flag
+
+        static const uint16_t constBuffLen = 64; // event buffer length
+        uint8_t prevBuf[constBuffLen]; // previous event buffer
+
+        void Initialize();
+        HIDInterface* FindInterface(uint8_t iface, uint8_t alt, uint8_t proto);
+
+        void ZeroMemory(uint8_t len, uint8_t *buf);
+        bool BuffersIdentical(uint8_t len, uint8_t *buf1, uint8_t *buf2);
+        void SaveBuffer(uint8_t len, uint8_t *src, uint8_t *dest);
+
+protected:
+        EpInfo epInfo[totalEndpoints];
+        HIDInterface hidInterfaces[maxHidInterfaces];
+
+        bool bHasReportId;
+
+        uint16_t PID, VID; // PID and VID of connected device
+
+        // HID implementation
+        HIDReportParser* GetReportParser(uint8_t id);
+
+        virtual uint8_t OnInitSuccessful() {
+                return 0;
+        };
+
+        virtual void ParseHIDData(USBHID *hid __attribute__((unused)), bool is_rpt_id __attribute__((unused)), uint8_t len __attribute__((unused)), uint8_t *buf __attribute__((unused))) {
+                return;
+        };
+
+public:
+        HIDUniversal(USB *p);
+
+        // HID implementation
+        bool SetReportParser(uint8_t id, HIDReportParser *prs);
+
+        // USBDeviceConfig implementation
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t Release();
+        uint8_t Poll();
+
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        virtual bool isReady() {
+                return bPollEnable;
+        };
+
+        // UsbConfigXtracter implementation
+        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+
+        // Send report - do not mix with SetReport()!
+        uint8_t SndRpt(uint16_t nbytes, uint8_t *dataptr);
+};
+
+#endif // __HIDUNIVERSAL_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hidusagestr.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,978 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined( __HIDUSAGESTR_H__)
+#define  __HIDUSAGESTR_H__
+
+#include "Usb.h"
+
+const char pstrSpace [] PROGMEM = " ";
+const char pstrCRLF [] PROGMEM = "\r\n";
+const char pstrSingleTab [] PROGMEM = "\t";
+const char pstrDoubleTab [] PROGMEM = "\t\t";
+const char pstrTripleTab [] PROGMEM = "\t\t\t";
+
+// Usage Page String Titles
+const char pstrUsagePageUndefined [] PROGMEM = "Undef";
+const char pstrUsagePageGenericDesktopControls [] PROGMEM = "Gen Desktop Ctrls";
+const char pstrUsagePageSimulationControls [] PROGMEM = "Simu Ctrls";
+const char pstrUsagePageVRControls [] PROGMEM = "VR Ctrls";
+const char pstrUsagePageSportControls [] PROGMEM = "Sport Ctrls";
+const char pstrUsagePageGameControls [] PROGMEM = "Game Ctrls";
+const char pstrUsagePageGenericDeviceControls [] PROGMEM = "Gen Dev Ctrls";
+const char pstrUsagePageKeyboardKeypad [] PROGMEM = "Kbrd/Keypad";
+const char pstrUsagePageLEDs [] PROGMEM = "LEDs";
+const char pstrUsagePageButton [] PROGMEM = "Button";
+const char pstrUsagePageOrdinal [] PROGMEM = "Ordinal";
+const char pstrUsagePageTelephone [] PROGMEM = "Tel";
+const char pstrUsagePageConsumer [] PROGMEM = "Consumer";
+const char pstrUsagePageDigitizer [] PROGMEM = "Digitizer";
+const char pstrUsagePagePID [] PROGMEM = "PID";
+const char pstrUsagePageUnicode [] PROGMEM = "Unicode";
+const char pstrUsagePageAlphaNumericDisplay [] PROGMEM = "Alpha Num Disp";
+const char pstrUsagePageMedicalInstruments [] PROGMEM = "Medical Instr";
+const char pstrUsagePageMonitor [] PROGMEM = "Monitor";
+const char pstrUsagePagePower [] PROGMEM = "Power";
+const char pstrUsagePageBarCodeScanner [] PROGMEM = "Bar Code Scan";
+const char pstrUsagePageScale [] PROGMEM = "Scale";
+const char pstrUsagePageMSRDevices [] PROGMEM = "Magn Stripe Read Dev";
+const char pstrUsagePagePointOfSale [] PROGMEM = "POS";
+const char pstrUsagePageCameraControl [] PROGMEM = "Cam Ctrl";
+const char pstrUsagePageArcade [] PROGMEM = "Arcade";
+const char pstrUsagePageReserved [] PROGMEM = "Reserved";
+const char pstrUsagePageVendorDefined [] PROGMEM = "Vendor Def";
+
+// Generic Desktop Controls Page
+const char pstrUsagePointer [] PROGMEM = "Pointer";
+const char pstrUsageMouse [] PROGMEM = "Mouse";
+const char pstrUsageJoystick [] PROGMEM = "Joystick";
+const char pstrUsageGamePad [] PROGMEM = "Game Pad";
+const char pstrUsageKeyboard [] PROGMEM = "Kbrd";
+const char pstrUsageKeypad [] PROGMEM = "Keypad";
+const char pstrUsageMultiAxisController [] PROGMEM = "Multi-axis Ctrl";
+const char pstrUsageTabletPCSystemControls [] PROGMEM = "Tablet PC Sys Ctrls";
+const char pstrUsageX [] PROGMEM = "X";
+const char pstrUsageY [] PROGMEM = "Y";
+const char pstrUsageZ [] PROGMEM = "Z";
+const char pstrUsageRx [] PROGMEM = "Rx";
+const char pstrUsageRy [] PROGMEM = "Ry";
+const char pstrUsageRz [] PROGMEM = "Rz";
+const char pstrUsageSlider [] PROGMEM = "Slider";
+const char pstrUsageDial [] PROGMEM = "Dial";
+const char pstrUsageWheel [] PROGMEM = "Wheel";
+const char pstrUsageHatSwitch [] PROGMEM = "Hat Switch";
+const char pstrUsageCountedBuffer [] PROGMEM = "Counted Buf";
+const char pstrUsageByteCount [] PROGMEM = "Byte Count";
+const char pstrUsageMotionWakeup [] PROGMEM = "Motion Wakeup";
+const char pstrUsageStart [] PROGMEM = "Start";
+const char pstrUsageSelect [] PROGMEM = "Sel";
+const char pstrUsageVx [] PROGMEM = "Vx";
+const char pstrUsageVy [] PROGMEM = "Vy";
+const char pstrUsageVz [] PROGMEM = "Vz";
+const char pstrUsageVbrx [] PROGMEM = "Vbrx";
+const char pstrUsageVbry [] PROGMEM = "Vbry";
+const char pstrUsageVbrz [] PROGMEM = "Vbrz";
+const char pstrUsageVno [] PROGMEM = "Vno";
+const char pstrUsageFeatureNotification [] PROGMEM = "Feature Notif";
+const char pstrUsageResolutionMultiplier [] PROGMEM = "Res Mult";
+const char pstrUsageSystemControl [] PROGMEM = "Sys Ctrl";
+const char pstrUsageSystemPowerDown [] PROGMEM = "Sys Pwr Down";
+const char pstrUsageSystemSleep [] PROGMEM = "Sys Sleep";
+const char pstrUsageSystemWakeup [] PROGMEM = "Sys Wakeup";
+const char pstrUsageSystemContextMenu [] PROGMEM = "Sys Context Menu";
+const char pstrUsageSystemMainMenu [] PROGMEM = "Sys Main Menu";
+const char pstrUsageSystemAppMenu [] PROGMEM = "Sys App Menu";
+const char pstrUsageSystemMenuHelp [] PROGMEM = "Sys Menu Help";
+const char pstrUsageSystemMenuExit [] PROGMEM = "Sys Menu Exit";
+const char pstrUsageSystemMenuSelect [] PROGMEM = "Sys Menu Select";
+const char pstrUsageSystemMenuRight [] PROGMEM = "Sys Menu Right";
+const char pstrUsageSystemMenuLeft [] PROGMEM = "Sys Menu Left";
+const char pstrUsageSystemMenuUp [] PROGMEM = "Sys Menu Up";
+const char pstrUsageSystemMenuDown [] PROGMEM = "Sys Menu Down";
+const char pstrUsageSystemColdRestart [] PROGMEM = "Sys Cold Restart";
+const char pstrUsageSystemWarmRestart [] PROGMEM = "Sys Warm Restart";
+const char pstrUsageDPadUp [] PROGMEM = "D-pad Up";
+const char pstrUsageDPadDown [] PROGMEM = "D-pad Down";
+const char pstrUsageDPadRight [] PROGMEM = "D-pad Right";
+const char pstrUsageDPadLeft [] PROGMEM = "D-pad Left";
+const char pstrUsageSystemDock [] PROGMEM = "Sys Dock";
+const char pstrUsageSystemUndock [] PROGMEM = "Sys Undock";
+const char pstrUsageSystemSetup [] PROGMEM = "Sys Setup";
+const char pstrUsageSystemBreak [] PROGMEM = "Sys Break";
+const char pstrUsageSystemDebuggerBreak [] PROGMEM = "Sys Dbg Brk";
+const char pstrUsageApplicationBreak [] PROGMEM = "App Break";
+const char pstrUsageApplicationDebuggerBreak [] PROGMEM = "App Dbg Brk";
+const char pstrUsageSystemSpeakerMute [] PROGMEM = "Sys Spk Mute";
+const char pstrUsageSystemHibernate [] PROGMEM = "Sys Hiber";
+const char pstrUsageSystemDisplayInvert [] PROGMEM = "Sys Disp Inv";
+const char pstrUsageSystemDisplayInternal [] PROGMEM = "Sys Disp Int";
+const char pstrUsageSystemDisplayExternal [] PROGMEM = "Sys Disp Ext";
+const char pstrUsageSystemDisplayBoth [] PROGMEM = "Sys Disp Both";
+const char pstrUsageSystemDisplayDual [] PROGMEM = "Sys Disp Dual";
+const char pstrUsageSystemDisplayToggleIntExt [] PROGMEM = "Sys Disp Tgl Int/Ext";
+const char pstrUsageSystemDisplaySwapPriSec [] PROGMEM = "Sys Disp Swap Pri/Sec";
+const char pstrUsageSystemDisplayLCDAutoscale [] PROGMEM = "Sys Disp LCD Autoscale";
+
+// Simulation Controls Page
+const char pstrUsageFlightSimulationDevice [] PROGMEM = "Flight Simu Dev";
+const char pstrUsageAutomobileSimulationDevice [] PROGMEM = "Auto Simu Dev";
+const char pstrUsageTankSimulationDevice [] PROGMEM = "Tank Simu Dev";
+const char pstrUsageSpaceshipSimulationDevice [] PROGMEM = "Space Simu Dev";
+const char pstrUsageSubmarineSimulationDevice [] PROGMEM = "Subm Simu Dev";
+const char pstrUsageSailingSimulationDevice [] PROGMEM = "Sail Simu Dev";
+const char pstrUsageMotocicleSimulationDevice [] PROGMEM = "Moto Simu Dev";
+const char pstrUsageSportsSimulationDevice [] PROGMEM = "Sport Simu Dev";
+const char pstrUsageAirplaneSimulationDevice [] PROGMEM = "Airp Simu Dev";
+const char pstrUsageHelicopterSimulationDevice [] PROGMEM = "Heli Simu Dev";
+const char pstrUsageMagicCarpetSimulationDevice [] PROGMEM = "Magic Carpet Simu Dev";
+const char pstrUsageBicycleSimulationDevice [] PROGMEM = "Bike Simu Dev";
+const char pstrUsageFlightControlStick [] PROGMEM = "Flight Ctrl Stick";
+const char pstrUsageFlightStick [] PROGMEM = "Flight Stick";
+const char pstrUsageCyclicControl [] PROGMEM = "Cyclic Ctrl";
+const char pstrUsageCyclicTrim [] PROGMEM = "Cyclic Trim";
+const char pstrUsageFlightYoke [] PROGMEM = "Flight Yoke";
+const char pstrUsageTrackControl [] PROGMEM = "Track Ctrl";
+const char pstrUsageAileron [] PROGMEM = "Aileron";
+const char pstrUsageAileronTrim [] PROGMEM = "Aileron Trim";
+const char pstrUsageAntiTorqueControl [] PROGMEM = "Anti-Torque Ctrl";
+const char pstrUsageAutopilotEnable [] PROGMEM = "Autopilot Enable";
+const char pstrUsageChaffRelease [] PROGMEM = "Chaff Release";
+const char pstrUsageCollectiveControl [] PROGMEM = "Collective Ctrl";
+const char pstrUsageDiveBrake [] PROGMEM = "Dive Brake";
+const char pstrUsageElectronicCountermeasures [] PROGMEM = "El Countermeasures";
+const char pstrUsageElevator [] PROGMEM = "Elevator";
+const char pstrUsageElevatorTrim [] PROGMEM = "Elevator Trim";
+const char pstrUsageRudder [] PROGMEM = "Rudder";
+const char pstrUsageThrottle [] PROGMEM = "Throttle";
+const char pstrUsageFlightCommunications [] PROGMEM = "Flight Comm";
+const char pstrUsageFlareRelease [] PROGMEM = "Flare Release";
+const char pstrUsageLandingGear [] PROGMEM = "Landing Gear";
+const char pstrUsageToeBrake [] PROGMEM = "Toe Brake";
+const char pstrUsageTrigger [] PROGMEM = "Trigger";
+const char pstrUsageWeaponsArm [] PROGMEM = "Weapons Arm";
+const char pstrUsageWeaponsSelect [] PROGMEM = "Weapons Sel";
+const char pstrUsageWingFlaps [] PROGMEM = "Wing Flaps";
+const char pstrUsageAccelerator [] PROGMEM = "Accel";
+const char pstrUsageBrake [] PROGMEM = "Brake";
+const char pstrUsageClutch [] PROGMEM = "Clutch";
+const char pstrUsageShifter [] PROGMEM = "Shifter";
+const char pstrUsageSteering [] PROGMEM = "Steering";
+const char pstrUsageTurretDirection [] PROGMEM = "Turret Dir";
+const char pstrUsageBarrelElevation [] PROGMEM = "Barrel Ele";
+const char pstrUsageDivePlane [] PROGMEM = "Dive Plane";
+const char pstrUsageBallast [] PROGMEM = "Ballast";
+const char pstrUsageBicycleCrank [] PROGMEM = "Bicycle Crank";
+const char pstrUsageHandleBars [] PROGMEM = "Handle Bars";
+const char pstrUsageFrontBrake [] PROGMEM = "Front Brake";
+const char pstrUsageRearBrake [] PROGMEM = "Rear Brake";
+
+// VR Controls Page
+const char pstrUsageBelt [] PROGMEM = "Belt";
+const char pstrUsageBodySuit [] PROGMEM = "Body Suit";
+const char pstrUsageFlexor [] PROGMEM = "Flexor";
+const char pstrUsageGlove [] PROGMEM = "Glove";
+const char pstrUsageHeadTracker [] PROGMEM = "Head Track";
+const char pstrUsageHeadMountedDisplay [] PROGMEM = "Head Disp";
+const char pstrUsageHandTracker [] PROGMEM = "Hand Track";
+const char pstrUsageOculometer [] PROGMEM = "Oculometer";
+const char pstrUsageVest [] PROGMEM = "Vest";
+const char pstrUsageAnimatronicDevice [] PROGMEM = "Animat Dev";
+const char pstrUsageStereoEnable [] PROGMEM = "Stereo Enbl";
+const char pstrUsageDisplayEnable [] PROGMEM = "Display Enbl";
+
+// Sport Controls Page
+const char pstrUsageBaseballBat [] PROGMEM = "Baseball Bat";
+const char pstrUsageGolfClub [] PROGMEM = "Golf Club";
+const char pstrUsageRowingMachine [] PROGMEM = "Rowing Mach";
+const char pstrUsageTreadmill [] PROGMEM = "Treadmill";
+const char pstrUsageOar [] PROGMEM = "Oar";
+const char pstrUsageSlope [] PROGMEM = "Slope";
+const char pstrUsageRate [] PROGMEM = "Rate";
+const char pstrUsageStickSpeed [] PROGMEM = "Stick Speed";
+const char pstrUsageStickFaceAngle [] PROGMEM = "Stick Face Ang";
+const char pstrUsageStickHeelToe [] PROGMEM = "Stick Heel/Toe";
+const char pstrUsageStickFollowThough [] PROGMEM = "Stick Flw Thru";
+const char pstrUsageStickTempo [] PROGMEM = "Stick Tempo";
+const char pstrUsageStickType [] PROGMEM = "Stick Type";
+const char pstrUsageStickHeight [] PROGMEM = "Stick Hght";
+const char pstrUsagePutter [] PROGMEM = "Putter";
+const char pstrUsage1Iron [] PROGMEM = "1 Iron";
+const char pstrUsage2Iron [] PROGMEM = "2 Iron";
+const char pstrUsage3Iron [] PROGMEM = "3 Iron";
+const char pstrUsage4Iron [] PROGMEM = "4 Iron";
+const char pstrUsage5Iron [] PROGMEM = "5 Iron";
+const char pstrUsage6Iron [] PROGMEM = "6 Iron";
+const char pstrUsage7Iron [] PROGMEM = "7 Iron";
+const char pstrUsage8Iron [] PROGMEM = "8 Iron";
+const char pstrUsage9Iron [] PROGMEM = "9 Iron";
+const char pstrUsage10Iron [] PROGMEM = "10 Iron";
+const char pstrUsage11Iron [] PROGMEM = "11 Iron";
+const char pstrUsageSandWedge [] PROGMEM = "Sand Wedge";
+const char pstrUsageLoftWedge [] PROGMEM = "Loft Wedge";
+const char pstrUsagePowerWedge [] PROGMEM = "Pwr Wedge";
+const char pstrUsage1Wood [] PROGMEM = "1 Wood";
+const char pstrUsage3Wood [] PROGMEM = "3 Wood";
+const char pstrUsage5Wood [] PROGMEM = "5 Wood";
+const char pstrUsage7Wood [] PROGMEM = "7 Wood";
+const char pstrUsage9Wood [] PROGMEM = "9 Wood";
+
+// Game Controls Page
+const char pstrUsage3DGameController [] PROGMEM = "3D Game Ctrl";
+const char pstrUsagePinballDevice [] PROGMEM = "Pinball Dev";
+const char pstrUsageGunDevice [] PROGMEM = "Gun Dev";
+const char pstrUsagePointOfView [] PROGMEM = "POV";
+const char pstrUsageTurnRightLeft [] PROGMEM = "Turn Right Left";
+const char pstrUsagePitchForwardBackward [] PROGMEM = "Pitch Fwd/Back";
+const char pstrUsageRollRightLeft [] PROGMEM = "Roll Right/Left";
+const char pstrUsageMoveRightLeft [] PROGMEM = "Move Right/Left";
+const char pstrUsageMoveForwardBackward [] PROGMEM = "Move Fwd/Back";
+const char pstrUsageMoveUpDown [] PROGMEM = "Move Up/Down";
+const char pstrUsageLeanRightLeft [] PROGMEM = "Lean Right/Left";
+const char pstrUsageLeanForwardBackward [] PROGMEM = "Lean Fwd/Back";
+const char pstrUsageHeightOfPOV [] PROGMEM = "Height of POV";
+const char pstrUsageFlipper [] PROGMEM = "Flipper";
+const char pstrUsageSecondaryFlipper [] PROGMEM = "Second Flipper";
+const char pstrUsageBump [] PROGMEM = "Bump";
+const char pstrUsageNewGame [] PROGMEM = "New Game";
+const char pstrUsageShootBall [] PROGMEM = "Shoot Ball";
+const char pstrUsagePlayer [] PROGMEM = "Player";
+const char pstrUsageGunBolt [] PROGMEM = "Gun Bolt";
+const char pstrUsageGunClip [] PROGMEM = "Gun Clip";
+const char pstrUsageGunSelector [] PROGMEM = "Gun Sel";
+const char pstrUsageGunSingleShot [] PROGMEM = "Gun Sngl Shot";
+const char pstrUsageGunBurst [] PROGMEM = "Gun Burst";
+const char pstrUsageGunAutomatic [] PROGMEM = "Gun Auto";
+const char pstrUsageGunSafety [] PROGMEM = "Gun Safety";
+const char pstrUsageGamepadFireJump [] PROGMEM = "Gamepad Fire/Jump";
+const char pstrUsageGamepadTrigger [] PROGMEM = "Gamepad Trig";
+
+// Generic Device Controls Page
+const char pstrUsageBatteryStrength [] PROGMEM = "Bat Strength";
+const char pstrUsageWirelessChannel [] PROGMEM = "Wireless Ch";
+const char pstrUsageWirelessID [] PROGMEM = "Wireless ID";
+const char pstrUsageDiscoverWirelessControl [] PROGMEM = "Discover Wireless Ctrl";
+const char pstrUsageSecurityCodeCharEntered [] PROGMEM = "Sec Code Char Entrd";
+const char pstrUsageSecurityCodeCharErased [] PROGMEM = "Sec Code Char Erased";
+const char pstrUsageSecurityCodeCleared [] PROGMEM = "Sec Code Cleared";
+
+// LED Page
+const char pstrUsageNumLock [] PROGMEM = "Num Lock";
+const char pstrUsageCapsLock [] PROGMEM = "Caps Lock";
+const char pstrUsageScrollLock [] PROGMEM = "Scroll Lock";
+const char pstrUsageCompose [] PROGMEM = "Compose";
+const char pstrUsageKana [] PROGMEM = "Kana";
+const char pstrUsagePower [] PROGMEM = "Pwr";
+const char pstrUsageShift [] PROGMEM = "Shift";
+const char pstrUsageDoNotDisturb [] PROGMEM = "DND";
+const char pstrUsageMute [] PROGMEM = "Mute";
+const char pstrUsageToneEnable [] PROGMEM = "Tone Enbl";
+const char pstrUsageHighCutFilter [] PROGMEM = "High Cut Fltr";
+const char pstrUsageLowCutFilter [] PROGMEM = "Low Cut Fltr";
+const char pstrUsageEqualizerEnable [] PROGMEM = "Eq Enbl";
+const char pstrUsageSoundFieldOn [] PROGMEM = "Sound Field On";
+const char pstrUsageSurroundOn [] PROGMEM = "Surround On";
+const char pstrUsageRepeat [] PROGMEM = "Repeat";
+const char pstrUsageStereo [] PROGMEM = "Stereo";
+const char pstrUsageSamplingRateDetect [] PROGMEM = "Smpl Rate Detect";
+const char pstrUsageSpinning [] PROGMEM = "Spinning";
+const char pstrUsageCAV [] PROGMEM = "CAV";
+const char pstrUsageCLV [] PROGMEM = "CLV";
+const char pstrUsageRecordingFormatDetect [] PROGMEM = "Rec Format Detect";
+const char pstrUsageOffHook [] PROGMEM = "Off Hook";
+const char pstrUsageRing [] PROGMEM = "Ring";
+const char pstrUsageMessageWaiting [] PROGMEM = "Msg Wait";
+const char pstrUsageDataMode [] PROGMEM = "Data Mode";
+const char pstrUsageBatteryOperation [] PROGMEM = "Bat Op";
+const char pstrUsageBatteryOK [] PROGMEM = "Bat OK";
+const char pstrUsageBatteryLow [] PROGMEM = "Bat Low";
+const char pstrUsageSpeaker [] PROGMEM = "Speaker";
+const char pstrUsageHeadSet [] PROGMEM = "Head Set";
+const char pstrUsageHold [] PROGMEM = "Hold";
+const char pstrUsageMicrophone [] PROGMEM = "Mic";
+const char pstrUsageCoverage [] PROGMEM = "Coverage";
+const char pstrUsageNightMode [] PROGMEM = "Night Mode";
+const char pstrUsageSendCalls [] PROGMEM = "Send Calls";
+const char pstrUsageCallPickup [] PROGMEM = "Call Pickup";
+const char pstrUsageConference [] PROGMEM = "Conf";
+const char pstrUsageStandBy [] PROGMEM = "Stand-by";
+const char pstrUsageCameraOn [] PROGMEM = "Cam On";
+const char pstrUsageCameraOff [] PROGMEM = "Cam Off";
+const char pstrUsageOnLine [] PROGMEM = "On-Line";
+const char pstrUsageOffLine [] PROGMEM = "Off-Line";
+const char pstrUsageBusy [] PROGMEM = "Busy";
+const char pstrUsageReady [] PROGMEM = "Ready";
+const char pstrUsagePaperOut [] PROGMEM = "Paper Out";
+const char pstrUsagePaperJam [] PROGMEM = "Paper Jam";
+const char pstrUsageRemote [] PROGMEM = "Remote";
+const char pstrUsageForward [] PROGMEM = "Fwd";
+const char pstrUsageReverse [] PROGMEM = "Rev";
+const char pstrUsageStop [] PROGMEM = "Stop";
+const char pstrUsageRewind [] PROGMEM = "Rewind";
+const char pstrUsageFastForward [] PROGMEM = "Fast Fwd";
+const char pstrUsagePlay [] PROGMEM = "Play";
+const char pstrUsagePause [] PROGMEM = "Pause";
+const char pstrUsageRecord [] PROGMEM = "Rec";
+const char pstrUsageError [] PROGMEM = "Error";
+const char pstrUsageSelectedIndicator [] PROGMEM = "Usage Sel Ind";
+const char pstrUsageInUseIndicator [] PROGMEM = "Usage In Use Ind";
+const char pstrUsageMultiModeIndicator [] PROGMEM = "Usage Multi Mode Ind";
+const char pstrUsageIndicatorOn [] PROGMEM = "Ind On";
+const char pstrUsageIndicatorFlash [] PROGMEM = "Ind Flash";
+const char pstrUsageIndicatorSlowBlink [] PROGMEM = "Ind Slow Blk";
+const char pstrUsageIndicatorFastBlink [] PROGMEM = "Ind Fast Blk";
+const char pstrUsageIndicatorOff [] PROGMEM = "Ind Off";
+const char pstrUsageFlashOnTime [] PROGMEM = "Flash On Time";
+const char pstrUsageSlowBlinkOnTime [] PROGMEM = "Slow Blk On Time";
+const char pstrUsageSlowBlinkOffTime [] PROGMEM = "Slow Blk Off Time";
+const char pstrUsageFastBlinkOnTime [] PROGMEM = "Fast Blk On Time";
+const char pstrUsageFastBlinkOffTime [] PROGMEM = "Fast Blk Off Time";
+const char pstrUsageIndicatorColor [] PROGMEM = "Usage Ind Color";
+const char pstrUsageIndicatorRed [] PROGMEM = "Ind Red";
+const char pstrUsageIndicatorGreen [] PROGMEM = "Ind Green";
+const char pstrUsageIndicatorAmber [] PROGMEM = "Ind Amber";
+const char pstrUsageGenericIndicator [] PROGMEM = "Gen Ind";
+const char pstrUsageSystemSuspend [] PROGMEM = "Sys Suspend";
+const char pstrUsageExternalPowerConnected [] PROGMEM = "Ext Pwr Conn";
+
+// Telephony Usage Page
+const char pstrUsagePhone [] PROGMEM = "Phone";
+const char pstrUsageAnsweringMachine [] PROGMEM = "Answ Mach";
+const char pstrUsageMessageControls [] PROGMEM = "Msg Ctrls";
+const char pstrUsageHandset [] PROGMEM = "Handset";
+const char pstrUsageHeadset [] PROGMEM = "Headset";
+const char pstrUsageTelephonyKeyPad [] PROGMEM = "Tel Key Pad";
+const char pstrUsageProgrammableButton [] PROGMEM = "Prog Button";
+const char pstrUsageHookSwitch [] PROGMEM = "Hook Sw";
+const char pstrUsageFlash [] PROGMEM = "Flash";
+const char pstrUsageFeature [] PROGMEM = "Feature";
+//const char pstrUsageHold [] PROGMEM = "Hold";
+const char pstrUsageRedial [] PROGMEM = "Redial";
+const char pstrUsageTransfer [] PROGMEM = "Transfer";
+const char pstrUsageDrop [] PROGMEM = "Drop";
+const char pstrUsagePark [] PROGMEM = "Park";
+const char pstrUsageForwardCalls [] PROGMEM = "Fwd Calls";
+const char pstrUsageAlternateFunction [] PROGMEM = "Alt Func";
+const char pstrUsageLine [] PROGMEM = "Line";
+const char pstrUsageSpeakerPhone [] PROGMEM = "Spk Phone";
+//const char pstrUsageConference [] PROGMEM = "Conference";
+const char pstrUsageRingEnable [] PROGMEM = "Ring Enbl";
+const char pstrUsageRingSelect [] PROGMEM = "Ring Sel";
+const char pstrUsagePhoneMute [] PROGMEM = "Phone Mute";
+const char pstrUsageCallerID [] PROGMEM = "Caller ID";
+const char pstrUsageSend [] PROGMEM = "Send";
+const char pstrUsageSpeedDial [] PROGMEM = "Speed Dial";
+const char pstrUsageStoreNumber [] PROGMEM = "Store Num";
+const char pstrUsageRecallNumber [] PROGMEM = "Recall Num";
+const char pstrUsagePhoneDirectory [] PROGMEM = "Phone Dir";
+const char pstrUsageVoiceMail [] PROGMEM = "Voice Mail";
+const char pstrUsageScreenCalls [] PROGMEM = "Screen Calls";
+//const char pstrUsageDoNotDisturb [] PROGMEM = "Do Not Disturb";
+const char pstrUsageMessage [] PROGMEM = "Msg";
+const char pstrUsageAnswerOnOff [] PROGMEM = "Answer On/Off";
+const char pstrUsageInsideDialTone [] PROGMEM = "Inside Dial Tone";
+const char pstrUsageOutsideDialTone [] PROGMEM = "Outside Dial Tone";
+const char pstrUsageInsideRingTone [] PROGMEM = "Inside Ring Tone";
+const char pstrUsageOutsideRingTone [] PROGMEM = "Outside Ring Tone";
+const char pstrUsagePriorityRingTone [] PROGMEM = "Prior Ring Tone";
+const char pstrUsageInsideRingback [] PROGMEM = "Inside Ringback";
+const char pstrUsagePriorityRingback [] PROGMEM = "Priority Ringback";
+const char pstrUsageLineBusyTone [] PROGMEM = "Ln Busy Tone";
+const char pstrUsageReorderTone [] PROGMEM = "Reorder Tone";
+const char pstrUsageCallWaitingTone [] PROGMEM = "Call Wait Tone";
+const char pstrUsageConfirmationTone1 [] PROGMEM = "Cnfrm Tone1";
+const char pstrUsageConfirmationTone2 [] PROGMEM = "Cnfrm Tone2";
+const char pstrUsageTonesOff [] PROGMEM = "Tones Off";
+const char pstrUsageOutsideRingback [] PROGMEM = "Outside Ringback";
+const char pstrUsageRinger [] PROGMEM = "Ringer";
+const char pstrUsagePhoneKey0 [] PROGMEM = "0";
+const char pstrUsagePhoneKey1 [] PROGMEM = "1";
+const char pstrUsagePhoneKey2 [] PROGMEM = "2";
+const char pstrUsagePhoneKey3 [] PROGMEM = "3";
+const char pstrUsagePhoneKey4 [] PROGMEM = "4";
+const char pstrUsagePhoneKey5 [] PROGMEM = "5";
+const char pstrUsagePhoneKey6 [] PROGMEM = "6";
+const char pstrUsagePhoneKey7 [] PROGMEM = "7";
+const char pstrUsagePhoneKey8 [] PROGMEM = "8";
+const char pstrUsagePhoneKey9 [] PROGMEM = "9";
+const char pstrUsagePhoneKeyStar [] PROGMEM = "*";
+const char pstrUsagePhoneKeyPound [] PROGMEM = "#";
+const char pstrUsagePhoneKeyA [] PROGMEM = "A";
+const char pstrUsagePhoneKeyB [] PROGMEM = "B";
+const char pstrUsagePhoneKeyC [] PROGMEM = "C";
+const char pstrUsagePhoneKeyD [] PROGMEM = "D";
+
+// Consumer Usage Page
+const char pstrUsageConsumerControl [] PROGMEM = "Consumer Ctrl";
+const char pstrUsageNumericKeyPad [] PROGMEM = "Num Key Pad";
+//const char pstrUsageProgrammableButton [] PROGMEM = "Prog Btn";
+//const char pstrUsageMicrophone [] PROGMEM = "Mic";
+const char pstrUsageHeadphone [] PROGMEM = "Headphone";
+const char pstrUsageGraphicEqualizer [] PROGMEM = "Graph Eq";
+const char pstrUsagePlus10 [] PROGMEM = "+10";
+const char pstrUsagePlus100 [] PROGMEM = "+100";
+const char pstrUsageAMPM [] PROGMEM = "AM/PM";
+//const char pstrUsagePower [] PROGMEM = "Pwr";
+const char pstrUsageReset [] PROGMEM = "Reset";
+const char pstrUsageSleep [] PROGMEM = "Sleep";
+const char pstrUsageSleepAfter [] PROGMEM = "Sleep After";
+const char pstrUsageSleepMode [] PROGMEM = "Sleep Mode";
+const char pstrUsageIllumination [] PROGMEM = "Illumin";
+const char pstrUsageFunctionButtons [] PROGMEM = "Func Btns";
+const char pstrUsageMenu [] PROGMEM = "Menu";
+const char pstrUsageMenuPick [] PROGMEM = "Menu Pick";
+const char pstrUsageMenuUp [] PROGMEM = "Menu Up";
+const char pstrUsageMenuDown [] PROGMEM = "Menu Down";
+const char pstrUsageMenuLeft [] PROGMEM = "Menu Left";
+const char pstrUsageMenuRight [] PROGMEM = "Menu Right";
+const char pstrUsageMenuEscape [] PROGMEM = "Menu Esc";
+const char pstrUsageMenuValueIncrease [] PROGMEM = "Menu Val Inc";
+const char pstrUsageMenuValueDecrease [] PROGMEM = "Menu Val Dec";
+const char pstrUsageDataOnScreen [] PROGMEM = "Data On Scr";
+const char pstrUsageClosedCaption [] PROGMEM = "Closed Cptn";
+const char pstrUsageClosedCaptionSelect [] PROGMEM = "Closed Cptn Sel";
+const char pstrUsageVCRTV [] PROGMEM = "VCR/TV";
+const char pstrUsageBroadcastMode [] PROGMEM = "Brdcast Mode";
+const char pstrUsageSnapshot [] PROGMEM = "Snapshot";
+const char pstrUsageStill [] PROGMEM = "Still";
+const char pstrUsageSelection [] PROGMEM = "Sel";
+const char pstrUsageAssignSelection [] PROGMEM = "Assign Sel";
+const char pstrUsageModeStep [] PROGMEM = "Mode Step";
+const char pstrUsageRecallLast [] PROGMEM = "Recall Last";
+const char pstrUsageEnterChannel [] PROGMEM = "Entr Channel";
+const char pstrUsageOrderMovie [] PROGMEM = "Ord Movie";
+const char pstrUsageChannel [] PROGMEM = "Channel";
+const char pstrUsageMediaSelection [] PROGMEM = "Med Sel";
+const char pstrUsageMediaSelectComputer [] PROGMEM = "Med Sel Comp";
+const char pstrUsageMediaSelectTV [] PROGMEM = "Med Sel TV";
+const char pstrUsageMediaSelectWWW [] PROGMEM = "Med Sel WWW";
+const char pstrUsageMediaSelectDVD [] PROGMEM = "Med Sel DVD";
+const char pstrUsageMediaSelectTelephone [] PROGMEM = "Med Sel Tel";
+const char pstrUsageMediaSelectProgramGuide [] PROGMEM = "Med Sel PG";
+const char pstrUsageMediaSelectVideoPhone [] PROGMEM = "Med Sel Vid";
+const char pstrUsageMediaSelectGames [] PROGMEM = "Med Sel Games";
+const char pstrUsageMediaSelectMessages [] PROGMEM = "Med Sel Msg";
+const char pstrUsageMediaSelectCD [] PROGMEM = "Med Sel CD";
+const char pstrUsageMediaSelectVCR [] PROGMEM = "Med Sel VCR";
+const char pstrUsageMediaSelectTuner [] PROGMEM = "Med Sel Tuner";
+const char pstrUsageQuit [] PROGMEM = "Quit";
+const char pstrUsageHelp [] PROGMEM = "Help";
+const char pstrUsageMediaSelectTape [] PROGMEM = "Med Sel Tape";
+const char pstrUsageMediaSelectCable [] PROGMEM = "Med Sel Cbl";
+const char pstrUsageMediaSelectSatellite [] PROGMEM = "Med Sel Sat";
+const char pstrUsageMediaSelectSecurity [] PROGMEM = "Med Sel Secur";
+const char pstrUsageMediaSelectHome [] PROGMEM = "Med Sel Home";
+const char pstrUsageMediaSelectCall [] PROGMEM = "Med Sel Call";
+const char pstrUsageChannelIncrement [] PROGMEM = "Ch Inc";
+const char pstrUsageChannelDecrement [] PROGMEM = "Ch Dec";
+const char pstrUsageMediaSelectSAP [] PROGMEM = "Med Sel SAP";
+const char pstrUsageVCRPlus [] PROGMEM = "VCR+";
+const char pstrUsageOnce [] PROGMEM = "Once";
+const char pstrUsageDaily [] PROGMEM = "Daily";
+const char pstrUsageWeekly [] PROGMEM = "Weekly";
+const char pstrUsageMonthly [] PROGMEM = "Monthly";
+//const char pstrUsagePlay [] PROGMEM = "Play";
+//const char pstrUsagePause [] PROGMEM = "Pause";
+//const char pstrUsageRecord [] PROGMEM = "Rec";
+//const char pstrUsageFastForward [] PROGMEM = "FF";
+//const char pstrUsageRewind [] PROGMEM = "Rewind";
+const char pstrUsageScanNextTrack [] PROGMEM = "Next Track";
+const char pstrUsageScanPreviousTrack [] PROGMEM = "Prev Track";
+//const char pstrUsageStop [] PROGMEM = "Stop";
+const char pstrUsageEject [] PROGMEM = "Eject";
+const char pstrUsageRandomPlay [] PROGMEM = "Random";
+const char pstrUsageSelectDisk [] PROGMEM = "Sel Disk";
+const char pstrUsageEnterDisk [] PROGMEM = "Ent Disk";
+//const char pstrUsageRepeat [] PROGMEM = "Repeat";
+const char pstrUsageTracking [] PROGMEM = "Tracking";
+const char pstrUsageTrackNormal [] PROGMEM = "Trk Norm";
+const char pstrUsageSlowTracking [] PROGMEM = "Slow Trk";
+const char pstrUsageFrameForward [] PROGMEM = "Frm Fwd";
+const char pstrUsageFrameBackwards [] PROGMEM = "Frm Back";
+const char pstrUsageMark [] PROGMEM = "Mark";
+const char pstrUsageClearMark [] PROGMEM = "Clr Mark";
+const char pstrUsageRepeatFromMark [] PROGMEM = "Rpt Mark";
+const char pstrUsageReturnToMark [] PROGMEM = "Ret to Mark";
+const char pstrUsageSearchMarkForward [] PROGMEM = "Search Mark Fwd";
+const char pstrUsageSearchMarkBackwards [] PROGMEM = "Search Mark Back";
+const char pstrUsageCounterReset [] PROGMEM = "Counter Reset";
+const char pstrUsageShowCounter [] PROGMEM = "Show Counter";
+const char pstrUsageTrackingIncrement [] PROGMEM = "Track Inc";
+const char pstrUsageTrackingDecrement [] PROGMEM = "Track Dec";
+const char pstrUsageStopEject [] PROGMEM = "Stop/Eject";
+const char pstrUsagePlayPause [] PROGMEM = "Play/Pause";
+const char pstrUsagePlaySkip [] PROGMEM = "Play/Skip";
+const char pstrUsageVolume [] PROGMEM = "Vol";
+const char pstrUsageBalance [] PROGMEM = "Balance";
+//const char pstrUsageMute [] PROGMEM = "Mute";
+const char pstrUsageBass [] PROGMEM = "Bass";
+const char pstrUsageTreble [] PROGMEM = "Treble";
+const char pstrUsageBassBoost [] PROGMEM = "Bass Boost";
+const char pstrUsageSurroundMode [] PROGMEM = "Surround";
+const char pstrUsageLoudness [] PROGMEM = "Loud";
+const char pstrUsageMPX [] PROGMEM = "MPX";
+const char pstrUsageVolumeIncrement [] PROGMEM = "Vol Inc";
+const char pstrUsageVolumeDecrement [] PROGMEM = "Vol Dec";
+const char pstrUsageSpeedSelect [] PROGMEM = "Speed";
+const char pstrUsagePlaybackSpeed [] PROGMEM = "Play Speed";
+const char pstrUsageStandardPlay [] PROGMEM = "Std Play";
+const char pstrUsageLongPlay [] PROGMEM = "Long Play";
+const char pstrUsageExtendedPlay [] PROGMEM = "Ext Play";
+const char pstrUsageSlow [] PROGMEM = "Slow";
+const char pstrUsageFanEnable [] PROGMEM = "Fan Enbl";
+const char pstrUsageFanSpeed [] PROGMEM = "Fan Speed";
+const char pstrUsageLightEnable [] PROGMEM = "Light Enbl";
+const char pstrUsageLightIlluminationLevel [] PROGMEM = "Light Illum Lev";
+const char pstrUsageClimateControlEnable [] PROGMEM = "Climate Enbl";
+const char pstrUsageRoomTemperature [] PROGMEM = "Room Temp";
+const char pstrUsageSecurityEnable [] PROGMEM = "Secur Enbl";
+const char pstrUsageFireAlarm [] PROGMEM = "Fire Alm";
+const char pstrUsagePoliceAlarm [] PROGMEM = "Police Alm";
+const char pstrUsageProximity [] PROGMEM = "Prox";
+const char pstrUsageMotion [] PROGMEM = "Motion";
+const char pstrUsageDuresAlarm [] PROGMEM = "Dures Alm";
+const char pstrUsageHoldupAlarm [] PROGMEM = "Holdup Alm";
+const char pstrUsageMedicalAlarm [] PROGMEM = "Med Alm";
+const char pstrUsageBalanceRight [] PROGMEM = "Balance Right";
+const char pstrUsageBalanceLeft [] PROGMEM = "Balance Left";
+const char pstrUsageBassIncrement [] PROGMEM = "Bass Inc";
+const char pstrUsageBassDecrement [] PROGMEM = "Bass Dec";
+const char pstrUsageTrebleIncrement [] PROGMEM = "Treble Inc";
+const char pstrUsageTrebleDecrement [] PROGMEM = "Treble Dec";
+const char pstrUsageSpeakerSystem [] PROGMEM = "Spk Sys";
+const char pstrUsageChannelLeft [] PROGMEM = "Ch Left";
+const char pstrUsageChannelRight [] PROGMEM = "Ch Right";
+const char pstrUsageChannelCenter [] PROGMEM = "Ch Center";
+const char pstrUsageChannelFront [] PROGMEM = "Ch Front";
+const char pstrUsageChannelCenterFront [] PROGMEM = "Ch Cntr Front";
+const char pstrUsageChannelSide [] PROGMEM = "Ch Side";
+const char pstrUsageChannelSurround [] PROGMEM = "Ch Surround";
+const char pstrUsageChannelLowFreqEnhancement [] PROGMEM = "Ch Low Freq Enh";
+const char pstrUsageChannelTop [] PROGMEM = "Ch Top";
+const char pstrUsageChannelUnknown [] PROGMEM = "Ch Unk";
+const char pstrUsageSubChannel [] PROGMEM = "Sub-ch";
+const char pstrUsageSubChannelIncrement [] PROGMEM = "Sub-ch Inc";
+const char pstrUsageSubChannelDecrement [] PROGMEM = "Sub-ch Dec";
+const char pstrUsageAlternateAudioIncrement [] PROGMEM = "Alt Aud Inc";
+const char pstrUsageAlternateAudioDecrement [] PROGMEM = "Alt Aud Dec";
+const char pstrUsageApplicationLaunchButtons [] PROGMEM = "App Launch Btns";
+const char pstrUsageALLaunchButtonConfigTool [] PROGMEM = "AL Launch Conf Tl";
+const char pstrUsageALProgrammableButton [] PROGMEM = "AL Pgm Btn";
+const char pstrUsageALConsumerControlConfig [] PROGMEM = "AL Cons Ctrl Cfg";
+const char pstrUsageALWordProcessor [] PROGMEM = "AL Word Proc";
+const char pstrUsageALTextEditor [] PROGMEM = "AL Txt Edtr";
+const char pstrUsageALSpreadsheet [] PROGMEM = "AL Sprdsheet";
+const char pstrUsageALGraphicsEditor [] PROGMEM = "AL Graph Edtr";
+const char pstrUsageALPresentationApp [] PROGMEM = "AL Present App";
+const char pstrUsageALDatabaseApp [] PROGMEM = "AL DB App";
+const char pstrUsageALEmailReader [] PROGMEM = "AL E-mail Rdr";
+const char pstrUsageALNewsreader [] PROGMEM = "AL Newsrdr";
+const char pstrUsageALVoicemail [] PROGMEM = "AL Voicemail";
+const char pstrUsageALContactsAddressBook [] PROGMEM = "AL Addr Book";
+const char pstrUsageALCalendarSchedule [] PROGMEM = "AL Clndr/Schdlr";
+const char pstrUsageALTaskProjectManager [] PROGMEM = "AL Task/Prj Mgr";
+const char pstrUsageALLogJournalTimecard [] PROGMEM = "AL Log/Jrnl/Tmcrd";
+const char pstrUsageALCheckbookFinance [] PROGMEM = "AL Chckbook/Fin";
+const char pstrUsageALCalculator [] PROGMEM = "AL Calc";
+const char pstrUsageALAVCapturePlayback [] PROGMEM = "AL A/V Capt/Play";
+const char pstrUsageALLocalMachineBrowser [] PROGMEM = "AL Loc Mach Brow";
+const char pstrUsageALLANWANBrow [] PROGMEM = "AL LAN/WAN Brow";
+const char pstrUsageALInternetBrowser [] PROGMEM = "AL I-net Brow";
+const char pstrUsageALRemoteNetISPConnect [] PROGMEM = "AL Rem Net Con";
+const char pstrUsageALNetworkConference [] PROGMEM = "AL Net Conf";
+const char pstrUsageALNetworkChat [] PROGMEM = "AL Net Chat";
+const char pstrUsageALTelephonyDialer [] PROGMEM = "AL Tel/Dial";
+const char pstrUsageALLogon [] PROGMEM = "AL Logon";
+const char pstrUsageALLogoff [] PROGMEM = "AL Logoff";
+const char pstrUsageALLogonLogoff [] PROGMEM = "AL Logon/Logoff";
+const char pstrUsageALTermLockScrSav [] PROGMEM = "AL Term Lock/Scr Sav";
+const char pstrUsageALControlPannel [] PROGMEM = "AL Ctrl Pan";
+const char pstrUsageALCommandLineProcessorRun [] PROGMEM = "AL Cmd/Run";
+const char pstrUsageALProcessTaskManager [] PROGMEM = "AL Task Mgr";
+const char pstrUsageALSelectTaskApplication [] PROGMEM = "AL Sel App";
+const char pstrUsageALNextTaskApplication [] PROGMEM = "AL Next App";
+const char pstrUsageALPreviousTaskApplication [] PROGMEM = "AL Prev App";
+const char pstrUsageALPreemptiveHaltTaskApp [] PROGMEM = "AL Prmpt Halt App";
+const char pstrUsageALIntegratedHelpCenter [] PROGMEM = "AL Hlp Cntr";
+const char pstrUsageALDocuments [] PROGMEM = "AL Docs";
+const char pstrUsageALThesaurus [] PROGMEM = "AL Thsrs";
+const char pstrUsageALDictionary [] PROGMEM = "AL Dict";
+const char pstrUsageALDesktop [] PROGMEM = "AL Desktop";
+const char pstrUsageALSpellCheck [] PROGMEM = "AL Spell Chk";
+const char pstrUsageALGrammarCheck [] PROGMEM = "AL Gram Chk";
+const char pstrUsageALWirelessStatus [] PROGMEM = "AL Wireless Sts";
+const char pstrUsageALKeyboardLayout [] PROGMEM = "AL Kbd Layout";
+const char pstrUsageALVirusProtection [] PROGMEM = "AL Vir Protect";
+const char pstrUsageALEncryption [] PROGMEM = "AL Encrypt";
+const char pstrUsageALScreenSaver [] PROGMEM = "AL Scr Sav";
+const char pstrUsageALAlarms [] PROGMEM = "AL Alarms";
+const char pstrUsageALClock [] PROGMEM = "AL Clock";
+const char pstrUsageALFileBrowser [] PROGMEM = "AL File Brow";
+const char pstrUsageALPowerStatus [] PROGMEM = "AL Pwr Sts";
+const char pstrUsageALImageBrowser [] PROGMEM = "AL Img Brow";
+const char pstrUsageALAudioBrowser [] PROGMEM = "AL Aud Brow";
+const char pstrUsageALMovieBrowser [] PROGMEM = "AL Mov Brow";
+const char pstrUsageALDigitalRightsManager [] PROGMEM = "AL Dig Rights Mgr";
+const char pstrUsageALDigitalWallet [] PROGMEM = "AL Dig Wallet";
+const char pstrUsageALInstantMessaging [] PROGMEM = "AL Inst Msg";
+const char pstrUsageALOEMFeaturesBrowser [] PROGMEM = "AL OEM Tips Brow";
+const char pstrUsageALOEMHelp [] PROGMEM = "AL OEM Hlp";
+const char pstrUsageALOnlineCommunity [] PROGMEM = "AL Online Com";
+const char pstrUsageALEntertainmentContentBrow [] PROGMEM = "AL Ent Cont Brow";
+const char pstrUsageALOnlineShoppingBrowser [] PROGMEM = "AL Online Shop Brow";
+const char pstrUsageALSmartCardInfoHelp [] PROGMEM = "AL SmartCard Inf";
+const char pstrUsageALMarketMonitorFinBrowser [] PROGMEM = "AL Market Brow";
+const char pstrUsageALCustomCorpNewsBrowser [] PROGMEM = "AL Cust Corp News Brow";
+const char pstrUsageALOnlineActivityBrowser [] PROGMEM = "AL Online Act Brow";
+const char pstrUsageALResearchSearchBrowser [] PROGMEM = "AL Search Brow";
+const char pstrUsageALAudioPlayer [] PROGMEM = "AL Aud Player";
+const char pstrUsageGenericGUIAppControls [] PROGMEM = "Gen GUI App Ctrl";
+const char pstrUsageACNew [] PROGMEM = "AC New";
+const char pstrUsageACOpen [] PROGMEM = "AC Open";
+const char pstrUsageACClose [] PROGMEM = "AC Close";
+const char pstrUsageACExit [] PROGMEM = "AC Exit";
+const char pstrUsageACMaximize [] PROGMEM = "AC Max";
+const char pstrUsageACMinimize [] PROGMEM = "AC Min";
+const char pstrUsageACSave [] PROGMEM = "AC Save";
+const char pstrUsageACPrint [] PROGMEM = "AC Print";
+const char pstrUsageACProperties [] PROGMEM = "AC Prop";
+const char pstrUsageACUndo [] PROGMEM = "AC Undo";
+const char pstrUsageACCopy [] PROGMEM = "AC Copy";
+const char pstrUsageACCut [] PROGMEM = "AC Cut";
+const char pstrUsageACPaste [] PROGMEM = "AC Paste";
+const char pstrUsageACSelectAll [] PROGMEM = "AC Sel All";
+const char pstrUsageACFind [] PROGMEM = "AC Find";
+const char pstrUsageACFindAndReplace [] PROGMEM = "AC Find/Replace";
+const char pstrUsageACSearch [] PROGMEM = "AC Search";
+const char pstrUsageACGoto [] PROGMEM = "AC Goto";
+const char pstrUsageACHome [] PROGMEM = "AC Home";
+const char pstrUsageACBack [] PROGMEM = "AC Back";
+const char pstrUsageACForward [] PROGMEM = "AC Fwd";
+const char pstrUsageACStop [] PROGMEM = "AC Stop";
+const char pstrUsageACRefresh [] PROGMEM = "AC Refresh";
+const char pstrUsageACPreviousLink [] PROGMEM = "AC Prev Link";
+const char pstrUsageACNextLink [] PROGMEM = "AC Next Link";
+const char pstrUsageACBookmarks [] PROGMEM = "AC Bkmarks";
+const char pstrUsageACHistory [] PROGMEM = "AC Hist";
+const char pstrUsageACSubscriptions [] PROGMEM = "AC Subscr";
+const char pstrUsageACZoomIn [] PROGMEM = "AC Zoom In";
+const char pstrUsageACZoomOut [] PROGMEM = "AC Zoom Out";
+const char pstrUsageACZoom [] PROGMEM = "AC Zoom";
+const char pstrUsageACFullScreenView [] PROGMEM = "AC Full Scr";
+const char pstrUsageACNormalView [] PROGMEM = "AC Norm View";
+const char pstrUsageACViewToggle [] PROGMEM = "AC View Tgl";
+const char pstrUsageACScrollUp [] PROGMEM = "AC Scroll Up";
+const char pstrUsageACScrollDown [] PROGMEM = "AC Scroll Down";
+const char pstrUsageACScroll [] PROGMEM = "AC Scroll";
+const char pstrUsageACPanLeft [] PROGMEM = "AC Pan Left";
+const char pstrUsageACPanRight [] PROGMEM = "AC Pan Right";
+const char pstrUsageACPan [] PROGMEM = "AC Pan";
+const char pstrUsageACNewWindow [] PROGMEM = "AC New Wnd";
+const char pstrUsageACTileHoriz [] PROGMEM = "AC Tile Horiz";
+const char pstrUsageACTileVert [] PROGMEM = "AC Tile Vert";
+const char pstrUsageACFormat [] PROGMEM = "AC Frmt";
+const char pstrUsageACEdit [] PROGMEM = "AC Edit";
+const char pstrUsageACBold [] PROGMEM = "AC Bold";
+const char pstrUsageACItalics [] PROGMEM = "AC Ital";
+const char pstrUsageACUnderline [] PROGMEM = "AC Under";
+const char pstrUsageACStrikethrough [] PROGMEM = "AC Strike";
+const char pstrUsageACSubscript [] PROGMEM = "AC Sub";
+const char pstrUsageACSuperscript [] PROGMEM = "AC Super";
+const char pstrUsageACAllCaps [] PROGMEM = "AC All Caps";
+const char pstrUsageACRotate [] PROGMEM = "AC Rotate";
+const char pstrUsageACResize [] PROGMEM = "AC Resize";
+const char pstrUsageACFlipHorizontal [] PROGMEM = "AC Flp H";
+const char pstrUsageACFlipVertical [] PROGMEM = "AC Flp V";
+const char pstrUsageACMirrorHorizontal [] PROGMEM = "AC Mir H";
+const char pstrUsageACMirrorVertical [] PROGMEM = "AC Mir V";
+const char pstrUsageACFontSelect [] PROGMEM = "AC Fnt Sel";
+const char pstrUsageACFontColor [] PROGMEM = "AC Fnt Clr";
+const char pstrUsageACFontSize [] PROGMEM = "AC Fnt Size";
+const char pstrUsageACJustifyLeft [] PROGMEM = "AC Just Left";
+const char pstrUsageACJustifyCenterH [] PROGMEM = "AC Just Cent H";
+const char pstrUsageACJustifyRight [] PROGMEM = "AC Just Right";
+const char pstrUsageACJustifyBlockH [] PROGMEM = "AC Just Block H";
+const char pstrUsageACJustifyTop [] PROGMEM = "AC Just Top";
+const char pstrUsageACJustifyCenterV [] PROGMEM = "AC Just Cent V";
+const char pstrUsageACJustifyBottom [] PROGMEM = "AC Just Bot";
+const char pstrUsageACJustifyBlockV [] PROGMEM = "AC Just Block V";
+const char pstrUsageACIndentDecrease [] PROGMEM = "AC Indent Dec";
+const char pstrUsageACIndentIncrease [] PROGMEM = "AC Indent Inc";
+const char pstrUsageACNumberedList [] PROGMEM = "AC Num List";
+const char pstrUsageACRestartNumbering [] PROGMEM = "AC Res Num";
+const char pstrUsageACBulletedList [] PROGMEM = "AC Blt List";
+const char pstrUsageACPromote [] PROGMEM = "AC Promote";
+const char pstrUsageACDemote [] PROGMEM = "AC Demote";
+const char pstrUsageACYes [] PROGMEM = "AC Yes";
+const char pstrUsageACNo [] PROGMEM = "AC No";
+const char pstrUsageACCancel [] PROGMEM = "AC Cancel";
+const char pstrUsageACCatalog [] PROGMEM = "AC Ctlg";
+const char pstrUsageACBuyChkout [] PROGMEM = "AC Buy";
+const char pstrUsageACAddToCart [] PROGMEM = "AC Add2Cart";
+const char pstrUsageACExpand [] PROGMEM = "AC Xpnd";
+const char pstrUsageACExpandAll [] PROGMEM = "AC Xpand All";
+const char pstrUsageACCollapse [] PROGMEM = "AC Collapse";
+const char pstrUsageACCollapseAll [] PROGMEM = "AC Collapse All";
+const char pstrUsageACPrintPreview [] PROGMEM = "AC Prn Prevw";
+const char pstrUsageACPasteSpecial [] PROGMEM = "AC Paste Spec";
+const char pstrUsageACInsertMode [] PROGMEM = "AC Ins Mode";
+const char pstrUsageACDelete [] PROGMEM = "AC Del";
+const char pstrUsageACLock [] PROGMEM = "AC Lock";
+const char pstrUsageACUnlock [] PROGMEM = "AC Unlock";
+const char pstrUsageACProtect [] PROGMEM = "AC Prot";
+const char pstrUsageACUnprotect [] PROGMEM = "AC Unprot";
+const char pstrUsageACAttachComment [] PROGMEM = "AC Attach Cmnt";
+const char pstrUsageACDeleteComment [] PROGMEM = "AC Del Cmnt";
+const char pstrUsageACViewComment [] PROGMEM = "AC View Cmnt";
+const char pstrUsageACSelectWord [] PROGMEM = "AC Sel Word";
+const char pstrUsageACSelectSentence [] PROGMEM = "AC Sel Sntc";
+const char pstrUsageACSelectParagraph [] PROGMEM = "AC Sel Para";
+const char pstrUsageACSelectColumn [] PROGMEM = "AC Sel Col";
+const char pstrUsageACSelectRow [] PROGMEM = "AC Sel Row";
+const char pstrUsageACSelectTable [] PROGMEM = "AC Sel Tbl";
+const char pstrUsageACSelectObject [] PROGMEM = "AC Sel Obj";
+const char pstrUsageACRedoRepeat [] PROGMEM = "AC Redo";
+const char pstrUsageACSort [] PROGMEM = "AC Sort";
+const char pstrUsageACSortAscending [] PROGMEM = "AC Sort Asc";
+const char pstrUsageACSortDescending [] PROGMEM = "AC Sort Desc";
+const char pstrUsageACFilter [] PROGMEM = "AC Filt";
+const char pstrUsageACSetClock [] PROGMEM = "AC Set Clk";
+const char pstrUsageACViewClock [] PROGMEM = "AC View Clk";
+const char pstrUsageACSelectTimeZone [] PROGMEM = "AC Sel Time Z";
+const char pstrUsageACEditTimeZone [] PROGMEM = "AC Edt Time Z";
+const char pstrUsageACSetAlarm [] PROGMEM = "AC Set Alm";
+const char pstrUsageACClearAlarm [] PROGMEM = "AC Clr Alm";
+const char pstrUsageACSnoozeAlarm [] PROGMEM = "AC Snz Alm";
+const char pstrUsageACResetAlarm [] PROGMEM = "AC Rst Alm";
+const char pstrUsageACSyncronize [] PROGMEM = "AC Sync";
+const char pstrUsageACSendReceive [] PROGMEM = "AC Snd/Rcv";
+const char pstrUsageACSendTo [] PROGMEM = "AC Snd To";
+const char pstrUsageACReply [] PROGMEM = "AC Reply";
+const char pstrUsageACReplyAll [] PROGMEM = "AC Reply All";
+const char pstrUsageACForwardMessage [] PROGMEM = "AC Fwd Msg";
+const char pstrUsageACSend [] PROGMEM = "AC Snd";
+const char pstrUsageACAttachFile [] PROGMEM = "AC Att File";
+const char pstrUsageACUpload [] PROGMEM = "AC Upld";
+const char pstrUsageACDownload [] PROGMEM = "AC Dnld";
+const char pstrUsageACSetBorders [] PROGMEM = "AC Set Brd";
+const char pstrUsageACInsertRow [] PROGMEM = "AC Ins Row";
+const char pstrUsageACInsertColumn [] PROGMEM = "AC Ins Col";
+const char pstrUsageACInsertFile [] PROGMEM = "AC Ins File";
+const char pstrUsageACInsertPicture [] PROGMEM = "AC Ins Pic";
+const char pstrUsageACInsertObject [] PROGMEM = "AC Ins Obj";
+const char pstrUsageACInsertSymbol [] PROGMEM = "AC Ins Sym";
+const char pstrUsageACSaveAndClose [] PROGMEM = "AC Sav&Cls";
+const char pstrUsageACRename [] PROGMEM = "AC Rename";
+const char pstrUsageACMerge [] PROGMEM = "AC Merge";
+const char pstrUsageACSplit [] PROGMEM = "AC Split";
+const char pstrUsageACDistributeHorizontaly [] PROGMEM = "AC Dist Hor";
+const char pstrUsageACDistributeVerticaly [] PROGMEM = "AC Dist Ver";
+
+// Digitaizers
+const char pstrUsageDigitizer [] PROGMEM = "Digitizer";
+const char pstrUsagePen [] PROGMEM = "Pen";
+const char pstrUsageLightPen [] PROGMEM = "Light Pen";
+const char pstrUsageTouchScreen [] PROGMEM = "Touch Scr";
+const char pstrUsageTouchPad [] PROGMEM = "Touch Pad";
+const char pstrUsageWhiteBoard [] PROGMEM = "White Brd";
+const char pstrUsageCoordinateMeasuringMachine [] PROGMEM = "Coord Meas Mach";
+const char pstrUsage3DDigitizer [] PROGMEM = "3D Dgtz";
+const char pstrUsageStereoPlotter [] PROGMEM = "Stereo Plot";
+const char pstrUsageArticulatedArm [] PROGMEM = "Art Arm";
+const char pstrUsageArmature [] PROGMEM = "Armature";
+const char pstrUsageMultiplePointDigitizer [] PROGMEM = "Multi Point Dgtz";
+const char pstrUsageFreeSpaceWand [] PROGMEM = "Free Space Wand";
+const char pstrUsageStylus [] PROGMEM = "Stylus";
+const char pstrUsagePuck [] PROGMEM = "Puck";
+const char pstrUsageFinger [] PROGMEM = "Finger";
+const char pstrUsageTipPressure [] PROGMEM = "Tip Press";
+const char pstrUsageBarrelPressure [] PROGMEM = "Brl Press";
+const char pstrUsageInRange [] PROGMEM = "In Range";
+const char pstrUsageTouch [] PROGMEM = "Touch";
+const char pstrUsageUntouch [] PROGMEM = "Untouch";
+const char pstrUsageTap [] PROGMEM = "Tap";
+const char pstrUsageQuality [] PROGMEM = "Qlty";
+const char pstrUsageDataValid [] PROGMEM = "Data Valid";
+const char pstrUsageTransducerIndex [] PROGMEM = "Transducer Ind";
+const char pstrUsageTabletFunctionKeys [] PROGMEM = "Tabl Func Keys";
+const char pstrUsageProgramChangeKeys [] PROGMEM = "Pgm Chng Keys";
+//const char pstrUsageBatteryStrength [] PROGMEM = "Bat Strength";
+const char pstrUsageInvert [] PROGMEM = "Invert";
+const char pstrUsageXTilt [] PROGMEM = "X Tilt";
+const char pstrUsageYTilt [] PROGMEM = "Y Tilt";
+const char pstrUsageAzimuth [] PROGMEM = "Azimuth";
+const char pstrUsageAltitude [] PROGMEM = "Altitude";
+const char pstrUsageTwist [] PROGMEM = "Twist";
+const char pstrUsageTipSwitch [] PROGMEM = "Tip Sw";
+const char pstrUsageSecondaryTipSwitch [] PROGMEM = "Scnd Tip Sw";
+const char pstrUsageBarrelSwitch [] PROGMEM = "Brl Sw";
+const char pstrUsageEraser [] PROGMEM = "Eraser";
+const char pstrUsageTabletPick [] PROGMEM = "Tbl Pick";
+
+// Alphanumeric Display Page
+const char pstrUsageAlphanumericDisplay [] PROGMEM = "Alphanum Disp";
+const char pstrUsageBitmappedDisplay [] PROGMEM = "Bmp Disp";
+const char pstrUsageDisplayAttributesReport [] PROGMEM = "Disp Attr Rpt";
+const char pstrUsageASCIICharacterSet [] PROGMEM = "ASCII chset";
+const char pstrUsageDataReadBack [] PROGMEM = "Data Rd Back";
+const char pstrUsageFontReadBack [] PROGMEM = "Fnt Rd Back";
+const char pstrUsageDisplayControlReport [] PROGMEM = "Disp Ctrl Rpt";
+const char pstrUsageClearDisplay [] PROGMEM = "Clr Disp";
+//const char pstrUsageDisplayEnable [] PROGMEM = "Disp Enbl";
+const char pstrUsageScreenSaverDelay [] PROGMEM = "Scr Sav Delay";
+const char pstrUsageScreenSaverEnable [] PROGMEM = "Scr Sav Enbl";
+const char pstrUsageVerticalScroll [] PROGMEM = "V Scroll";
+const char pstrUsageHorizontalScroll [] PROGMEM = "H Scroll";
+const char pstrUsageCharacterReport [] PROGMEM = "Char Rpt";
+const char pstrUsageDisplayData [] PROGMEM = "Disp Data";
+const char pstrUsageDisplayStatus [] PROGMEM = "Disp Stat";
+const char pstrUsageStatusNotReady [] PROGMEM = "Stat !Ready";
+const char pstrUsageStatusReady [] PROGMEM = "Stat Ready";
+const char pstrUsageErrorNotALoadableCharacter [] PROGMEM = "Err Not Ld Char";
+const char pstrUsageErrorFotDataCanNotBeRead [] PROGMEM = "Fnt Data Rd Err";
+const char pstrUsageCursorPositionReport [] PROGMEM = "Cur Pos Rpt";
+const char pstrUsageRow [] PROGMEM = "Row";
+const char pstrUsageColumn [] PROGMEM = "Col";
+const char pstrUsageRows [] PROGMEM = "Rows";
+const char pstrUsageColumns [] PROGMEM = "Cols";
+const char pstrUsageCursorPixelPosition [] PROGMEM = "Cur Pix Pos";
+const char pstrUsageCursorMode [] PROGMEM = "Cur Mode";
+const char pstrUsageCursorEnable [] PROGMEM = "Cur Enbl";
+const char pstrUsageCursorBlink [] PROGMEM = "Cur Blnk";
+const char pstrUsageFontReport [] PROGMEM = "Fnt Rpt";
+const char pstrUsageFontData [] PROGMEM = "Fnt Data";
+const char pstrUsageCharacterWidth [] PROGMEM = "Char Wdth";
+const char pstrUsageCharacterHeight [] PROGMEM = "Char Hght";
+const char pstrUsageCharacterSpacingHorizontal [] PROGMEM = "Char Space H";
+const char pstrUsageCharacterSpacingVertical [] PROGMEM = "Char Space V";
+const char pstrUsageUnicodeCharset [] PROGMEM = "Unicode Char";
+const char pstrUsageFont7Segment [] PROGMEM = "Fnt 7-seg";
+const char pstrUsage7SegmentDirectMap [] PROGMEM = "7-seg map";
+const char pstrUsageFont14Segment [] PROGMEM = "Fnt 14-seg";
+const char pstrUsage14SegmentDirectMap [] PROGMEM = "14-seg map";
+const char pstrUsageDisplayBrightness [] PROGMEM = "Disp Bright";
+const char pstrUsageDisplayContrast [] PROGMEM = "Disp Cntrst";
+const char pstrUsageCharacterAttribute [] PROGMEM = "Char Attr";
+const char pstrUsageAttributeReadback [] PROGMEM = "Attr Readbk";
+const char pstrUsageAttributeData [] PROGMEM = "Attr Data";
+const char pstrUsageCharAttributeEnhance [] PROGMEM = "Char Attr Enh";
+const char pstrUsageCharAttributeUnderline [] PROGMEM = "Char Attr Undl";
+const char pstrUsageCharAttributeBlink [] PROGMEM = "Char Attr Blnk";
+const char pstrUsageBitmapSizeX [] PROGMEM = "Bmp Size X";
+const char pstrUsageBitmapSizeY [] PROGMEM = "Bmp Size Y";
+const char pstrUsageBitDepthFormat [] PROGMEM = "Bit Dpth Fmt";
+const char pstrUsageDisplayOrientation [] PROGMEM = "Disp Ornt";
+const char pstrUsagePaletteReport [] PROGMEM = "Pal Rpt";
+const char pstrUsagePaletteDataSize [] PROGMEM = "Pal Data Size";
+const char pstrUsagePaletteDataOffset [] PROGMEM = "Pal Data Off";
+const char pstrUsagePaletteData [] PROGMEM = "Pal Data";
+const char pstrUsageBlitReport [] PROGMEM = "Blit Rpt";
+const char pstrUsageBlitRectangleX1 [] PROGMEM = "Blit Rect X1";
+const char pstrUsageBlitRectangleY1 [] PROGMEM = "Blit Rect Y1";
+const char pstrUsageBlitRectangleX2 [] PROGMEM = "Blit Rect X2";
+const char pstrUsageBlitRectangleY2 [] PROGMEM = "Blit Rect Y2";
+const char pstrUsageBlitData [] PROGMEM = "Blit Data";
+const char pstrUsageSoftButton [] PROGMEM = "Soft Btn";
+const char pstrUsageSoftButtonID [] PROGMEM = "Soft Btn ID";
+const char pstrUsageSoftButtonSide [] PROGMEM = "Soft Btn Side";
+const char pstrUsageSoftButtonOffset1 [] PROGMEM = "Soft Btn Off1";
+const char pstrUsageSoftButtonOffset2 [] PROGMEM = "Soft Btn Off2";
+const char pstrUsageSoftButtonReport [] PROGMEM = "Soft Btn Rpt";
+
+// Medical Instrument Page
+const char pstrUsageMedicalUltrasound [] PROGMEM = "Med Ultrasnd";
+const char pstrUsageVCRAcquisition [] PROGMEM = "VCR/Acq";
+const char pstrUsageFreezeThaw [] PROGMEM = "Freeze";
+const char pstrUsageClipStore [] PROGMEM = "Clip Store";
+const char pstrUsageUpdate [] PROGMEM = "Update";
+const char pstrUsageNext [] PROGMEM = "Next";
+const char pstrUsageSave [] PROGMEM = "Save";
+const char pstrUsagePrint [] PROGMEM = "Print";
+const char pstrUsageMicrophoneEnable [] PROGMEM = "Mic Enbl";
+const char pstrUsageCine [] PROGMEM = "Cine";
+const char pstrUsageTransmitPower [] PROGMEM = "Trans Pwr";
+//const char pstrUsageVolume [] PROGMEM = "Vol";
+const char pstrUsageFocus [] PROGMEM = "Focus";
+const char pstrUsageDepth [] PROGMEM = "Depth";
+const char pstrUsageSoftStepPrimary [] PROGMEM = "Soft Stp-Pri";
+const char pstrUsageSoftStepSecondary [] PROGMEM = "Soft Stp-Sec";
+const char pstrUsageDepthGainCompensation [] PROGMEM = "Dpth Gain Comp";
+const char pstrUsageZoomSelect [] PROGMEM = "Zoom Sel";
+const char pstrUsageZoomAdjust [] PROGMEM = "Zoom Adj";
+const char pstrUsageSpectralDopplerModeSelect [] PROGMEM = "Spec Dop Mode Sel";
+const char pstrUsageSpectralDopplerModeAdjust [] PROGMEM = "Spec Dop Mode Adj";
+const char pstrUsageColorDopplerModeSelect [] PROGMEM = "Color Dop Mode Sel";
+const char pstrUsageColorDopplerModeAdjust [] PROGMEM = "Color Dop Mode Adj";
+const char pstrUsageMotionModeSelect [] PROGMEM = "Motion Mode Sel";
+const char pstrUsageMotionModeAdjust [] PROGMEM = "Motion Mode Adj";
+const char pstrUsage2DModeSelect [] PROGMEM = "2D Mode Sel";
+const char pstrUsage2DModeAdjust [] PROGMEM = "2D Mode Adj";
+const char pstrUsageSoftControlSelect [] PROGMEM = "Soft Ctrl Sel";
+const char pstrUsageSoftControlAdjust [] PROGMEM = "Soft Ctrl Adj";
+
+//extern const char *usagePageTitles0[15];
+//const char *usagePageTitles1[];
+//const char *genDesktopTitles0[];
+//const char *genDesktopTitles1[];
+//const char *genDesktopTitles2[];
+//const char *genDesktopTitles3[];
+//const char *genDesktopTitles4[];
+//const char *simuTitles0[];
+//const char *simuTitles1[];
+//const char *simuTitles2[];
+//const char *vrTitles0[];
+//const char *vrTitles1[];
+//const char *sportsCtrlTitles0[];
+//const char *sportsCtrlTitles1[];
+//const char *sportsCtrlTitles2[];
+//const char *gameTitles0[];
+//const char *gameTitles1[];
+//const char *genDevCtrlTitles[];
+//const char *ledTitles[];
+//const char *telTitles0[];
+//const char *telTitles1[];
+//const char *telTitles2[];
+//const char *telTitles3[];
+//const char *telTitles4[];
+//const char *telTitles5[];
+//const char *consTitles0[];
+//const char *consTitles1[];
+//const char *consTitles2[];
+//const char *consTitles3[];
+//const char *consTitles4[];
+//const char *consTitles5[];
+//const char *consTitles6[];
+//const char *consTitles7[];
+//const char *consTitles8[];
+//const char *consTitles9[];
+//const char *consTitlesA[];
+//const char *consTitlesB[];
+//const char *consTitlesC[];
+//const char *consTitlesD[];
+//const char *consTitlesE[];
+//const char *digitTitles0[];
+//const char *digitTitles1[];
+//const char *digitTitles2[];
+//const char *aplphanumTitles0[];
+//const char *aplphanumTitles1[];
+//const char *aplphanumTitles2[];
+//const char *medInstrTitles0[];
+//const char *medInstrTitles1[];
+//const char *medInstrTitles2[];
+//const char *medInstrTitles3[];
+//const char *medInstrTitles4[];
+
+#endif //__HIDUSAGESTR_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/hidusagetitlearrays.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,1049 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__HIDUSAGETITLEARRAYS_H__)
+#define __HIDUSAGETITLEARRAYS_H__
+
+#include "hidusagestr.h"
+
+// This is here why?
+
+//const char *usagePageTitles0[]	PROGMEM =
+//{
+//	pstrUsagePageGenericDesktopControls	,
+//	pstrUsagePageSimulationControls		,
+//	pstrUsagePageVRControls				,
+//	pstrUsagePageSportControls			,
+//	pstrUsagePageGameControls			,
+//	pstrUsagePageGenericDeviceControls	,
+//	pstrUsagePageKeyboardKeypad			,
+//	pstrUsagePageLEDs					,
+//	pstrUsagePageButton					,
+//	pstrUsagePageOrdinal				,
+//	pstrUsagePageTelephone				,
+//	pstrUsagePageConsumer				,
+//	pstrUsagePageDigitizer				,
+//	pstrUsagePagePID					,
+//	pstrUsagePageUnicode
+//};
+//
+//const char *usagePageTitles1[]	PROGMEM =
+//{
+//	pstrUsagePageBarCodeScanner			,
+//	pstrUsagePageScale					,
+//	pstrUsagePageMSRDevices				,
+//	pstrUsagePagePointOfSale			,
+//	pstrUsagePageCameraControl			,
+//	pstrUsagePageArcade
+//};
+//const char *genDesktopTitles0[] PROGMEM =
+//{
+//	pstrUsagePointer					,
+//	pstrUsageMouse						,
+//	pstrUsageJoystick					,
+//	pstrUsageGamePad					,
+//	pstrUsageKeyboard					,
+//	pstrUsageKeypad						,
+//	pstrUsageMultiAxisController		,
+//	pstrUsageTabletPCSystemControls
+//
+//};
+//const char *genDesktopTitles1[] PROGMEM =
+//{
+//	pstrUsageX							,
+//	pstrUsageY							,
+//	pstrUsageZ							,
+//	pstrUsageRx							,
+//	pstrUsageRy							,
+//	pstrUsageRz							,
+//	pstrUsageSlider						,
+//	pstrUsageDial						,
+//	pstrUsageWheel						,
+//	pstrUsageHatSwitch					,
+//	pstrUsageCountedBuffer				,
+//	pstrUsageByteCount					,
+//	pstrUsageMotionWakeup				,
+//	pstrUsageStart						,
+//	pstrUsageSelect						,
+//	pstrUsagePageReserved				,
+//	pstrUsageVx							,
+//	pstrUsageVy							,
+//	pstrUsageVz							,
+//	pstrUsageVbrx						,
+//	pstrUsageVbry						,
+//	pstrUsageVbrz						,
+//	pstrUsageVno						,
+//	pstrUsageFeatureNotification		,
+//	pstrUsageResolutionMultiplier
+//};
+//const char *genDesktopTitles2[] PROGMEM =
+//{
+//	pstrUsageSystemControl		,
+//	pstrUsageSystemPowerDown	,
+//	pstrUsageSystemSleep		,
+//	pstrUsageSystemWakeup		,
+//	pstrUsageSystemContextMenu	,
+//	pstrUsageSystemMainMenu		,
+//	pstrUsageSystemAppMenu		,
+//	pstrUsageSystemMenuHelp		,
+//	pstrUsageSystemMenuExit		,
+//	pstrUsageSystemMenuSelect	,
+//	pstrUsageSystemMenuRight	,
+//	pstrUsageSystemMenuLeft		,
+//	pstrUsageSystemMenuUp		,
+//	pstrUsageSystemMenuDown		,
+//	pstrUsageSystemColdRestart	,
+//	pstrUsageSystemWarmRestart	,
+//	pstrUsageDPadUp				,
+//	pstrUsageDPadDown			,
+//	pstrUsageDPadRight			,
+//	pstrUsageDPadLeft
+//};
+//const char *genDesktopTitles3[] PROGMEM =
+//{
+//	pstrUsageSystemDock				,
+//	pstrUsageSystemUndock			,
+//	pstrUsageSystemSetup			,
+//	pstrUsageSystemBreak			,
+//	pstrUsageSystemDebuggerBreak	,
+//	pstrUsageApplicationBreak		,
+//	pstrUsageApplicationDebuggerBreak,
+//	pstrUsageSystemSpeakerMute		,
+//	pstrUsageSystemHibernate
+//};
+//const char *genDesktopTitles4[] PROGMEM =
+//{
+//	pstrUsageSystemDisplayInvert		,
+//	pstrUsageSystemDisplayInternal		,
+//	pstrUsageSystemDisplayExternal		,
+//	pstrUsageSystemDisplayBoth			,
+//	pstrUsageSystemDisplayDual			,
+//	pstrUsageSystemDisplayToggleIntExt	,
+//	pstrUsageSystemDisplaySwapPriSec	,
+//	pstrUsageSystemDisplayLCDAutoscale
+//};
+//const char *simuTitles0[] PROGMEM =
+//{
+//	pstrUsageFlightSimulationDevice		,
+//	pstrUsageAutomobileSimulationDevice	,
+//	pstrUsageTankSimulationDevice		,
+//	pstrUsageSpaceshipSimulationDevice	,
+//	pstrUsageSubmarineSimulationDevice	,
+//	pstrUsageSailingSimulationDevice	,
+//	pstrUsageMotocicleSimulationDevice	,
+//	pstrUsageSportsSimulationDevice		,
+//	pstrUsageAirplaneSimulationDevice	,
+//	pstrUsageHelicopterSimulationDevice	,
+//	pstrUsageMagicCarpetSimulationDevice,
+//	pstrUsageBicycleSimulationDevice
+//};
+//const char *simuTitles1[] PROGMEM =
+//{
+//	pstrUsageFlightControlStick			,
+//	pstrUsageFlightStick				,
+//	pstrUsageCyclicControl				,
+//	pstrUsageCyclicTrim					,
+//	pstrUsageFlightYoke					,
+//	pstrUsageTrackControl
+//};
+//const char *simuTitles2[] PROGMEM =
+//{
+//	pstrUsageAileron					,
+//	pstrUsageAileronTrim				,
+//	pstrUsageAntiTorqueControl			,
+//	pstrUsageAutopilotEnable			,
+//	pstrUsageChaffRelease				,
+//	pstrUsageCollectiveControl			,
+//	pstrUsageDiveBrake					,
+//	pstrUsageElectronicCountermeasures	,
+//	pstrUsageElevator					,
+//	pstrUsageElevatorTrim				,
+//	pstrUsageRudder						,
+//	pstrUsageThrottle					,
+//	pstrUsageFlightCommunications		,
+//	pstrUsageFlareRelease				,
+//	pstrUsageLandingGear				,
+//	pstrUsageToeBrake					,
+//	pstrUsageTrigger					,
+//	pstrUsageWeaponsArm					,
+//	pstrUsageWeaponsSelect				,
+//	pstrUsageWingFlaps					,
+//	pstrUsageAccelerator				,
+//	pstrUsageBrake						,
+//	pstrUsageClutch						,
+//	pstrUsageShifter					,
+//	pstrUsageSteering					,
+//	pstrUsageTurretDirection			,
+//	pstrUsageBarrelElevation			,
+//	pstrUsageDivePlane					,
+//	pstrUsageBallast					,
+//	pstrUsageBicycleCrank				,
+//	pstrUsageHandleBars					,
+//	pstrUsageFrontBrake					,
+//	pstrUsageRearBrake
+//};
+//const char *vrTitles0[]	PROGMEM =
+//{
+//	pstrUsageBelt				,
+//	pstrUsageBodySuit			,
+//	pstrUsageFlexor				,
+//	pstrUsageGlove				,
+//	pstrUsageHeadTracker		,
+//	pstrUsageHeadMountedDisplay	,
+//	pstrUsageHandTracker		,
+//	pstrUsageOculometer			,
+//	pstrUsageVest				,
+//	pstrUsageAnimatronicDevice
+//};
+//const char *vrTitles1[]	PROGMEM =
+//{
+//	pstrUsageStereoEnable	,
+//	pstrUsageDisplayEnable
+//};
+//const char *sportsCtrlTitles0[]	PROGMEM =
+//{
+//	pstrUsageBaseballBat				,
+//	pstrUsageGolfClub					,
+//	pstrUsageRowingMachine				,
+//	pstrUsageTreadmill
+//};
+//const char *sportsCtrlTitles1[]	PROGMEM =
+//{
+//	pstrUsageOar						,
+//	pstrUsageSlope						,
+//	pstrUsageRate						,
+//	pstrUsageStickSpeed					,
+//	pstrUsageStickFaceAngle				,
+//	pstrUsageStickHeelToe				,
+//	pstrUsageStickFollowThough			,
+//	pstrUsageStickTempo					,
+//	pstrUsageStickType					,
+//	pstrUsageStickHeight
+//};
+//const char *sportsCtrlTitles2[]	PROGMEM =
+//{
+//	pstrUsagePutter						,
+//	pstrUsage1Iron						,
+//	pstrUsage2Iron						,
+//	pstrUsage3Iron						,
+//	pstrUsage4Iron						,
+//	pstrUsage5Iron						,
+//	pstrUsage6Iron						,
+//	pstrUsage7Iron						,
+//	pstrUsage8Iron						,
+//	pstrUsage9Iron						,
+//	pstrUsage10Iron						,
+//	pstrUsage11Iron						,
+//	pstrUsageSandWedge					,
+//	pstrUsageLoftWedge					,
+//	pstrUsagePowerWedge					,
+//	pstrUsage1Wood						,
+//	pstrUsage3Wood						,
+//	pstrUsage5Wood						,
+//	pstrUsage7Wood						,
+//	pstrUsage9Wood
+//};
+//const char *gameTitles0[] PROGMEM =
+//{
+//	pstrUsage3DGameController		,
+//	pstrUsagePinballDevice			,
+//	pstrUsageGunDevice
+//};
+//const char *gameTitles1[] PROGMEM =
+//{
+//	pstrUsagePointOfView			,
+//	pstrUsageTurnRightLeft			,
+//	pstrUsagePitchForwardBackward	,
+//	pstrUsageRollRightLeft			,
+//	pstrUsageMoveRightLeft			,
+//	pstrUsageMoveForwardBackward	,
+//	pstrUsageMoveUpDown				,
+//	pstrUsageLeanRightLeft			,
+//	pstrUsageLeanForwardBackward	,
+//	pstrUsageHeightOfPOV			,
+//	pstrUsageFlipper				,
+//	pstrUsageSecondaryFlipper		,
+//	pstrUsageBump					,
+//	pstrUsageNewGame				,
+//	pstrUsageShootBall				,
+//	pstrUsagePlayer					,
+//	pstrUsageGunBolt				,
+//	pstrUsageGunClip				,
+//	pstrUsageGunSelector			,
+//	pstrUsageGunSingleShot			,
+//	pstrUsageGunBurst				,
+//	pstrUsageGunAutomatic			,
+//	pstrUsageGunSafety				,
+//	pstrUsageGamepadFireJump		,
+//	pstrUsageGamepadTrigger
+//};
+//const char *genDevCtrlTitles[] PROGMEM =
+//{
+//	pstrUsageBatteryStrength,
+//	pstrUsageWirelessChannel,
+//	pstrUsageWirelessID,
+//	pstrUsageDiscoverWirelessControl,
+//	pstrUsageSecurityCodeCharEntered,
+//	pstrUsageSecurityCodeCharErased,
+//	pstrUsageSecurityCodeCleared
+//};
+//const char *ledTitles[] PROGMEM =
+//{
+//	pstrUsageNumLock						,
+//	pstrUsageCapsLock					,
+//	pstrUsageScrollLock					,
+//	pstrUsageCompose					,
+//	pstrUsageKana						,
+//	pstrUsagePower						,
+//	pstrUsageShift						,
+//	pstrUsageDoNotDisturb				,
+//	pstrUsageMute						,
+//	pstrUsageToneEnable					,
+//	pstrUsageHighCutFilter				,
+//	pstrUsageLowCutFilter				,
+//	pstrUsageEqualizerEnable			,
+//	pstrUsageSoundFieldOn				,
+//	pstrUsageSurroundOn					,
+//	pstrUsageRepeat						,
+//	pstrUsageStereo						,
+//	pstrUsageSamplingRateDetect			,
+//	pstrUsageSpinning					,
+//	pstrUsageCAV						,
+//	pstrUsageCLV						,
+//	pstrUsageRecordingFormatDetect		,
+//	pstrUsageOffHook					,
+//	pstrUsageRing						,
+//	pstrUsageMessageWaiting				,
+//	pstrUsageDataMode					,
+//	pstrUsageBatteryOperation			,
+//	pstrUsageBatteryOK					,
+//	pstrUsageBatteryLow					,
+//	pstrUsageSpeaker					,
+//	pstrUsageHeadSet					,
+//	pstrUsageHold						,
+//	pstrUsageMicrophone					,
+//	pstrUsageCoverage					,
+//	pstrUsageNightMode					,
+//	pstrUsageSendCalls					,
+//	pstrUsageCallPickup					,
+//	pstrUsageConference					,
+//	pstrUsageStandBy					,
+//	pstrUsageCameraOn					,
+//	pstrUsageCameraOff					,
+//	pstrUsageOnLine						,
+//	pstrUsageOffLine					,
+//	pstrUsageBusy						,
+//	pstrUsageReady						,
+//	pstrUsagePaperOut					,
+//	pstrUsagePaperJam					,
+//	pstrUsageRemote						,
+//	pstrUsageForward					,
+//	pstrUsageReverse					,
+//	pstrUsageStop						,
+//	pstrUsageRewind						,
+//	pstrUsageFastForward				,
+//	pstrUsagePlay						,
+//	pstrUsagePause						,
+//	pstrUsageRecord						,
+//	pstrUsageError						,
+//	pstrUsageSelectedIndicator			,
+//	pstrUsageInUseIndicator				,
+//	pstrUsageMultiModeIndicator			,
+//	pstrUsageIndicatorOn				,
+//	pstrUsageIndicatorFlash				,
+//	pstrUsageIndicatorSlowBlink			,
+//	pstrUsageIndicatorFastBlink			,
+//	pstrUsageIndicatorOff				,
+//	pstrUsageFlashOnTime				,
+//	pstrUsageSlowBlinkOnTime			,
+//	pstrUsageSlowBlinkOffTime			,
+//	pstrUsageFastBlinkOnTime			,
+//	pstrUsageFastBlinkOffTime			,
+//	pstrUsageIndicatorColor				,
+//	pstrUsageIndicatorRed				,
+//	pstrUsageIndicatorGreen				,
+//	pstrUsageIndicatorAmber				,
+//	pstrUsageGenericIndicator			,
+//	pstrUsageSystemSuspend				,
+//	pstrUsageExternalPowerConnected
+//};
+//const char *telTitles0			[] PROGMEM =
+//{
+//	pstrUsagePhone				,
+//	pstrUsageAnsweringMachine	,
+//	pstrUsageMessageControls	,
+//	pstrUsageHandset			,
+//	pstrUsageHeadset			,
+//	pstrUsageTelephonyKeyPad	,
+//	pstrUsageProgrammableButton
+//};
+//const char *telTitles1			[] PROGMEM =
+//{
+//	pstrUsageHookSwitch					,
+//	pstrUsageFlash						,
+//	pstrUsageFeature					,
+//	pstrUsageHold						,
+//	pstrUsageRedial						,
+//	pstrUsageTransfer					,
+//	pstrUsageDrop						,
+//	pstrUsagePark						,
+//	pstrUsageForwardCalls				,
+//	pstrUsageAlternateFunction			,
+//	pstrUsageLine						,
+//	pstrUsageSpeakerPhone				,
+//	pstrUsageConference				,
+//	pstrUsageRingEnable				,
+//	pstrUsageRingSelect				,
+//	pstrUsagePhoneMute				,
+//	pstrUsageCallerID				,
+//	pstrUsageSend
+//};
+//const char *telTitles2			[] PROGMEM =
+//{
+//	pstrUsageSpeedDial		,
+//	pstrUsageStoreNumber	,
+//	pstrUsageRecallNumber	,
+//	pstrUsagePhoneDirectory
+//};
+//const char *telTitles3			[] PROGMEM =
+//{
+//	pstrUsageVoiceMail		,
+//	pstrUsageScreenCalls	,
+//	pstrUsageDoNotDisturb	,
+//	pstrUsageMessage		,
+//	pstrUsageAnswerOnOff
+//};
+//const char *telTitles4			[] PROGMEM =
+//{
+//	pstrUsageInsideDialTone			,
+//	pstrUsageOutsideDialTone		,
+//	pstrUsageInsideRingTone			,
+//	pstrUsageOutsideRingTone		,
+//	pstrUsagePriorityRingTone		,
+//	pstrUsageInsideRingback			,
+//	pstrUsagePriorityRingback		,
+//	pstrUsageLineBusyTone			,
+//	pstrUsageReorderTone			,
+//	pstrUsageCallWaitingTone		,
+//	pstrUsageConfirmationTone1		,
+//	pstrUsageConfirmationTone2		,
+//	pstrUsageTonesOff				,
+//	pstrUsageOutsideRingback		,
+//	pstrUsageRinger
+//};
+//const char *telTitles5			[] PROGMEM =
+//{
+//	pstrUsagePhoneKey0		,
+//	pstrUsagePhoneKey1		,
+//	pstrUsagePhoneKey2		,
+//	pstrUsagePhoneKey3		,
+//	pstrUsagePhoneKey4		,
+//	pstrUsagePhoneKey5		,
+//	pstrUsagePhoneKey6		,
+//	pstrUsagePhoneKey7		,
+//	pstrUsagePhoneKey8		,
+//	pstrUsagePhoneKey9		,
+//	pstrUsagePhoneKeyStar	,
+//	pstrUsagePhoneKeyPound	,
+//	pstrUsagePhoneKeyA		,
+//	pstrUsagePhoneKeyB		,
+//	pstrUsagePhoneKeyC		,
+//	pstrUsagePhoneKeyD
+//};
+//const char *consTitles0[]	PROGMEM	=
+//{
+//	pstrUsageConsumerControl,
+//	pstrUsageNumericKeyPad,
+//	pstrUsageProgrammableButton,
+//	pstrUsageMicrophone,
+//	pstrUsageHeadphone,
+//	pstrUsageGraphicEqualizer
+//};
+//const char *consTitles1[]	PROGMEM	=
+//{
+//	pstrUsagePlus10	,
+//	pstrUsagePlus100,
+//	pstrUsageAMPM
+//};
+//const char *consTitles2[]	PROGMEM	=
+//{
+//	pstrUsagePower			,
+//	pstrUsageReset			,
+//	pstrUsageSleep			,
+//	pstrUsageSleepAfter		,
+//	pstrUsageSleepMode		,
+//	pstrUsageIllumination	,
+//	pstrUsageFunctionButtons
+//
+//};
+//const char *consTitles3[]	PROGMEM	=
+//{
+//	pstrUsageMenu			,
+//	pstrUsageMenuPick		,
+//	pstrUsageMenuUp			,
+//	pstrUsageMenuDown		,
+//	pstrUsageMenuLeft		,
+//	pstrUsageMenuRight		,
+//	pstrUsageMenuEscape		,
+//	pstrUsageMenuValueIncrease,
+//	pstrUsageMenuValueDecrease
+//};
+//const char *consTitles4[]	PROGMEM	=
+//{
+//	pstrUsageDataOnScreen		,
+//	pstrUsageClosedCaption		,
+//	pstrUsageClosedCaptionSelect,
+//	pstrUsageVCRTV				,
+//	pstrUsageBroadcastMode		,
+//	pstrUsageSnapshot			,
+//	pstrUsageStill
+//};
+//const char *consTitles5[]	PROGMEM	=
+//{
+//	pstrUsageSelection					,
+//	pstrUsageAssignSelection			,
+//	pstrUsageModeStep					,
+//	pstrUsageRecallLast					,
+//	pstrUsageEnterChannel				,
+//	pstrUsageOrderMovie					,
+//	pstrUsageChannel					,
+//	pstrUsageMediaSelection				,
+//	pstrUsageMediaSelectComputer		,
+//	pstrUsageMediaSelectTV				,
+//	pstrUsageMediaSelectWWW				,
+//	pstrUsageMediaSelectDVD				,
+//	pstrUsageMediaSelectTelephone		,
+//	pstrUsageMediaSelectProgramGuide	,
+//	pstrUsageMediaSelectVideoPhone		,
+//	pstrUsageMediaSelectGames			,
+//	pstrUsageMediaSelectMessages		,
+//	pstrUsageMediaSelectCD				,
+//	pstrUsageMediaSelectVCR				,
+//	pstrUsageMediaSelectTuner			,
+//	pstrUsageQuit						,
+//	pstrUsageHelp						,
+//	pstrUsageMediaSelectTape			,
+//	pstrUsageMediaSelectCable			,
+//	pstrUsageMediaSelectSatellite		,
+//	pstrUsageMediaSelectSecurity		,
+//	pstrUsageMediaSelectHome			,
+//	pstrUsageMediaSelectCall			,
+//	pstrUsageChannelIncrement			,
+//	pstrUsageChannelDecrement			,
+//	pstrUsageMediaSelectSAP				,
+//	pstrUsagePageReserved				,
+//	pstrUsageVCRPlus					,
+//	pstrUsageOnce						,
+//	pstrUsageDaily						,
+//	pstrUsageWeekly						,
+//	pstrUsageMonthly
+//};
+//const char *consTitles6[]	PROGMEM	=
+//{
+//	pstrUsagePlay					,
+//	pstrUsagePause					,
+//	pstrUsageRecord					,
+//	pstrUsageFastForward			,
+//	pstrUsageRewind					,
+//	pstrUsageScanNextTrack			,
+//	pstrUsageScanPreviousTrack		,
+//	pstrUsageStop					,
+//	pstrUsageEject					,
+//	pstrUsageRandomPlay				,
+//	pstrUsageSelectDisk				,
+//	pstrUsageEnterDisk				,
+//	pstrUsageRepeat					,
+//	pstrUsageTracking					,
+//	pstrUsageTrackNormal				,
+//	pstrUsageSlowTracking				,
+//	pstrUsageFrameForward				,
+//	pstrUsageFrameBackwards				,
+//	pstrUsageMark						,
+//	pstrUsageClearMark					,
+//	pstrUsageRepeatFromMark				,
+//	pstrUsageReturnToMark				,
+//	pstrUsageSearchMarkForward			,
+//	pstrUsageSearchMarkBackwards		,
+//	pstrUsageCounterReset				,
+//	pstrUsageShowCounter				,
+//	pstrUsageTrackingIncrement			,
+//	pstrUsageTrackingDecrement			,
+//	pstrUsageStopEject					,
+//	pstrUsagePlayPause					,
+//	pstrUsagePlaySkip
+//};
+//const char *consTitles7[]	PROGMEM	=
+//{
+//	pstrUsageVolume						,
+//	pstrUsageBalance					,
+//	pstrUsageMute						,
+//	pstrUsageBass						,
+//	pstrUsageTreble						,
+//	pstrUsageBassBoost					,
+//	pstrUsageSurroundMode				,
+//	pstrUsageLoudness					,
+//	pstrUsageMPX						,
+//	pstrUsageVolumeIncrement			,
+//	pstrUsageVolumeDecrement
+//};
+//const char *consTitles8[]	PROGMEM	=
+//{
+//	pstrUsageSpeedSelect				,
+//	pstrUsagePlaybackSpeed				,
+//	pstrUsageStandardPlay				,
+//	pstrUsageLongPlay					,
+//	pstrUsageExtendedPlay				,
+//	pstrUsageSlow
+//};
+//const char *consTitles9[]	PROGMEM	=
+//{
+//	pstrUsageFanEnable					,
+//	pstrUsageFanSpeed					,
+//	pstrUsageLightEnable				,
+//	pstrUsageLightIlluminationLevel		,
+//	pstrUsageClimateControlEnable		,
+//	pstrUsageRoomTemperature			,
+//	pstrUsageSecurityEnable				,
+//	pstrUsageFireAlarm					,
+//	pstrUsagePoliceAlarm				,
+//	pstrUsageProximity					,
+//	pstrUsageMotion						,
+//	pstrUsageDuresAlarm					,
+//	pstrUsageHoldupAlarm					,
+//	pstrUsageMedicalAlarm
+//};
+//const char *consTitlesA[]	PROGMEM	=
+//{
+//	pstrUsageBalanceRight				,
+//	pstrUsageBalanceLeft				,
+//	pstrUsageBassIncrement				,
+//	pstrUsageBassDecrement				,
+//	pstrUsageTrebleIncrement			,
+//	pstrUsageTrebleDecrement
+//};
+//const char *consTitlesB[]	PROGMEM	=
+//{
+//	pstrUsageSpeakerSystem				,
+//	pstrUsageChannelLeft				,
+//	pstrUsageChannelRight				,
+//	pstrUsageChannelCenter				,
+//	pstrUsageChannelFront				,
+//	pstrUsageChannelCenterFront			,
+//	pstrUsageChannelSide				,
+//	pstrUsageChannelSurround			,
+//	pstrUsageChannelLowFreqEnhancement	,
+//	pstrUsageChannelTop					,
+//	pstrUsageChannelUnknown
+//};
+//const char *consTitlesC[]	PROGMEM	=
+//{
+//	pstrUsageSubChannel					,
+//	pstrUsageSubChannelIncrement		,
+//	pstrUsageSubChannelDecrement		,
+//	pstrUsageAlternateAudioIncrement	,
+//	pstrUsageAlternateAudioDecrement
+//};
+//const char *consTitlesD[]	PROGMEM	=
+//{
+//	pstrUsageApplicationLaunchButtons	,
+//	pstrUsageALLaunchButtonConfigTool	,
+//	pstrUsageALProgrammableButton		,
+//	pstrUsageALConsumerControlConfig	,
+//	pstrUsageALWordProcessor			,
+//	pstrUsageALTextEditor				,
+//	pstrUsageALSpreadsheet				,
+//	pstrUsageALGraphicsEditor			,
+//	pstrUsageALPresentationApp			,
+//	pstrUsageALDatabaseApp				,
+//	pstrUsageALEmailReader				,
+//	pstrUsageALNewsreader				,
+//	pstrUsageALVoicemail				,
+//	pstrUsageALContactsAddressBook		,
+//	pstrUsageALCalendarSchedule			,
+//	pstrUsageALTaskProjectManager		,
+//	pstrUsageALLogJournalTimecard		,
+//	pstrUsageALCheckbookFinance			,
+//	pstrUsageALCalculator				,
+//	pstrUsageALAVCapturePlayback		,
+//	pstrUsageALLocalMachineBrowser		,
+//	pstrUsageALLANWANBrow				,
+//	pstrUsageALInternetBrowser			,
+//	pstrUsageALRemoteNetISPConnect		,
+//	pstrUsageALNetworkConference		,
+//	pstrUsageALNetworkChat				,
+//	pstrUsageALTelephonyDialer			,
+//	pstrUsageALLogon					,
+//	pstrUsageALLogoff					,
+//	pstrUsageALLogonLogoff				,
+//	pstrUsageALTermLockScrSav			,
+//	pstrUsageALControlPannel			,
+//	pstrUsageALCommandLineProcessorRun	,
+//	pstrUsageALProcessTaskManager		,
+//	pstrUsageALSelectTaskApplication	,
+//	pstrUsageALNextTaskApplication		,
+//	pstrUsageALPreviousTaskApplication	,
+//	pstrUsageALPreemptiveHaltTaskApp	,
+//	pstrUsageALIntegratedHelpCenter		,
+//	pstrUsageALDocuments				,
+//	pstrUsageALThesaurus				,
+//	pstrUsageALDictionary				,
+//	pstrUsageALDesktop					,
+//	pstrUsageALSpellCheck				,
+//	pstrUsageALGrammarCheck				,
+//	pstrUsageALWirelessStatus			,
+//	pstrUsageALKeyboardLayout			,
+//	pstrUsageALVirusProtection			,
+//	pstrUsageALEncryption				,
+//	pstrUsageALScreenSaver				,
+//	pstrUsageALAlarms					,
+//	pstrUsageALClock					,
+//	pstrUsageALFileBrowser				,
+//	pstrUsageALPowerStatus				,
+//	pstrUsageALImageBrowser				,
+//	pstrUsageALAudioBrowser				,
+//	pstrUsageALMovieBrowser				,
+//	pstrUsageALDigitalRightsManager		,
+//	pstrUsageALDigitalWallet			,
+//	pstrUsagePageReserved				,
+//	pstrUsageALInstantMessaging			,
+//	pstrUsageALOEMFeaturesBrowser		,
+//	pstrUsageALOEMHelp					,
+//	pstrUsageALOnlineCommunity			,
+//	pstrUsageALEntertainmentContentBrow	,
+//	pstrUsageALOnlineShoppingBrowser	,
+//	pstrUsageALSmartCardInfoHelp		,
+//	pstrUsageALMarketMonitorFinBrowser	,
+//	pstrUsageALCustomCorpNewsBrowser		,
+//	pstrUsageALOnlineActivityBrowser		,
+//	pstrUsageALResearchSearchBrowser		,
+//	pstrUsageALAudioPlayer
+//};
+//const char *consTitlesE[]	PROGMEM	=
+//{
+//	pstrUsageGenericGUIAppControls		,
+//	pstrUsageACNew						,
+//	pstrUsageACOpen						,
+//	pstrUsageACClose					,
+//	pstrUsageACExit						,
+//	pstrUsageACMaximize					,
+//	pstrUsageACMinimize					,
+//	pstrUsageACSave						,
+//	pstrUsageACPrint					,
+//	pstrUsageACProperties				,
+//	pstrUsageACUndo						,
+//	pstrUsageACCopy						,
+//	pstrUsageACCut						,
+//	pstrUsageACPaste					,
+//	pstrUsageACSelectAll				,
+//	pstrUsageACFind						,
+//	pstrUsageACFindAndReplace			,
+//	pstrUsageACSearch					,
+//	pstrUsageACGoto						,
+//	pstrUsageACHome						,
+//	pstrUsageACBack						,
+//	pstrUsageACForward					,
+//	pstrUsageACStop						,
+//	pstrUsageACRefresh					,
+//	pstrUsageACPreviousLink				,
+//	pstrUsageACNextLink					,
+//	pstrUsageACBookmarks				,
+//	pstrUsageACHistory					,
+//	pstrUsageACSubscriptions			,
+//	pstrUsageACZoomIn					,
+//	pstrUsageACZoomOut					,
+//	pstrUsageACZoom						,
+//	pstrUsageACFullScreenView			,
+//	pstrUsageACNormalView				,
+//	pstrUsageACViewToggle				,
+//	pstrUsageACScrollUp					,
+//	pstrUsageACScrollDown				,
+//	pstrUsageACScroll					,
+//	pstrUsageACPanLeft					,
+//	pstrUsageACPanRight					,
+//	pstrUsageACPan						,
+//	pstrUsageACNewWindow				,
+//	pstrUsageACTileHoriz				,
+//	pstrUsageACTileVert					,
+//	pstrUsageACFormat					,
+//	pstrUsageACEdit						,
+//	pstrUsageACBold						,
+//	pstrUsageACItalics					,
+//	pstrUsageACUnderline				,
+//	pstrUsageACStrikethrough			,
+//	pstrUsageACSubscript				,
+//	pstrUsageACSuperscript				,
+//	pstrUsageACAllCaps					,
+//	pstrUsageACRotate					,
+//	pstrUsageACResize					,
+//	pstrUsageACFlipHorizontal			,
+//	pstrUsageACFlipVertical				,
+//	pstrUsageACMirrorHorizontal			,
+//	pstrUsageACMirrorVertical			,
+//	pstrUsageACFontSelect				,
+//	pstrUsageACFontColor				,
+//	pstrUsageACFontSize					,
+//	pstrUsageACJustifyLeft				,
+//	pstrUsageACJustifyCenterH			,
+//	pstrUsageACJustifyRight				,
+//	pstrUsageACJustifyBlockH			,
+//	pstrUsageACJustifyTop				,
+//	pstrUsageACJustifyCenterV			,
+//	pstrUsageACJustifyBottom			,
+//	pstrUsageACJustifyBlockV			,
+//	pstrUsageACIndentDecrease			,
+//	pstrUsageACIndentIncrease			,
+//	pstrUsageACNumberedList				,
+//	pstrUsageACRestartNumbering			,
+//	pstrUsageACBulletedList				,
+//	pstrUsageACPromote					,
+//	pstrUsageACDemote					,
+//	pstrUsageACYes						,
+//	pstrUsageACNo						,
+//	pstrUsageACCancel					,
+//	pstrUsageACCatalog					,
+//	pstrUsageACBuyChkout				,
+//	pstrUsageACAddToCart				,
+//	pstrUsageACExpand					,
+//	pstrUsageACExpandAll				,
+//	pstrUsageACCollapse					,
+//	pstrUsageACCollapseAll				,
+//	pstrUsageACPrintPreview				,
+//	pstrUsageACPasteSpecial				,
+//	pstrUsageACInsertMode				,
+//	pstrUsageACDelete					,
+//	pstrUsageACLock						,
+//	pstrUsageACUnlock					,
+//	pstrUsageACProtect					,
+//	pstrUsageACUnprotect				,
+//	pstrUsageACAttachComment			,
+//	pstrUsageACDeleteComment			,
+//	pstrUsageACViewComment				,
+//	pstrUsageACSelectWord				,
+//	pstrUsageACSelectSentence			,
+//	pstrUsageACSelectParagraph			,
+//	pstrUsageACSelectColumn				,
+//	pstrUsageACSelectRow				,
+//	pstrUsageACSelectTable				,
+//	pstrUsageACSelectObject				,
+//	pstrUsageACRedoRepeat				,
+//	pstrUsageACSort						,
+//	pstrUsageACSortAscending			,
+//	pstrUsageACSortDescending			,
+//	pstrUsageACFilter					,
+//	pstrUsageACSetClock					,
+//	pstrUsageACViewClock				,
+//	pstrUsageACSelectTimeZone			,
+//	pstrUsageACEditTimeZone				,
+//	pstrUsageACSetAlarm					,
+//	pstrUsageACClearAlarm				,
+//	pstrUsageACSnoozeAlarm				,
+//	pstrUsageACResetAlarm				,
+//	pstrUsageACSyncronize				,
+//	pstrUsageACSendReceive				,
+//	pstrUsageACSendTo					,
+//	pstrUsageACReply					,
+//	pstrUsageACReplyAll					,
+//	pstrUsageACForwardMessage			,
+//	pstrUsageACSend						,
+//	pstrUsageACAttachFile				,
+//	pstrUsageACUpload					,
+//	pstrUsageACDownload					,
+//	pstrUsageACSetBorders				,
+//	pstrUsageACInsertRow				,
+//	pstrUsageACInsertColumn				,
+//	pstrUsageACInsertFile				,
+//	pstrUsageACInsertPicture			,
+//	pstrUsageACInsertObject				,
+//	pstrUsageACInsertSymbol				,
+//	pstrUsageACSaveAndClose				,
+//	pstrUsageACRename					,
+//	pstrUsageACMerge					,
+//	pstrUsageACSplit					,
+//	pstrUsageACDistributeHorizontaly	,
+//	pstrUsageACDistributeVerticaly
+//};
+//const char *digitTitles0[] PROGMEM =
+//{
+//	pstrUsageDigitizer					,
+//	pstrUsagePen						,
+//	pstrUsageLightPen					,
+//	pstrUsageTouchScreen				,
+//	pstrUsageTouchPad					,
+//	pstrUsageWhiteBoard					,
+//	pstrUsageCoordinateMeasuringMachine	,
+//	pstrUsage3DDigitizer				,
+//	pstrUsageStereoPlotter				,
+//	pstrUsageArticulatedArm				,
+//	pstrUsageArmature					,
+//	pstrUsageMultiplePointDigitizer		,
+//	pstrUsageFreeSpaceWand
+//};
+//const char *digitTitles1[] PROGMEM =
+//{
+//	pstrUsageStylus						,
+//	pstrUsagePuck						,
+//	pstrUsageFinger
+//
+//};
+//const char *digitTitles2[] PROGMEM =
+//{
+//	pstrUsageTipPressure			,
+//	pstrUsageBarrelPressure			,
+//	pstrUsageInRange				,
+//	pstrUsageTouch					,
+//	pstrUsageUntouch				,
+//	pstrUsageTap					,
+//	pstrUsageQuality				,
+//	pstrUsageDataValid				,
+//	pstrUsageTransducerIndex		,
+//	pstrUsageTabletFunctionKeys		,
+//	pstrUsageProgramChangeKeys		,
+//	pstrUsageBatteryStrength		,
+//	pstrUsageInvert					,
+//	pstrUsageXTilt					,
+//	pstrUsageYTilt					,
+//	pstrUsageAzimuth				,
+//	pstrUsageAltitude				,
+//	pstrUsageTwist					,
+//	pstrUsageTipSwitch				,
+//	pstrUsageSecondaryTipSwitch		,
+//	pstrUsageBarrelSwitch			,
+//	pstrUsageEraser					,
+//	pstrUsageTabletPick
+//};
+//const char *aplphanumTitles0[]	PROGMEM =
+//{
+//	pstrUsageAlphanumericDisplay,
+//	pstrUsageBitmappedDisplay
+//};
+//const char *aplphanumTitles1[]	PROGMEM =
+//{
+//	pstrUsageDisplayAttributesReport	,
+//	pstrUsageASCIICharacterSet			,
+//	pstrUsageDataReadBack				,
+//	pstrUsageFontReadBack				,
+//	pstrUsageDisplayControlReport		,
+//	pstrUsageClearDisplay				,
+//	pstrUsageDisplayEnable				,
+//	pstrUsageScreenSaverDelay			,
+//	pstrUsageScreenSaverEnable			,
+//	pstrUsageVerticalScroll				,
+//	pstrUsageHorizontalScroll			,
+//	pstrUsageCharacterReport			,
+//	pstrUsageDisplayData				,
+//	pstrUsageDisplayStatus				,
+//	pstrUsageStatusNotReady				,
+//	pstrUsageStatusReady				,
+//	pstrUsageErrorNotALoadableCharacter	,
+//	pstrUsageErrorFotDataCanNotBeRead	,
+//	pstrUsageCursorPositionReport		,
+//	pstrUsageRow						,
+//	pstrUsageColumn						,
+//	pstrUsageRows						,
+//	pstrUsageColumns					,
+//	pstrUsageCursorPixelPosition		,
+//	pstrUsageCursorMode					,
+//	pstrUsageCursorEnable				,
+//	pstrUsageCursorBlink				,
+//	pstrUsageFontReport					,
+//	pstrUsageFontData					,
+//	pstrUsageCharacterWidth				,
+//	pstrUsageCharacterHeight			,
+//	pstrUsageCharacterSpacingHorizontal	,
+//	pstrUsageCharacterSpacingVertical	,
+//	pstrUsageUnicodeCharset				,
+//	pstrUsageFont7Segment				,
+//	pstrUsage7SegmentDirectMap			,
+//	pstrUsageFont14Segment				,
+//	pstrUsage14SegmentDirectMap			,
+//	pstrUsageDisplayBrightness			,
+//	pstrUsageDisplayContrast			,
+//	pstrUsageCharacterAttribute			,
+//	pstrUsageAttributeReadback			,
+//	pstrUsageAttributeData				,
+//	pstrUsageCharAttributeEnhance		,
+//	pstrUsageCharAttributeUnderline		,
+//	pstrUsageCharAttributeBlink
+//};
+//const char *aplphanumTitles2[]	PROGMEM =
+//{
+//	pstrUsageBitmapSizeX				,
+//	pstrUsageBitmapSizeY				,
+//	pstrUsagePageReserved				,
+//	pstrUsageBitDepthFormat				,
+//	pstrUsageDisplayOrientation			,
+//	pstrUsagePaletteReport				,
+//	pstrUsagePaletteDataSize			,
+//	pstrUsagePaletteDataOffset			,
+//	pstrUsagePaletteData				,
+//	pstrUsageBlitReport					,
+//	pstrUsageBlitRectangleX1			,
+//	pstrUsageBlitRectangleY1			,
+//	pstrUsageBlitRectangleX2			,
+//	pstrUsageBlitRectangleY2			,
+//	pstrUsageBlitData					,
+//	pstrUsageSoftButton					,
+//	pstrUsageSoftButtonID				,
+//	pstrUsageSoftButtonSide				,
+//	pstrUsageSoftButtonOffset1			,
+//	pstrUsageSoftButtonOffset2			,
+//	pstrUsageSoftButtonReport
+//};
+//const char *medInstrTitles0[] PROGMEM =
+//{
+//	pstrUsageVCRAcquisition				,
+//	pstrUsageFreezeThaw					,
+//	pstrUsageClipStore					,
+//	pstrUsageUpdate						,
+//	pstrUsageNext						,
+//	pstrUsageSave						,
+//	pstrUsagePrint						,
+//	pstrUsageMicrophoneEnable
+//};
+//const char *medInstrTitles1[] PROGMEM =
+//{
+//	pstrUsageCine						,
+//	pstrUsageTransmitPower				,
+//	pstrUsageVolume						,
+//	pstrUsageFocus						,
+//	pstrUsageDepth
+//};
+//const char *medInstrTitles2[] PROGMEM =
+//{
+//	pstrUsageSoftStepPrimary		,
+//	pstrUsageSoftStepSecondary
+//};
+//const char *medInstrTitles3[] PROGMEM =
+//{
+//	pstrUsageZoomSelect					,
+//	pstrUsageZoomAdjust					,
+//	pstrUsageSpectralDopplerModeSelect	,
+//	pstrUsageSpectralDopplerModeAdjust	,
+//	pstrUsageColorDopplerModeSelect		,
+//	pstrUsageColorDopplerModeAdjust		,
+//	pstrUsageMotionModeSelect			,
+//	pstrUsageMotionModeAdjust			,
+//	pstrUsage2DModeSelect				,
+//	pstrUsage2DModeAdjust
+//};
+//const char *medInstrTitles4[] PROGMEM =
+//{
+//	pstrUsageSoftControlSelect			,
+//	pstrUsageSoftControlAdjust
+//};
+
+#endif // __HIDUSAGETITLEARRAYS_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/macros.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,90 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(_usb_h_) || defined(MACROS_H)
+#error "Never include macros.h directly; include Usb.h instead"
+#else
+#define MACROS_H
+
+////////////////////////////////////////////////////////////////////////////////
+// HANDY MACROS
+////////////////////////////////////////////////////////////////////////////////
+
+#define VALUE_BETWEEN(v,l,h) (((v)>(l)) && ((v)<(h)))
+#define VALUE_WITHIN(v,l,h) (((v)>=(l)) && ((v)<=(h)))
+#define output_pgm_message(wa,fp,mp,el) wa = &mp, fp((char *)pgm_read_pointer(wa), el)
+#define output_if_between(v,l,h,wa,fp,mp,el) if(VALUE_BETWEEN(v,l,h)) output_pgm_message(wa,fp,mp[v-(l+1)],el);
+
+#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))
+#ifndef __BYTE_GRABBING_DEFINED__
+#define __BYTE_GRABBING_DEFINED__ 1
+#ifdef BROKEN_OPTIMIZER_LITTLE_ENDIAN
+// Note: Use this if your compiler generates horrible assembler!
+#define BGRAB0(__usi__)  (((uint8_t *)&(__usi__))[0])
+#define BGRAB1(__usi__)  (((uint8_t *)&(__usi__))[1])
+#define BGRAB2(__usi__)  (((uint8_t *)&(__usi__))[2])
+#define BGRAB3(__usi__)  (((uint8_t *)&(__usi__))[3])
+#define BGRAB4(__usi__)  (((uint8_t *)&(__usi__))[4])
+#define BGRAB5(__usi__)  (((uint8_t *)&(__usi__))[5])
+#define BGRAB6(__usi__)  (((uint8_t *)&(__usi__))[6])
+#define BGRAB7(__usi__)  (((uint8_t *)&(__usi__))[7])
+#else
+// Note: The cast alone to uint8_t is actually enough.
+// GCC throws out the "& 0xff", and the size is no different.
+// Some compilers need it.
+#define BGRAB0(__usi__)  ((uint8_t)((__usi__) & 0xff ))
+#define BGRAB1(__usi__)  ((uint8_t)(((__usi__) >> 8) & 0xff))
+#define BGRAB2(__usi__)  ((uint8_t)(((__usi__) >> 16) & 0xff))
+#define BGRAB3(__usi__)  ((uint8_t)(((__usi__) >> 24) & 0xff))
+#define BGRAB4(__usi__)  ((uint8_t)(((__usi__) >> 32) & 0xff))
+#define BGRAB5(__usi__)  ((uint8_t)(((__usi__) >> 40) & 0xff))
+#define BGRAB6(__usi__)  ((uint8_t)(((__usi__) >> 48) & 0xff))
+#define BGRAB7(__usi__)  ((uint8_t)(((__usi__) >> 56) & 0xff))
+#endif
+#define BOVER1(__usi__)  ((uint16_t)(__usi__) << 8)
+#define BOVER2(__usi__)  ((uint32_t)(__usi__) << 16)
+#define BOVER3(__usi__)  ((uint32_t)(__usi__) << 24)
+#define BOVER4(__usi__)  ((uint64_t)(__usi__) << 32)
+#define BOVER5(__usi__)  ((uint64_t)(__usi__) << 40)
+#define BOVER6(__usi__)  ((uint64_t)(__usi__) << 48)
+#define BOVER7(__usi__)  ((uint64_t)(__usi__) << 56)
+
+// These are the smallest and fastest ways I have found so far in pure C/C++.
+#define BMAKE16(__usc1__,__usc0__) ((uint16_t)((uint16_t)(__usc0__) | (uint16_t)BOVER1(__usc1__)))
+#define BMAKE32(__usc3__,__usc2__,__usc1__,__usc0__) ((uint32_t)((uint32_t)(__usc0__) | (uint32_t)BOVER1(__usc1__) | (uint32_t)BOVER2(__usc2__) | (uint32_t)BOVER3(__usc3__)))
+#define BMAKE64(__usc7__,__usc6__,__usc5__,__usc4__,__usc3__,__usc2__,__usc1__,__usc0__) ((uint64_t)((uint64_t)__usc0__ | (uint64_t)BOVER1(__usc1__) | (uint64_t)BOVER2(__usc2__) | (uint64_t)BOVER3(__usc3__) | (uint64_t)BOVER4(__usc4__) | (uint64_t)BOVER5(__usc5__) | (uint64_t)BOVER6(__usc6__) | (uint64_t)BOVER1(__usc7__)))
+#endif
+
+/*
+ * Debug macros: Strings are stored in progmem (flash) instead of RAM.
+ */
+#define USBTRACE(s) (Notify(PSTR(s), 0x80))
+#define USBTRACE1(s,l) (Notify(PSTR(s), l))
+#define USBTRACE2(s,r) (Notify(PSTR(s), 0x80), D_PrintHex((r), 0x80), Notify(PSTR("\r\n"), 0x80))
+#define USBTRACE3(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l), Notify(PSTR("\r\n"), l))
+
+
+#endif /* MACROS_H */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/masstorage.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,1274 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#include "masstorage.h"
+
+const uint8_t BulkOnly::epDataInIndex = 1;
+const uint8_t BulkOnly::epDataOutIndex = 2;
+const uint8_t BulkOnly::epInterruptInIndex = 3;
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Interface code
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Get the capacity of the media
+ *
+ * @param lun Logical Unit Number
+ * @return media capacity
+ */
+uint32_t BulkOnly::GetCapacity(uint8_t lun) {
+        if(LUNOk[lun])
+                return CurrentCapacity[lun];
+        return 0LU;
+}
+
+/**
+ * Get the sector (block) size used on the media
+ *
+ * @param lun Logical Unit Number
+ * @return media sector size
+ */
+uint16_t BulkOnly::GetSectorSize(uint8_t lun) {
+        if(LUNOk[lun])
+                return CurrentSectorSize[lun];
+        return 0U;
+}
+
+/**
+ * Test if LUN is ready for use
+ *
+ * @param lun Logical Unit Number
+ * @return true if LUN is ready for use
+ */
+bool BulkOnly::LUNIsGood(uint8_t lun) {
+        return LUNOk[lun];
+}
+
+/**
+ * Test if LUN is write protected
+ *
+ * @param lun Logical Unit Number
+ * @return cached status of write protect switch
+ */
+bool BulkOnly::WriteProtected(uint8_t lun) {
+        return WriteOk[lun];
+}
+
+/**
+ * Wrap and execute a SCSI CDB with length of 6
+ *
+ * @param cdb CDB to execute
+ * @param buf_size Size of expected transaction
+ * @param buf Buffer
+ * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT
+ * @return
+ */
+uint8_t BulkOnly::SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {
+        // promote buf_size to 32bits.
+        CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);
+        //SetCurLUN(cdb->LUN);
+        return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));
+}
+
+/**
+ * Wrap and execute a SCSI CDB with length of 10
+ *
+ * @param cdb CDB to execute
+ * @param buf_size Size of expected transaction
+ * @param buf Buffer
+ * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT
+ * @return
+ */
+uint8_t BulkOnly::SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {
+        // promote buf_size to 32bits.
+        CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);
+        //SetCurLUN(cdb->LUN);
+        return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));
+}
+
+/**
+ * Lock or Unlock the tray or door on device.
+ * Caution: Some devices with buggy firmware will lock up.
+ *
+ * @param lun Logical Unit Number
+ * @param lock 1 to lock, 0 to unlock
+ * @return
+ */
+uint8_t BulkOnly::LockMedia(uint8_t lun, uint8_t lock) {
+        Notify(PSTR("\r\nLockMedia\r\n"), 0x80);
+        Notify(PSTR("---------\r\n"), 0x80);
+
+        CDB6_t cdb = CDB6_t(SCSI_CMD_PREVENT_REMOVAL, lun, (uint8_t)0, lock);
+        return SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_IN);
+}
+
+/**
+ * Media control, for spindle motor and media tray or door.
+ * This includes CDROM, TAPE and anything with a media loader.
+ *
+ * @param lun Logical Unit Number
+ * @param ctl 0x00 Stop Motor, 0x01 Start Motor, 0x02 Eject Media, 0x03 Load Media
+ * @return 0 on success
+ */
+uint8_t BulkOnly::MediaCTL(uint8_t lun, uint8_t ctl) {
+        Notify(PSTR("\r\nMediaCTL\r\n"), 0x80);
+        Notify(PSTR("-----------------\r\n"), 0x80);
+
+        uint8_t rcode = MASS_ERR_UNIT_NOT_READY;
+        if(bAddress) {
+                CDB6_t cdb = CDB6_t(SCSI_CMD_START_STOP_UNIT, lun, ctl & 0x03, 0);
+                rcode = SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_OUT);
+        } else {
+                SetCurLUN(lun);
+        }
+        return rcode;
+}
+
+/**
+ * Read data from media
+ *
+ * @param lun Logical Unit Number
+ * @param addr LBA address on media to read
+ * @param bsize size of a block (we should probably use the cached size)
+ * @param blocks how many blocks to read
+ * @param buf memory that is able to hold the requested data
+ * @return 0 on success
+ */
+uint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf) {
+        if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
+        Notify(PSTR("\r\nRead LUN:\t"), 0x80);
+        D_PrintHex<uint8_t > (lun, 0x90);
+        Notify(PSTR("\r\nLBA:\t\t"), 0x90);
+        D_PrintHex<uint32_t > (addr, 0x90);
+        Notify(PSTR("\r\nblocks:\t\t"), 0x90);
+        D_PrintHex<uint8_t > (blocks, 0x90);
+        Notify(PSTR("\r\nblock size:\t"), 0x90);
+        D_PrintHex<uint16_t > (bsize, 0x90);
+        Notify(PSTR("\r\n---------\r\n"), 0x80);
+        CDB10_t cdb = CDB10_t(SCSI_CMD_READ_10, lun, blocks, addr);
+
+again:
+        uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), buf, (uint8_t)MASS_CMD_DIR_IN);
+
+        if(er == MASS_ERR_STALL) {
+                MediaCTL(lun, 1);
+                delay(150);
+                if(!TestUnitReady(lun)) goto again;
+        }
+        return er;
+}
+
+/**
+ * Write data to media
+ *
+ * @param lun Logical Unit Number
+ * @param addr LBA address on media to write
+ * @param bsize size of a block (we should probably use the cached size)
+ * @param blocks how many blocks to write
+ * @param buf memory that contains the data to write
+ * @return 0 on success
+ */
+uint8_t BulkOnly::Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf) {
+        if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
+        if(!WriteOk[lun]) return MASS_ERR_WRITE_PROTECTED;
+        Notify(PSTR("\r\nWrite LUN:\t"), 0x80);
+        D_PrintHex<uint8_t > (lun, 0x90);
+        Notify(PSTR("\r\nLBA:\t\t"), 0x90);
+        D_PrintHex<uint32_t > (addr, 0x90);
+        Notify(PSTR("\r\nblocks:\t\t"), 0x90);
+        D_PrintHex<uint8_t > (blocks, 0x90);
+        Notify(PSTR("\r\nblock size:\t"), 0x90);
+        D_PrintHex<uint16_t > (bsize, 0x90);
+        Notify(PSTR("\r\n---------\r\n"), 0x80);
+        CDB10_t cdb = CDB10_t(SCSI_CMD_WRITE_10, lun, blocks, addr);
+
+again:
+        uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), (void*)buf, (uint8_t)MASS_CMD_DIR_OUT);
+
+        if(er == MASS_ERR_WRITE_STALL) {
+                MediaCTL(lun, 1);
+                delay(150);
+                if(!TestUnitReady(lun)) goto again;
+        }
+        return er;
+}
+
+// End of user functions, the remaining code below is driver internals.
+// Only developer serviceable parts below!
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Main driver code
+
+////////////////////////////////////////////////////////////////////////////////
+
+BulkOnly::BulkOnly(USB *p) :
+pUsb(p),
+bAddress(0),
+bIface(0),
+bNumEP(1),
+qNextPollTime(0),
+bPollEnable(false),
+//dCBWTag(0),
+bLastUsbError(0) {
+        ClearAllEP();
+        dCBWTag = 0;
+        if(pUsb)
+                pUsb->RegisterDeviceClass(this);
+}
+
+/**
+ * USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET == success
+ * We need to standardize either the rcode, or change the API to return values
+ * so a signal that additional actions are required can be produced.
+ * Some of these codes do exist already.
+ *
+ * TECHNICAL: We could do most of this code elsewhere, with the exception of checking the class instance.
+ * Doing so would save some program memory when using multiple drivers.
+ *
+ * @param parent USB address of parent
+ * @param port address of port on parent
+ * @param lowspeed true if device is low speed
+ * @return
+ */
+uint8_t BulkOnly::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {
+
+        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);
+
+        uint8_t buf[constBufSize];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        USBTRACE("MS ConfigureDevice\r\n");
+        ClearAllEP();
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+
+        if(bAddress)
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+        // <TECHNICAL>
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+        if(!p) {
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+
+        if(!p->epinfo) {
+                USBTRACE("epinfo\r\n");
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(rcode) {
+                goto FailGetDevDescr;
+        }
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+        // Steal and abuse from epInfo structure to save on memory.
+        epInfo[1].epAddr = udd->bNumConfigurations;
+        // </TECHNICAL>
+        return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;
+
+FailGetDevDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetDevDescr(rcode);
+#endif
+        rcode = USB_ERROR_FailGetDevDescr;
+
+        Release();
+        return rcode;
+};
+
+/**
+ *
+ * @param parent (not used)
+ * @param port (not used)
+ * @param lowspeed true if device is low speed
+ * @return 0 for success
+ */
+uint8_t BulkOnly::Init(uint8_t parent __attribute__((unused)), uint8_t port __attribute__((unused)), bool lowspeed) {
+        uint8_t rcode;
+        uint8_t num_of_conf = epInfo[1].epAddr; // number of configurations
+        epInfo[1].epAddr = 0;
+        USBTRACE("MS Init\r\n");
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+        UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        // Assign new address to the device
+        delay(2000);
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                USBTRACE2("setAddr:", rcode);
+                return rcode;
+        }
+
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        p = addrPool.GetUsbDevicePtr(bAddress);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        p->lowspeed = lowspeed;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        USBTRACE2("NC:", num_of_conf);
+
+        for(uint8_t i = 0; i < num_of_conf; i++) {
+                ConfigDescParser< USB_CLASS_MASS_STORAGE,
+                        MASS_SUBCLASS_SCSI,
+                        MASS_PROTO_BBB,
+                        CP_MASK_COMPARE_CLASS |
+                        CP_MASK_COMPARE_SUBCLASS |
+                        CP_MASK_COMPARE_PROTOCOL > BulkOnlyParser(this);
+
+                rcode = pUsb->getConfDescr(bAddress, 0, i, &BulkOnlyParser);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+
+                if(bNumEP > 1)
+                        break;
+        }
+
+        if(bNumEP < 3)
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        // Assign epInfo to epinfo pointer
+        pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);
+
+        USBTRACE2("Conf:", bConfNum);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+        //Linux does a 1sec delay after this.
+        delay(1000);
+
+        rcode = GetMaxLUN(&bMaxLUN);
+        if(rcode)
+                goto FailGetMaxLUN;
+
+        if(bMaxLUN >= MASS_MAX_SUPPORTED_LUN) bMaxLUN = MASS_MAX_SUPPORTED_LUN - 1;
+        ErrorMessage<uint8_t > (PSTR("MaxLUN"), bMaxLUN);
+
+        delay(1000); // Delay a bit for slow firmware.
+
+        for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
+                InquiryResponse response;
+                rcode = Inquiry(lun, sizeof (InquiryResponse), (uint8_t*) & response);
+                if(rcode) {
+                        ErrorMessage<uint8_t > (PSTR("Inquiry"), rcode);
+                } else {
+#if 0
+                        printf("LUN %i `", lun);
+                        uint8_t *buf = response.VendorID;
+                        for(int i = 0; i < 28; i++) printf("%c", buf[i]);
+                        printf("'\r\nQualifier %1.1X ", response.PeripheralQualifier);
+                        printf("Device type %2.2X ", response.DeviceType);
+                        printf("RMB %1.1X ", response.Removable);
+                        printf("SSCS %1.1X ", response.SCCS);
+                        uint8_t sv = response.Version;
+                        printf("SCSI version %2.2X\r\nDevice conforms to ", sv);
+                        switch(sv) {
+                                case 0:
+                                        printf("No specific");
+                                        break;
+                                case 1:
+                                        printf("ANSI X3.131-1986 (ANSI 1)");
+                                        break;
+                                case 2:
+                                        printf("ANSI X3.131-1994 (ANSI 2)");
+                                        break;
+                                case 3:
+                                        printf("ANSI INCITS 301-1997 (SPC)");
+                                        break;
+                                case 4:
+                                        printf("ANSI INCITS 351-2001 (SPC-2)");
+                                        break;
+                                case 5:
+                                        printf("ANSI INCITS 408-2005 (SPC-4)");
+                                        break;
+                                case 6:
+                                        printf("T10/1731-D (SPC-4)");
+                                        break;
+                                default:
+                                        printf("unknown");
+                        }
+                        printf(" standards.\r\n");
+#endif
+                        uint8_t tries = 0xf0;
+                        while((rcode = TestUnitReady(lun))) {
+                                if(rcode == 0x08) break; // break on no media, this is OK to do.
+                                // try to lock media and spin up
+                                if(tries < 14) {
+                                        LockMedia(lun, 1);
+                                        MediaCTL(lun, 1); // I actually have a USB stick that needs this!
+                                } else delay(2 * (tries + 1));
+                                tries++;
+                                if(!tries) break;
+                        }
+                        if(!rcode) {
+                                delay(1000);
+                                LUNOk[lun] = CheckLUN(lun);
+                                if(!LUNOk[lun]) LUNOk[lun] = CheckLUN(lun);
+                        }
+                }
+        }
+
+
+        CheckMedia();
+
+        rcode = OnInit();
+
+        if(rcode)
+                goto FailOnInit;
+
+#ifdef DEBUG_USB_HOST
+        USBTRACE("MS configured\r\n\r\n");
+#endif
+
+        bPollEnable = true;
+
+        //USBTRACE("Poll enabled\r\n");
+        return 0;
+
+FailSetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetConfDescr();
+        goto Fail;
+#endif
+
+FailOnInit:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("OnInit:");
+        goto Fail;
+#endif
+
+FailGetMaxLUN:
+#ifdef DEBUG_USB_HOST
+        USBTRACE("GetMaxLUN:");
+        goto Fail;
+#endif
+
+        //#ifdef DEBUG_USB_HOST
+        //FailInvalidSectorSize:
+        //        USBTRACE("Sector Size is NOT VALID: ");
+        //        goto Fail;
+        //#endif
+
+FailSetDevTblEntry:
+#ifdef DEBUG_USB_HOST
+        NotifyFailSetDevTblEntry();
+        goto Fail;
+#endif
+
+FailGetConfDescr:
+#ifdef DEBUG_USB_HOST
+        NotifyFailGetConfDescr();
+#endif
+
+#ifdef DEBUG_USB_HOST
+Fail:
+        NotifyFail(rcode);
+#endif
+        Release();
+        return rcode;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param conf
+ * @param iface
+ * @param alt
+ * @param proto
+ * @param pep
+ */
+void BulkOnly::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto __attribute__((unused)), const USB_ENDPOINT_DESCRIPTOR * pep) {
+        ErrorMessage<uint8_t > (PSTR("Conf.Val"), conf);
+        ErrorMessage<uint8_t > (PSTR("Iface Num"), iface);
+        ErrorMessage<uint8_t > (PSTR("Alt.Set"), alt);
+
+        bConfNum = conf;
+
+        uint8_t index;
+
+#if 1
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_BULK) {
+                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
+                // Fill in the endpoint info structure
+                epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+                epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+                epInfo[index].bmSndToggle = 0;
+                epInfo[index].bmRcvToggle = 0;
+
+                bNumEP++;
+
+                PrintEndpointDescriptor(pep);
+
+        }
+#else
+        if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_INTERRUPT && (pep->bEndpointAddress & 0x80) == 0x80)
+                index = epInterruptInIndex;
+        else if((pep->bmAttributes & bmUSB_TRANSFER_TYPE) == USB_TRANSFER_TYPE_BULK)
+                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
+        else
+                return;
+
+        // Fill in the endpoint info structure
+        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);
+        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;
+        epInfo[index].bmSndToggle = 0;
+        epInfo[index].bmRcvToggle = 0;
+
+        bNumEP++;
+
+        PrintEndpointDescriptor(pep);
+#endif
+}
+
+/**
+ * For driver use only.
+ *
+ * @return
+ */
+uint8_t BulkOnly::Release() {
+        ClearAllEP();
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+        return 0;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @return true if LUN is ready for use.
+ */
+bool BulkOnly::CheckLUN(uint8_t lun) {
+        uint8_t rcode;
+        Capacity capacity;
+        for(uint8_t i = 0; i < 8; i++) capacity.data[i] = 0;
+
+        rcode = ReadCapacity10(lun, (uint8_t*)capacity.data);
+        if(rcode) {
+                //printf(">>>>>>>>>>>>>>>>ReadCapacity returned %i\r\n", rcode);
+                return false;
+        }
+        ErrorMessage<uint8_t > (PSTR(">>>>>>>>>>>>>>>>CAPACITY OK ON LUN"), lun);
+        for(uint8_t i = 0; i < 8 /*sizeof (Capacity)*/; i++)
+                D_PrintHex<uint8_t > (capacity.data[i], 0x80);
+        Notify(PSTR("\r\n\r\n"), 0x80);
+        // Only 512/1024/2048/4096 are valid values!
+        uint32_t c = BMAKE32(capacity.data[4], capacity.data[5], capacity.data[6], capacity.data[7]);
+        if(c != 0x0200LU && c != 0x0400LU && c != 0x0800LU && c != 0x1000LU) {
+                return false;
+        }
+        // Store capacity information.
+        CurrentSectorSize[lun] = (uint16_t)(c); // & 0xFFFF);
+
+        CurrentCapacity[lun] = BMAKE32(capacity.data[0], capacity.data[1], capacity.data[2], capacity.data[3]) + 1;
+        if(CurrentCapacity[lun] == /*0xffffffffLU */ 0x01LU || CurrentCapacity[lun] == 0x00LU) {
+                // Buggy firmware will report 0xffffffff or 0 for no media
+                if(CurrentCapacity[lun])
+                        ErrorMessage<uint8_t > (PSTR(">>>>>>>>>>>>>>>>BUGGY FIRMWARE. CAPACITY FAIL ON LUN"), lun);
+                return false;
+        }
+        delay(20);
+        Page3F(lun);
+        if(!TestUnitReady(lun)) return true;
+        return false;
+}
+
+/**
+ * For driver use only.
+ *
+ * Scan for media change on all LUNs
+ */
+void BulkOnly::CheckMedia() {
+        for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
+                if(TestUnitReady(lun)) {
+                        LUNOk[lun] = false;
+                        continue;
+                }
+                if(!LUNOk[lun])
+                        LUNOk[lun] = CheckLUN(lun);
+        }
+#if 0
+        printf("}}}}}}}}}}}}}}}}STATUS ");
+        for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {
+                if(LUNOk[lun])
+                        printf("#");
+                else printf(".");
+        }
+        printf("\r\n");
+#endif
+        qNextPollTime = (uint32_t)millis() + 2000;
+}
+
+/**
+ * For driver use only.
+ *
+ * @return
+ */
+uint8_t BulkOnly::Poll() {
+        //uint8_t rcode = 0;
+
+        if(!bPollEnable)
+                return 0;
+
+        if((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L) {
+                CheckMedia();
+        }
+        //rcode = 0;
+
+        return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+// SCSI code
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * For driver use only.
+ *
+ * @param plun
+ * @return
+ */
+uint8_t BulkOnly::GetMaxLUN(uint8_t *plun) {
+        uint8_t ret = pUsb->ctrlReq(bAddress, 0, bmREQ_MASSIN, MASS_REQ_GET_MAX_LUN, 0, 0, bIface, 1, 1, plun, NULL);
+
+        if(ret == hrSTALL)
+                *plun = 0;
+
+        return 0;
+}
+
+/**
+ * For driver use only. Used during Driver Init
+ *
+ * @param lun Logical Unit Number
+ * @param bsize
+ * @param buf
+ * @return
+ */
+uint8_t BulkOnly::Inquiry(uint8_t lun, uint16_t bsize, uint8_t *buf) {
+        Notify(PSTR("\r\nInquiry\r\n"), 0x80);
+        Notify(PSTR("---------\r\n"), 0x80);
+
+        CDB6_t cdb = CDB6_t(SCSI_CMD_INQUIRY, lun, 0LU, (uint8_t)bsize, 0);
+        uint8_t rc = SCSITransaction6(&cdb, bsize, buf, (uint8_t)MASS_CMD_DIR_IN);
+
+        return rc;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @return
+ */
+uint8_t BulkOnly::TestUnitReady(uint8_t lun) {
+        //SetCurLUN(lun);
+        if(!bAddress)
+                return MASS_ERR_UNIT_NOT_READY;
+
+        Notify(PSTR("\r\nTestUnitReady\r\n"), 0x80);
+        Notify(PSTR("-----------------\r\n"), 0x80);
+
+        CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint8_t)0, 0);
+        return SCSITransaction6(&cdb, 0, NULL, (uint8_t)MASS_CMD_DIR_IN);
+
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @param pc
+ * @param page
+ * @param subpage
+ * @param len
+ * @param pbuf
+ * @return
+ */
+uint8_t BulkOnly::ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t * pbuf) {
+        Notify(PSTR("\r\rModeSense\r\n"), 0x80);
+        Notify(PSTR("------------\r\n"), 0x80);
+
+        CDB6_t cdb = CDB6_t(SCSI_CMD_MODE_SENSE_6, lun, (uint32_t)((((pc << 6) | page) << 8) | subpage), len, 0);
+        return SCSITransaction6(&cdb, len, pbuf, (uint8_t)MASS_CMD_DIR_IN);
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @param bsize
+ * @param buf
+ * @return
+ */
+uint8_t BulkOnly::ReadCapacity10(uint8_t lun, uint8_t *buf) {
+        Notify(PSTR("\r\nReadCapacity\r\n"), 0x80);
+        Notify(PSTR("---------------\r\n"), 0x80);
+
+        CDB10_t cdb = CDB10_t(SCSI_CMD_READ_CAPACITY_10, lun);
+        return SCSITransaction10(&cdb, 8, buf, (uint8_t)MASS_CMD_DIR_IN);
+}
+
+/**
+ * For driver use only.
+ *
+ * Page 3F contains write protect status.
+ *
+ * @param lun Logical Unit Number to test.
+ * @return Write protect switch status.
+ */
+uint8_t BulkOnly::Page3F(uint8_t lun) {
+        uint8_t buf[192];
+        for(int i = 0; i < 192; i++) {
+                buf[i] = 0x00;
+        }
+        WriteOk[lun] = true;
+        uint8_t rc = ModeSense6(lun, 0, 0x3f, 0, 192, buf);
+        if(!rc) {
+                WriteOk[lun] = ((buf[2] & 0x80) == 0);
+                Notify(PSTR("Mode Sense: "), 0x80);
+                for(int i = 0; i < 4; i++) {
+                        D_PrintHex<uint8_t > (buf[i], 0x80);
+                        Notify(PSTR(" "), 0x80);
+                }
+                Notify(PSTR("\r\n"), 0x80);
+        }
+        return rc;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @param size
+ * @param buf
+ * @return
+ */
+uint8_t BulkOnly::RequestSense(uint8_t lun, uint16_t size, uint8_t *buf) {
+        Notify(PSTR("\r\nRequestSense\r\n"), 0x80);
+        Notify(PSTR("----------------\r\n"), 0x80);
+
+        CDB6_t cdb = CDB6_t(SCSI_CMD_REQUEST_SENSE, lun, 0LU, (uint8_t)size, 0);
+        CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)size, &cdb, (uint8_t)MASS_CMD_DIR_IN);
+        //SetCurLUN(lun);
+        return Transaction(&cbw, size, buf);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+// USB code
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * For driver use only.
+ *
+ * @param index
+ * @return
+ */
+uint8_t BulkOnly::ClearEpHalt(uint8_t index) {
+        if(index == 0)
+                return 0;
+
+        uint8_t ret = 0;
+
+        while((ret = (pUsb->ctrlReq(bAddress, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT, USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT, 0, ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr), 0, 0, NULL, NULL)) == 0x01))
+                delay(6);
+
+        if(ret) {
+                ErrorMessage<uint8_t > (PSTR("ClearEpHalt"), ret);
+                ErrorMessage<uint8_t > (PSTR("EP"), ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr));
+                return ret;
+        }
+        epInfo[index].bmSndToggle = 0;
+        epInfo[index].bmRcvToggle = 0;
+        return 0;
+}
+
+/**
+ * For driver use only.
+ *
+ */
+void BulkOnly::Reset() {
+        while(pUsb->ctrlReq(bAddress, 0, bmREQ_MASSOUT, MASS_REQ_BOMSR, 0, 0, bIface, 0, 0, NULL, NULL) == 0x01) delay(6);
+}
+
+/**
+ * For driver use only.
+ *
+ * @return 0 if successful
+ */
+uint8_t BulkOnly::ResetRecovery() {
+        Notify(PSTR("\r\nResetRecovery\r\n"), 0x80);
+        Notify(PSTR("-----------------\r\n"), 0x80);
+
+        delay(6);
+        Reset();
+        delay(6);
+        ClearEpHalt(epDataInIndex);
+        delay(6);
+        bLastUsbError = ClearEpHalt(epDataOutIndex);
+        delay(6);
+        return bLastUsbError;
+}
+
+/**
+ * For driver use only.
+ *
+ * Clear all EP data and clear all LUN status
+ */
+void BulkOnly::ClearAllEP() {
+        for(uint8_t i = 0; i < MASS_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr = 0;
+                epInfo[i].maxPktSize = (i) ? 0 : 8;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+                epInfo[i].bmNakPower = USB_NAK_DEFAULT;
+        }
+
+        for(uint8_t i = 0; i < MASS_MAX_SUPPORTED_LUN; i++) {
+                LUNOk[i] = false;
+                WriteOk[i] = false;
+                CurrentCapacity[i] = 0lu;
+                CurrentSectorSize[i] = 0;
+        }
+
+        bIface = 0;
+        bNumEP = 1;
+        bAddress = 0;
+        qNextPollTime = 0;
+        bPollEnable = false;
+        bLastUsbError = 0;
+        bMaxLUN = 0;
+        bTheLUN = 0;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param pcsw
+ * @param pcbw
+ * @return
+ */
+bool BulkOnly::IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw) {
+        if(pcsw->dCSWSignature != MASS_CSW_SIGNATURE) {
+                Notify(PSTR("CSW:Sig error\r\n"), 0x80);
+                return false;
+        }
+        if(pcsw->dCSWTag != pcbw->dCBWTag) {
+                Notify(PSTR("CSW:Wrong tag\r\n"), 0x80);
+                return false;
+        }
+        return true;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param error
+ * @param index
+ * @return
+ */
+uint8_t BulkOnly::HandleUsbError(uint8_t error, uint8_t index) {
+        uint8_t count = 3;
+
+        bLastUsbError = error;
+        //if (error)
+        //ClearEpHalt(index);
+        while(error && count) {
+                if(error != hrSUCCESS) {
+                        ErrorMessage<uint8_t > (PSTR("USB Error"), error);
+                        ErrorMessage<uint8_t > (PSTR("Index"), index);
+                }
+                switch(error) {
+                                // case hrWRONGPID:
+                        case hrSUCCESS:
+                                return MASS_ERR_SUCCESS;
+                        case hrBUSY:
+                                // SIE is busy, just hang out and try again.
+                                return MASS_ERR_UNIT_BUSY;
+                        case hrTIMEOUT:
+                        case hrJERR: return MASS_ERR_DEVICE_DISCONNECTED;
+                        case hrSTALL:
+                                if(index == 0)
+                                        return MASS_ERR_STALL;
+                                ClearEpHalt(index);
+                                if(index != epDataInIndex)
+                                        return MASS_ERR_WRITE_STALL;
+                                return MASS_ERR_STALL;
+
+                        case hrNAK:
+                                if(index == 0)
+                                        return MASS_ERR_UNIT_BUSY;
+                                return MASS_ERR_UNIT_BUSY;
+
+                        case hrTOGERR:
+                                // Handle a very super rare corner case, where toggles become de-synched.
+                                // I have only ran into one device that has this firmware bug, and this is
+                                // the only clean way to get back into sync with the buggy device firmware.
+                                //   --AJK
+                                if(bAddress && bConfNum) {
+                                        error = pUsb->setConf(bAddress, 0, bConfNum);
+
+                                        if(error)
+                                                break;
+                                }
+                                return MASS_ERR_SUCCESS;
+                        default:
+                                ErrorMessage<uint8_t > (PSTR("\r\nUSB"), error);
+                                return MASS_ERR_GENERAL_USB_ERROR;
+                }
+                count--;
+        } // while
+
+        return ((error && !count) ? MASS_ERR_GENERAL_USB_ERROR : MASS_ERR_SUCCESS);
+}
+
+#if MS_WANT_PARSER
+
+uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf) {
+        return Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf, 0);
+}
+#endif
+
+/**
+ * For driver use only.
+ *
+ * @param pcbw
+ * @param buf_size
+ * @param buf
+ * @param flags
+ * @return
+ */
+uint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf
+#if MS_WANT_PARSER
+        , uint8_t flags
+#endif
+        ) {
+
+#if MS_WANT_PARSER
+        uint16_t bytes = (pcbw->dCBWDataTransferLength > buf_size) ? buf_size : pcbw->dCBWDataTransferLength;
+        printf("Transfersize %i\r\n", bytes);
+        delay(1000);
+
+        bool callback = (flags & MASS_TRANS_FLG_CALLBACK) == MASS_TRANS_FLG_CALLBACK;
+#else
+        uint16_t bytes = buf_size;
+#endif
+        bool write = (pcbw->bmCBWFlags & MASS_CMD_DIR_IN) != MASS_CMD_DIR_IN;
+        uint8_t ret = 0;
+        uint8_t usberr;
+        CommandStatusWrapper csw; // up here, we allocate ahead to save cpu cycles.
+        SetCurLUN(pcbw->bmCBWLUN);
+        ErrorMessage<uint32_t > (PSTR("CBW.dCBWTag"), pcbw->dCBWTag);
+
+        while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw)) == hrBUSY) delay(1);
+
+        ret = HandleUsbError(usberr, epDataOutIndex);
+        //ret = HandleUsbError(pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw), epDataOutIndex);
+        if(ret) {
+                ErrorMessage<uint8_t > (PSTR("============================ CBW"), ret);
+        } else {
+                if(bytes) {
+                        if(!write) {
+#if MS_WANT_PARSER
+                                if(callback) {
+                                        uint8_t rbuf[bytes];
+                                        while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, rbuf)) == hrBUSY) delay(1);
+                                        if(usberr == hrSUCCESS) ((USBReadParser*)buf)->Parse(bytes, rbuf, 0);
+                                } else {
+#endif
+                                        while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*)buf)) == hrBUSY) delay(1);
+#if MS_WANT_PARSER
+
+                                }
+#endif
+                                ret = HandleUsbError(usberr, epDataInIndex);
+                        } else {
+                                while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes, (uint8_t*)buf)) == hrBUSY) delay(1);
+                                ret = HandleUsbError(usberr, epDataOutIndex);
+                        }
+                        if(ret) {
+                                ErrorMessage<uint8_t > (PSTR("============================ DAT"), ret);
+                        }
+                }
+        }
+
+        {
+                bytes = sizeof (CommandStatusWrapper);
+                int tries = 2;
+                while(tries--) {
+                        while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*) & csw)) == hrBUSY) delay(1);
+                        if(!usberr) break;
+                        ClearEpHalt(epDataInIndex);
+                        if(tries) ResetRecovery();
+                }
+                if(!ret) {
+                        Notify(PSTR("CBW:\t\tOK\r\n"), 0x80);
+                        Notify(PSTR("Data Stage:\tOK\r\n"), 0x80);
+                } else {
+                        // Throw away csw, IT IS NOT OF ANY USE.
+                        ResetRecovery();
+                        return ret;
+                }
+                ret = HandleUsbError(usberr, epDataInIndex);
+                if(ret) {
+                        ErrorMessage<uint8_t > (PSTR("============================ CSW"), ret);
+                }
+                if(usberr == hrSUCCESS) {
+                        if(IsValidCSW(&csw, pcbw)) {
+                                //ErrorMessage<uint32_t > (PSTR("CSW.dCBWTag"), csw.dCSWTag);
+                                //ErrorMessage<uint8_t > (PSTR("bCSWStatus"), csw.bCSWStatus);
+                                //ErrorMessage<uint32_t > (PSTR("dCSWDataResidue"), csw.dCSWDataResidue);
+                                Notify(PSTR("CSW:\t\tOK\r\n\r\n"), 0x80);
+                                return csw.bCSWStatus;
+                        } else {
+                                // NOTE! Sometimes this is caused by the reported residue being wrong.
+                                // Get a different device. It isn't compliant, and should have never passed Q&A.
+                                // I own one... 05e3:0701 Genesys Logic, Inc. USB 2.0 IDE Adapter.
+                                // Other devices that exhibit this behavior exist in the wild too.
+                                // Be sure to check quirks in the Linux source code before reporting a bug. --xxxajk
+                                Notify(PSTR("Invalid CSW\r\n"), 0x80);
+                                ResetRecovery();
+                                //return MASS_ERR_SUCCESS;
+                                return MASS_ERR_INVALID_CSW;
+                        }
+                }
+        }
+        return ret;
+}
+
+/**
+ * For driver use only.
+ *
+ * @param lun Logical Unit Number
+ * @return
+ */
+uint8_t BulkOnly::SetCurLUN(uint8_t lun) {
+        if(lun > bMaxLUN)
+                return MASS_ERR_INVALID_LUN;
+        bTheLUN = lun;
+        return MASS_ERR_SUCCESS;
+};
+
+/**
+ * For driver use only.
+ *
+ * @param status
+ * @return
+ */
+uint8_t BulkOnly::HandleSCSIError(uint8_t status) {
+        uint8_t ret = 0;
+
+        switch(status) {
+                case 0: return MASS_ERR_SUCCESS;
+
+                case 2:
+                        ErrorMessage<uint8_t > (PSTR("Phase Error"), status);
+                        ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
+                        ResetRecovery();
+                        return MASS_ERR_GENERAL_SCSI_ERROR;
+
+                case 1:
+                        ErrorMessage<uint8_t > (PSTR("SCSI Error"), status);
+                        ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
+                        RequestSenseResponce rsp;
+
+                        ret = RequestSense(bTheLUN, sizeof (RequestSenseResponce), (uint8_t*) & rsp);
+
+                        if(ret) {
+                                return MASS_ERR_GENERAL_SCSI_ERROR;
+                        }
+                        ErrorMessage<uint8_t > (PSTR("Response Code"), rsp.bResponseCode);
+                        if(rsp.bResponseCode & 0x80) {
+                                Notify(PSTR("Information field: "), 0x80);
+                                for(int i = 0; i < 4; i++) {
+                                        D_PrintHex<uint8_t > (rsp.CmdSpecificInformation[i], 0x80);
+                                        Notify(PSTR(" "), 0x80);
+                                }
+                                Notify(PSTR("\r\n"), 0x80);
+                        }
+                        ErrorMessage<uint8_t > (PSTR("Sense Key"), rsp.bmSenseKey);
+                        ErrorMessage<uint8_t > (PSTR("Add Sense Code"), rsp.bAdditionalSenseCode);
+                        ErrorMessage<uint8_t > (PSTR("Add Sense Qual"), rsp.bAdditionalSenseQualifier);
+                        // warning, this is not testing ASQ, only SK and ASC.
+                        switch(rsp.bmSenseKey) {
+                                case SCSI_S_UNIT_ATTENTION:
+                                        switch(rsp.bAdditionalSenseCode) {
+                                                case SCSI_ASC_MEDIA_CHANGED:
+                                                        return MASS_ERR_MEDIA_CHANGED;
+                                                default:
+                                                        return MASS_ERR_UNIT_NOT_READY;
+                                        }
+                                case SCSI_S_NOT_READY:
+                                        switch(rsp.bAdditionalSenseCode) {
+                                                case SCSI_ASC_MEDIUM_NOT_PRESENT:
+                                                        return MASS_ERR_NO_MEDIA;
+                                                default:
+                                                        return MASS_ERR_UNIT_NOT_READY;
+                                        }
+                                case SCSI_S_ILLEGAL_REQUEST:
+                                        switch(rsp.bAdditionalSenseCode) {
+                                                case SCSI_ASC_LBA_OUT_OF_RANGE:
+                                                        return MASS_ERR_BAD_LBA;
+                                                default:
+                                                        return MASS_ERR_CMD_NOT_SUPPORTED;
+                                        }
+                                default:
+                                        return MASS_ERR_GENERAL_SCSI_ERROR;
+                        }
+
+                        // case 4: return MASS_ERR_UNIT_BUSY; // Busy means retry later.
+                        //    case 0x05/0x14: we stalled out
+                        //    case 0x15/0x16: we naked out.
+                default:
+                        ErrorMessage<uint8_t > (PSTR("Gen SCSI Err"), status);
+                        ErrorMessage<uint8_t > (PSTR("LUN"), bTheLUN);
+                        return status;
+        } // switch
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+// Debugging code
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *
+ * @param ep_ptr
+ */
+void BulkOnly::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR * ep_ptr) {
+        Notify(PSTR("Endpoint descriptor:"), 0x80);
+        Notify(PSTR("\r\nLength:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
+        Notify(PSTR("\r\nType:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
+        Notify(PSTR("\r\nAddress:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
+        Notify(PSTR("\r\nAttributes:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
+        Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
+        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
+        Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
+        Notify(PSTR("\r\n"), 0x80);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+// misc/to kill/to-do
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+/* We won't be needing this... */
+uint8_t BulkOnly::Read(uint8_t lun __attribute__((unused)), uint32_t addr __attribute__((unused)), uint16_t bsize __attribute__((unused)), uint8_t blocks __attribute__((unused)), USBReadParser * prs __attribute__((unused))) {
+#if MS_WANT_PARSER
+        if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;
+        Notify(PSTR("\r\nRead (With parser)\r\n"), 0x80);
+        Notify(PSTR("---------\r\n"), 0x80);
+
+        CommandBlockWrapper cbw = CommandBlockWrapper();
+
+        cbw.dCBWSignature = MASS_CBW_SIGNATURE;
+        cbw.dCBWTag = ++dCBWTag;
+        cbw.dCBWDataTransferLength = ((uint32_t)bsize * blocks);
+        cbw.bmCBWFlags = MASS_CMD_DIR_IN,
+                cbw.bmCBWLUN = lun;
+        cbw.bmCBWCBLength = 10;
+
+        cbw.CBWCB[0] = SCSI_CMD_READ_10;
+        cbw.CBWCB[8] = blocks;
+        cbw.CBWCB[2] = ((addr >> 24) & 0xff);
+        cbw.CBWCB[3] = ((addr >> 16) & 0xff);
+        cbw.CBWCB[4] = ((addr >> 8) & 0xff);
+        cbw.CBWCB[5] = (addr & 0xff);
+
+        return HandleSCSIError(Transaction(&cbw, bsize, prs, 1));
+#else
+        return MASS_ERR_NOT_IMPLEMENTED;
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/masstorage.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,579 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(__MASSTORAGE_H__)
+#define __MASSTORAGE_H__
+
+// Cruft removal, makes driver smaller, faster.
+#ifndef MS_WANT_PARSER
+#define MS_WANT_PARSER 0
+#endif
+
+#include "Usb.h"
+
+#define bmREQ_MASSOUT       USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+#define bmREQ_MASSIN        USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+
+// Mass Storage Subclass Constants
+#define MASS_SUBCLASS_SCSI_NOT_REPORTED 0x00    // De facto use
+#define MASS_SUBCLASS_RBC               0x01
+#define MASS_SUBCLASS_ATAPI             0x02    // MMC-5 (ATAPI)
+#define MASS_SUBCLASS_OBSOLETE1         0x03    // Was QIC-157
+#define MASS_SUBCLASS_UFI               0x04    // Specifies how to interface Floppy Disk Drives to USB
+#define MASS_SUBCLASS_OBSOLETE2         0x05    // Was SFF-8070i
+#define MASS_SUBCLASS_SCSI              0x06    // SCSI Transparent Command Set
+#define MASS_SUBCLASS_LSDFS             0x07    // Specifies how host has to negotiate access before trying SCSI
+#define MASS_SUBCLASS_IEEE1667          0x08
+
+// Mass Storage Class Protocols
+#define MASS_PROTO_CBI                  0x00    // CBI (with command completion interrupt)
+#define MASS_PROTO_CBI_NO_INT           0x01    // CBI (without command completion interrupt)
+#define MASS_PROTO_OBSOLETE             0x02
+#define MASS_PROTO_BBB                  0x50    // Bulk Only Transport
+#define MASS_PROTO_UAS                  0x62
+
+// Request Codes
+#define MASS_REQ_ADSC                   0x00
+#define MASS_REQ_GET                    0xFC
+#define MASS_REQ_PUT                    0xFD
+#define MASS_REQ_GET_MAX_LUN            0xFE
+#define MASS_REQ_BOMSR                  0xFF    // Bulk-Only Mass Storage Reset
+
+#define MASS_CBW_SIGNATURE              0x43425355
+#define MASS_CSW_SIGNATURE              0x53425355
+
+#define MASS_CMD_DIR_OUT                0 // (0 << 7)
+#define MASS_CMD_DIR_IN                 0x80 //(1 << 7)
+
+/*
+ * Reference documents from T10 (http://www.t10.org)
+ * SCSI Primary Commands - 3 (SPC-3)
+ * SCSI Block Commands - 2 (SBC-2)
+ * Multi-Media Commands - 5 (MMC-5)
+ */
+
+/* Group 1 commands (CDB's here are should all be 6-bytes) */
+#define SCSI_CMD_TEST_UNIT_READY        0x00
+#define SCSI_CMD_REQUEST_SENSE          0x03
+#define SCSI_CMD_FORMAT_UNIT            0x04
+#define SCSI_CMD_READ_6                 0x08
+#define SCSI_CMD_WRITE_6                0x0A
+#define SCSI_CMD_INQUIRY                0x12
+#define SCSI_CMD_MODE_SELECT_6          0x15
+#define SCSI_CMD_MODE_SENSE_6           0x1A
+#define SCSI_CMD_START_STOP_UNIT        0x1B
+#define SCSI_CMD_PREVENT_REMOVAL        0x1E
+/* Group 2 Commands (CDB's here are 10-bytes) */
+#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23
+#define SCSI_CMD_READ_CAPACITY_10       0x25
+#define SCSI_CMD_READ_10                0x28
+#define SCSI_CMD_WRITE_10               0x2A
+#define SCSI_CMD_SEEK_10                0x2B
+#define SCSI_CMD_ERASE_10               0x2C
+#define SCSI_CMD_WRITE_AND_VERIFY_10    0x2E
+#define SCSI_CMD_VERIFY_10              0x2F
+#define SCSI_CMD_SYNCHRONIZE_CACHE      0x35
+#define SCSI_CMD_WRITE_BUFFER           0x3B
+#define SCSI_CMD_READ_BUFFER            0x3C
+#define SCSI_CMD_READ_SUBCHANNEL        0x42
+#define SCSI_CMD_READ_TOC               0x43
+#define SCSI_CMD_READ_HEADER            0x44
+#define SCSI_CMD_PLAY_AUDIO_10          0x45
+#define SCSI_CMD_GET_CONFIGURATION      0x46
+#define SCSI_CMD_PLAY_AUDIO_MSF         0x47
+#define SCSI_CMD_PLAY_AUDIO_TI          0x48
+#define SCSI_CMD_PLAY_TRACK_REL_10      0x49
+#define SCSI_CMD_GET_EVENT_STATUS       0x4A
+#define SCSI_CMD_PAUSE_RESUME           0x4B
+#define SCSI_CMD_READ_DISC_INFORMATION  0x51
+#define SCSI_CMD_READ_TRACK_INFORMATION 0x52
+#define SCSI_CMD_RESERVE_TRACK          0x53
+#define SCSI_CMD_SEND_OPC_INFORMATION   0x54
+#define SCSI_CMD_MODE_SELECT_10         0x55
+#define SCSI_CMD_REPAIR_TRACK           0x58
+#define SCSI_CMD_MODE_SENSE_10          0x5A
+#define SCSI_CMD_CLOSE_TRACK_SESSION    0x5B
+#define SCSI_CMD_READ_BUFFER_CAPACITY   0x5C
+#define SCSI_CMD_SEND_CUE_SHEET         0x5D
+/* Group 5 Commands (CDB's here are 12-bytes) */
+#define SCSI_CMD_REPORT_LUNS            0xA0
+#define SCSI_CMD_BLANK                  0xA1
+#define SCSI_CMD_SECURITY_PROTOCOL_IN   0xA2
+#define SCSI_CMD_SEND_KEY               0xA3
+#define SCSI_CMD_REPORT_KEY             0xA4
+#define SCSI_CMD_PLAY_AUDIO_12          0xA5
+#define SCSI_CMD_LOAD_UNLOAD            0xA6
+#define SCSI_CMD_SET_READ_AHEAD         0xA7
+#define SCSI_CMD_READ_12                0xA8
+#define SCSI_CMD_PLAY_TRACK_REL_12      0xA9
+#define SCSI_CMD_WRITE_12               0xAA
+#define SCSI_CMD_READ_MEDIA_SERIAL_12   0xAB
+#define SCSI_CMD_GET_PERFORMANCE        0xAC
+#define SCSI_CMD_READ_DVD_STRUCTURE     0xAD
+#define SCSI_CMD_SECURITY_PROTOCOL_OUT  0xB5
+#define SCSI_CMD_SET_STREAMING          0xB6
+#define SCSI_CMD_READ_MSF               0xB9
+#define SCSI_CMD_SET_SPEED              0xBB
+#define SCSI_CMD_MECHANISM_STATUS       0xBD
+#define SCSI_CMD_READ_CD                0xBE
+#define SCSI_CMD_SEND_DISC_STRUCTURE    0xBF
+/* Vendor-unique Commands, included for completeness */
+#define SCSI_CMD_CD_PLAYBACK_STATUS     0xC4 /* SONY unique */
+#define SCSI_CMD_PLAYBACK_CONTROL       0xC9 /* SONY unique */
+#define SCSI_CMD_READ_CDDA              0xD8 /* Vendor unique */
+#define SCSI_CMD_READ_CDXA              0xDB /* Vendor unique */
+#define SCSI_CMD_READ_ALL_SUBCODES      0xDF /* Vendor unique */
+
+/* SCSI error codes */
+#define SCSI_S_NOT_READY                0x02
+#define SCSI_S_MEDIUM_ERROR             0x03
+#define SCSI_S_ILLEGAL_REQUEST          0x05
+#define SCSI_S_UNIT_ATTENTION           0x06
+#define SCSI_ASC_LBA_OUT_OF_RANGE       0x21
+#define SCSI_ASC_MEDIA_CHANGED          0x28
+#define SCSI_ASC_MEDIUM_NOT_PRESENT     0x3A
+
+/* USB error codes */
+#define MASS_ERR_SUCCESS                0x00
+#define MASS_ERR_PHASE_ERROR            0x02
+#define MASS_ERR_UNIT_NOT_READY         0x03
+#define MASS_ERR_UNIT_BUSY              0x04
+#define MASS_ERR_STALL                  0x05
+#define MASS_ERR_CMD_NOT_SUPPORTED      0x06
+#define MASS_ERR_INVALID_CSW            0x07
+#define MASS_ERR_NO_MEDIA               0x08
+#define MASS_ERR_BAD_LBA                0x09
+#define MASS_ERR_MEDIA_CHANGED          0x0A
+#define MASS_ERR_DEVICE_DISCONNECTED    0x11
+#define MASS_ERR_UNABLE_TO_RECOVER      0x12    // Reset recovery error
+#define MASS_ERR_INVALID_LUN            0x13
+#define MASS_ERR_WRITE_STALL            0x14
+#define MASS_ERR_READ_NAKS              0x15
+#define MASS_ERR_WRITE_NAKS             0x16
+#define MASS_ERR_WRITE_PROTECTED        0x17
+#define MASS_ERR_NOT_IMPLEMENTED        0xFD
+#define MASS_ERR_GENERAL_SCSI_ERROR     0xFE
+#define MASS_ERR_GENERAL_USB_ERROR      0xFF
+#define MASS_ERR_USER                   0xA0    // For subclasses to define their own error codes
+
+#define MASS_TRANS_FLG_CALLBACK         0x01    // Callback is involved
+#define MASS_TRANS_FLG_NO_STALL_CHECK   0x02    // STALL condition is not checked
+#define MASS_TRANS_FLG_NO_PHASE_CHECK   0x04    // PHASE_ERROR is not checked
+
+#define MASS_MAX_ENDPOINTS              3
+
+struct Capacity {
+        uint8_t data[8];
+        //uint32_t dwBlockAddress;
+        //uint32_t dwBlockLength;
+} __attribute__((packed));
+
+struct BASICCDB {
+        uint8_t Opcode;
+
+        unsigned unused : 5;
+        unsigned LUN : 3;
+
+        uint8_t info[12];
+} __attribute__((packed));
+
+typedef BASICCDB BASICCDB_t;
+
+struct CDB6 {
+        uint8_t Opcode;
+
+        unsigned LBAMSB : 5;
+        unsigned LUN : 3;
+
+        uint8_t LBAHB;
+        uint8_t LBALB;
+        uint8_t AllocationLength;
+        uint8_t Control;
+
+public:
+
+        CDB6(uint8_t _Opcode, uint8_t _LUN, uint32_t LBA, uint8_t _AllocationLength, uint8_t _Control) :
+        Opcode(_Opcode), LBAMSB(BGRAB2(LBA) & 0x1f), LUN(_LUN), LBAHB(BGRAB1(LBA)), LBALB(BGRAB0(LBA)),
+        AllocationLength(_AllocationLength), Control(_Control) {
+        }
+
+        CDB6(uint8_t _Opcode, uint8_t _LUN, uint8_t _AllocationLength, uint8_t _Control) :
+        Opcode(_Opcode), LBAMSB(0), LUN(_LUN), LBAHB(0), LBALB(0),
+        AllocationLength(_AllocationLength), Control(_Control) {
+        }
+} __attribute__((packed));
+
+typedef CDB6 CDB6_t;
+
+struct CDB10 {
+        uint8_t Opcode;
+
+        unsigned Service_Action : 5;
+        unsigned LUN : 3;
+
+        uint8_t LBA_L_M_MB;
+        uint8_t LBA_L_M_LB;
+        uint8_t LBA_L_L_MB;
+        uint8_t LBA_L_L_LB;
+
+        uint8_t Misc2;
+
+        uint8_t ALC_MB;
+        uint8_t ALC_LB;
+
+        uint8_t Control;
+public:
+
+        CDB10(uint8_t _Opcode, uint8_t _LUN) :
+        Opcode(_Opcode), Service_Action(0), LUN(_LUN),
+        LBA_L_M_MB(0), LBA_L_M_LB(0), LBA_L_L_MB(0), LBA_L_L_LB(0),
+        Misc2(0), ALC_MB(0), ALC_LB(0), Control(0) {
+        }
+
+        CDB10(uint8_t _Opcode, uint8_t _LUN, uint16_t xflen, uint32_t _LBA) :
+        Opcode(_Opcode), Service_Action(0), LUN(_LUN),
+        LBA_L_M_MB(BGRAB3(_LBA)), LBA_L_M_LB(BGRAB2(_LBA)), LBA_L_L_MB(BGRAB1(_LBA)), LBA_L_L_LB(BGRAB0(_LBA)),
+        Misc2(0), ALC_MB(BGRAB1(xflen)), ALC_LB(BGRAB0(xflen)), Control(0) {
+        }
+} __attribute__((packed));
+
+typedef CDB10 CDB10_t;
+
+struct CDB12 {
+        uint8_t Opcode;
+
+        unsigned Service_Action : 5;
+        unsigned Misc : 3;
+
+        uint8_t LBA_L_M_LB;
+        uint8_t LBA_L_L_MB;
+        uint8_t LBA_L_L_LB;
+
+        uint8_t ALC_M_LB;
+        uint8_t ALC_L_MB;
+        uint8_t ALC_L_LB;
+        uint8_t Control;
+} __attribute__((packed));
+
+typedef CDB12 CDB12_t;
+
+struct CDB_LBA32_16 {
+        uint8_t Opcode;
+
+        unsigned Service_Action : 5;
+        unsigned Misc : 3;
+
+        uint8_t LBA_L_M_MB;
+        uint8_t LBA_L_M_LB;
+        uint8_t LBA_L_L_MB;
+        uint8_t LBA_L_L_LB;
+
+        uint8_t A_M_M_MB;
+        uint8_t A_M_M_LB;
+        uint8_t A_M_L_MB;
+        uint8_t A_M_L_LB;
+
+        uint8_t ALC_M_MB;
+        uint8_t ALC_M_LB;
+        uint8_t ALC_L_MB;
+        uint8_t ALC_L_LB;
+
+        uint8_t Misc2;
+        uint8_t Control;
+} __attribute__((packed));
+
+struct CDB_LBA64_16 {
+        uint8_t Opcode;
+        uint8_t Misc;
+
+        uint8_t LBA_M_M_MB;
+        uint8_t LBA_M_M_LB;
+        uint8_t LBA_M_L_MB;
+        uint8_t LBA_M_L_LB;
+
+        uint8_t LBA_L_M_MB;
+        uint8_t LBA_L_M_LB;
+        uint8_t LBA_L_L_MB;
+        uint8_t LBA_L_L_LB;
+
+        uint8_t ALC_M_MB;
+        uint8_t ALC_M_LB;
+        uint8_t ALC_L_MB;
+        uint8_t ALC_L_LB;
+
+        uint8_t Misc2;
+        uint8_t Control;
+} __attribute__((packed));
+
+struct InquiryResponse {
+        uint8_t DeviceType : 5;
+        uint8_t PeripheralQualifier : 3;
+
+        unsigned Reserved : 7;
+        unsigned Removable : 1;
+
+        uint8_t Version;
+
+        unsigned ResponseDataFormat : 4;
+        unsigned HISUP : 1;
+        unsigned NormACA : 1;
+        unsigned TrmTsk : 1;
+        unsigned AERC : 1;
+
+        uint8_t AdditionalLength;
+        //uint8_t Reserved3[2];
+
+        unsigned PROTECT : 1;
+        unsigned Res : 2;
+        unsigned ThreePC : 1;
+        unsigned TPGS : 2;
+        unsigned ACC : 1;
+        unsigned SCCS : 1;
+
+        unsigned ADDR16 : 1;
+        unsigned R1 : 1;
+        unsigned R2 : 1;
+        unsigned MCHNGR : 1;
+        unsigned MULTIP : 1;
+        unsigned VS : 1;
+        unsigned ENCSERV : 1;
+        unsigned BQUE : 1;
+
+        unsigned SoftReset : 1;
+        unsigned CmdQue : 1;
+        unsigned Reserved4 : 1;
+        unsigned Linked : 1;
+        unsigned Sync : 1;
+        unsigned WideBus16Bit : 1;
+        unsigned WideBus32Bit : 1;
+        unsigned RelAddr : 1;
+
+        uint8_t VendorID[8];
+        uint8_t ProductID[16];
+        uint8_t RevisionID[4];
+} __attribute__((packed));
+
+struct CommandBlockWrapperBase {
+        uint32_t dCBWSignature;
+        uint32_t dCBWTag;
+        uint32_t dCBWDataTransferLength;
+        uint8_t bmCBWFlags;
+public:
+
+        CommandBlockWrapperBase() {
+        }
+
+        CommandBlockWrapperBase(uint32_t tag, uint32_t xflen, uint8_t flgs) :
+        dCBWSignature(MASS_CBW_SIGNATURE), dCBWTag(tag), dCBWDataTransferLength(xflen), bmCBWFlags(flgs) {
+        }
+} __attribute__((packed));
+
+struct CommandBlockWrapper : public CommandBlockWrapperBase {
+
+        struct {
+                uint8_t bmCBWLUN : 4;
+                uint8_t bmReserved1 : 4;
+        };
+
+        struct {
+                uint8_t bmCBWCBLength : 4;
+                uint8_t bmReserved2 : 4;
+        };
+
+        uint8_t CBWCB[16];
+
+public:
+        // All zeroed.
+
+        CommandBlockWrapper() :
+        CommandBlockWrapperBase(0, 0, 0), bmReserved1(0), bmReserved2(0) {
+                for(int i = 0; i < 16; i++) CBWCB[i] = 0;
+        }
+
+        // Generic Wrap, CDB zeroed.
+
+        CommandBlockWrapper(uint32_t tag, uint32_t xflen, uint8_t flgs, uint8_t lu, uint8_t cmdlen, uint8_t cmd) :
+        CommandBlockWrapperBase(tag, xflen, flgs),
+        bmCBWLUN(lu), bmReserved1(0), bmCBWCBLength(cmdlen), bmReserved2(0) {
+                for(int i = 0; i < 16; i++) CBWCB[i] = 0;
+                // Type punning can cause optimization problems and bugs.
+                // Using reinterpret_cast to a dreinterpretifferent object is the proper way to do this.
+                //(((BASICCDB_t *) CBWCB)->LUN) = cmd;
+                BASICCDB_t *x = reinterpret_cast<BASICCDB_t *>(CBWCB);
+                x->LUN = cmd;
+        }
+
+        // Wrap for CDB of 6
+
+        CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB6_t *cdb, uint8_t dir) :
+        CommandBlockWrapperBase(tag, xflen, dir),
+        bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(6), bmReserved2(0) {
+                memcpy(&CBWCB, cdb, 6);
+        }
+        // Wrap for CDB of 10
+
+        CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB10_t *cdb, uint8_t dir) :
+        CommandBlockWrapperBase(tag, xflen, dir),
+        bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(10), bmReserved2(0) {
+                memcpy(&CBWCB, cdb, 10);
+        }
+} __attribute__((packed));
+
+struct CommandStatusWrapper {
+        uint32_t dCSWSignature;
+        uint32_t dCSWTag;
+        uint32_t dCSWDataResidue;
+        uint8_t bCSWStatus;
+} __attribute__((packed));
+
+struct RequestSenseResponce {
+        uint8_t bResponseCode;
+        uint8_t bSegmentNumber;
+
+        uint8_t bmSenseKey : 4;
+        uint8_t bmReserved : 1;
+        uint8_t bmILI : 1;
+        uint8_t bmEOM : 1;
+        uint8_t bmFileMark : 1;
+
+        uint8_t Information[4];
+        uint8_t bAdditionalLength;
+        uint8_t CmdSpecificInformation[4];
+        uint8_t bAdditionalSenseCode;
+        uint8_t bAdditionalSenseQualifier;
+        uint8_t bFieldReplaceableUnitCode;
+        uint8_t SenseKeySpecific[3];
+} __attribute__((packed));
+
+class BulkOnly : public USBDeviceConfig, public UsbConfigXtracter {
+protected:
+        static const uint8_t epDataInIndex; // DataIn endpoint index
+        static const uint8_t epDataOutIndex; // DataOUT endpoint index
+        static const uint8_t epInterruptInIndex; // InterruptIN  endpoint index
+
+        USB *pUsb;
+        uint8_t bAddress;
+        uint8_t bConfNum; // configuration number
+        uint8_t bIface; // interface value
+        uint8_t bNumEP; // total number of EP in the configuration
+        uint32_t qNextPollTime; // next poll time
+        bool bPollEnable; // poll enable flag
+
+        EpInfo epInfo[MASS_MAX_ENDPOINTS];
+
+        uint32_t dCBWTag; // Tag
+        //uint32_t dCBWDataTransferLength; // Data Transfer Length
+        uint8_t bLastUsbError; // Last USB error
+        uint8_t bMaxLUN; // Max LUN
+        uint8_t bTheLUN; // Active LUN
+        uint32_t CurrentCapacity[MASS_MAX_SUPPORTED_LUN]; // Total sectors
+        uint16_t CurrentSectorSize[MASS_MAX_SUPPORTED_LUN]; // Sector size, clipped to 16 bits
+        bool LUNOk[MASS_MAX_SUPPORTED_LUN]; // use this to check for media changes.
+        bool WriteOk[MASS_MAX_SUPPORTED_LUN];
+        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
+
+
+        // Additional Initialization Method for Subclasses
+
+        virtual uint8_t OnInit() {
+                return 0;
+        };
+public:
+        BulkOnly(USB *p);
+
+        uint8_t GetLastUsbError() {
+                return bLastUsbError;
+        };
+
+        uint8_t GetbMaxLUN() {
+                return bMaxLUN; // Max LUN
+        }
+
+        uint8_t GetbTheLUN() {
+                return bTheLUN; // Active LUN
+        }
+
+        bool WriteProtected(uint8_t lun);
+        uint8_t MediaCTL(uint8_t lun, uint8_t ctl);
+        uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf);
+        uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser *prs);
+        uint8_t Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t *buf);
+        uint8_t LockMedia(uint8_t lun, uint8_t lock);
+
+        bool LUNIsGood(uint8_t lun);
+        uint32_t GetCapacity(uint8_t lun);
+        uint16_t GetSectorSize(uint8_t lun);
+
+        // USBDeviceConfig implementation
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);
+
+        uint8_t Release();
+        uint8_t Poll();
+
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        // UsbConfigXtracter implementation
+        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);
+
+        virtual bool DEVCLASSOK(uint8_t klass) {
+                return (klass == USB_CLASS_MASS_STORAGE);
+        }
+
+        uint8_t SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
+        uint8_t SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);
+
+private:
+        uint8_t Inquiry(uint8_t lun, uint16_t size, uint8_t *buf);
+        uint8_t TestUnitReady(uint8_t lun);
+        uint8_t RequestSense(uint8_t lun, uint16_t size, uint8_t *buf);
+        uint8_t ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t *buf);
+        uint8_t GetMaxLUN(uint8_t *max_lun);
+        uint8_t SetCurLUN(uint8_t lun);
+        void Reset();
+        uint8_t ResetRecovery();
+        uint8_t ReadCapacity10(uint8_t lun, uint8_t *buf);
+        void ClearAllEP();
+        void CheckMedia();
+        bool CheckLUN(uint8_t lun);
+        uint8_t Page3F(uint8_t lun);
+        bool IsValidCBW(uint8_t size, uint8_t *pcbw);
+        bool IsMeaningfulCBW(uint8_t size, uint8_t *pcbw);
+
+        bool IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw);
+
+        uint8_t ClearEpHalt(uint8_t index);
+#if MS_WANT_PARSER
+        uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf, uint8_t flags);
+#endif
+        uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf);
+        uint8_t HandleUsbError(uint8_t error, uint8_t index);
+        uint8_t HandleSCSIError(uint8_t status);
+
+};
+
+#endif // __MASSTORAGE_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/max3421e.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,236 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(_usb_h_) || defined(_max3421e_h_)
+#error "Never include max3421e.h directly; include Usb.h instead"
+#else
+
+#define _max3421e_h_
+
+/* MAX3421E register/bit names and bitmasks */
+
+/* Arduino pin definitions */
+/* pin numbers to port numbers */
+
+#define SE0     0
+#define SE1     1
+#define FSHOST  2
+#define LSHOST  3
+
+/* MAX3421E command byte format: rrrrr0wa where 'r' is register number  */
+//
+// MAX3421E Registers in HOST mode.
+//
+#define rRCVFIFO    0x08    //1<<3
+#define rSNDFIFO    0x10    //2<<3
+#define rSUDFIFO    0x20    //4<<3
+#define rRCVBC      0x30    //6<<3
+#define rSNDBC      0x38    //7<<3
+
+#define rUSBIRQ     0x68    //13<<3
+/* USBIRQ Bits  */
+#define bmVBUSIRQ   0x40    //b6
+#define bmNOVBUSIRQ 0x20    //b5
+#define bmOSCOKIRQ  0x01    //b0
+
+#define rUSBIEN     0x70    //14<<3
+/* USBIEN Bits  */
+#define bmVBUSIE    0x40    //b6
+#define bmNOVBUSIE  0x20    //b5
+#define bmOSCOKIE   0x01    //b0
+
+#define rUSBCTL     0x78    //15<<3
+/* USBCTL Bits  */
+#define bmCHIPRES   0x20    //b5
+#define bmPWRDOWN   0x10    //b4
+
+#define rCPUCTL     0x80    //16<<3
+/* CPUCTL Bits  */
+#define bmPUSLEWID1 0x80    //b7
+#define bmPULSEWID0 0x40    //b6
+#define bmIE        0x01    //b0
+
+#define rPINCTL     0x88    //17<<3
+/* PINCTL Bits  */
+#define bmFDUPSPI   0x10    //b4
+#define bmINTLEVEL  0x08    //b3
+#define bmPOSINT    0x04    //b2
+#define bmGPXB      0x02    //b1
+#define bmGPXA      0x01    //b0
+// GPX pin selections
+#define GPX_OPERATE 0x00
+#define GPX_VBDET   0x01
+#define GPX_BUSACT  0x02
+#define GPX_SOF     0x03
+
+#define rREVISION   0x90    //18<<3
+
+#define rIOPINS1    0xa0    //20<<3
+
+/* IOPINS1 Bits */
+#define bmGPOUT0    0x01
+#define bmGPOUT1    0x02
+#define bmGPOUT2    0x04
+#define bmGPOUT3    0x08
+#define bmGPIN0     0x10
+#define bmGPIN1     0x20
+#define bmGPIN2     0x40
+#define bmGPIN3     0x80
+
+#define rIOPINS2    0xa8    //21<<3
+/* IOPINS2 Bits */
+#define bmGPOUT4    0x01
+#define bmGPOUT5    0x02
+#define bmGPOUT6    0x04
+#define bmGPOUT7    0x08
+#define bmGPIN4     0x10
+#define bmGPIN5     0x20
+#define bmGPIN6     0x40
+#define bmGPIN7     0x80
+
+#define rGPINIRQ    0xb0    //22<<3
+/* GPINIRQ Bits */
+#define bmGPINIRQ0 0x01
+#define bmGPINIRQ1 0x02
+#define bmGPINIRQ2 0x04
+#define bmGPINIRQ3 0x08
+#define bmGPINIRQ4 0x10
+#define bmGPINIRQ5 0x20
+#define bmGPINIRQ6 0x40
+#define bmGPINIRQ7 0x80
+
+#define rGPINIEN    0xb8    //23<<3
+/* GPINIEN Bits */
+#define bmGPINIEN0 0x01
+#define bmGPINIEN1 0x02
+#define bmGPINIEN2 0x04
+#define bmGPINIEN3 0x08
+#define bmGPINIEN4 0x10
+#define bmGPINIEN5 0x20
+#define bmGPINIEN6 0x40
+#define bmGPINIEN7 0x80
+
+#define rGPINPOL    0xc0    //24<<3
+/* GPINPOL Bits */
+#define bmGPINPOL0 0x01
+#define bmGPINPOL1 0x02
+#define bmGPINPOL2 0x04
+#define bmGPINPOL3 0x08
+#define bmGPINPOL4 0x10
+#define bmGPINPOL5 0x20
+#define bmGPINPOL6 0x40
+#define bmGPINPOL7 0x80
+
+#define rHIRQ       0xc8    //25<<3
+/* HIRQ Bits */
+#define bmBUSEVENTIRQ   0x01   // indicates BUS Reset Done or BUS Resume
+#define bmRWUIRQ        0x02
+#define bmRCVDAVIRQ     0x04
+#define bmSNDBAVIRQ     0x08
+#define bmSUSDNIRQ      0x10
+#define bmCONDETIRQ     0x20
+#define bmFRAMEIRQ      0x40
+#define bmHXFRDNIRQ     0x80
+
+#define rHIEN           0xd0    //26<<3
+
+/* HIEN Bits */
+#define bmBUSEVENTIE    0x01
+#define bmRWUIE         0x02
+#define bmRCVDAVIE      0x04
+#define bmSNDBAVIE      0x08
+#define bmSUSDNIE       0x10
+#define bmCONDETIE      0x20
+#define bmFRAMEIE       0x40
+#define bmHXFRDNIE      0x80
+
+#define rMODE           0xd8    //27<<3
+
+/* MODE Bits */
+#define bmHOST          0x01
+#define bmLOWSPEED      0x02
+#define bmHUBPRE        0x04
+#define bmSOFKAENAB     0x08
+#define bmSEPIRQ        0x10
+#define bmDELAYISO      0x20
+#define bmDMPULLDN      0x40
+#define bmDPPULLDN      0x80
+
+#define rPERADDR    0xe0    //28<<3
+
+#define rHCTL       0xe8    //29<<3
+/* HCTL Bits */
+#define bmBUSRST        0x01
+#define bmFRMRST        0x02
+#define bmSAMPLEBUS     0x04
+#define bmSIGRSM        0x08
+#define bmRCVTOG0       0x10
+#define bmRCVTOG1       0x20
+#define bmSNDTOG0       0x40
+#define bmSNDTOG1       0x80
+
+#define rHXFR       0xf0    //30<<3
+/* Host transfer token values for writing the HXFR register (R30)   */
+/* OR this bit field with the endpoint number in bits 3:0               */
+#define tokSETUP  0x10  // HS=0, ISO=0, OUTNIN=0, SETUP=1
+#define tokIN     0x00  // HS=0, ISO=0, OUTNIN=0, SETUP=0
+#define tokOUT    0x20  // HS=0, ISO=0, OUTNIN=1, SETUP=0
+#define tokINHS   0x80  // HS=1, ISO=0, OUTNIN=0, SETUP=0
+#define tokOUTHS  0xA0  // HS=1, ISO=0, OUTNIN=1, SETUP=0
+#define tokISOIN  0x40  // HS=0, ISO=1, OUTNIN=0, SETUP=0
+#define tokISOOUT 0x60  // HS=0, ISO=1, OUTNIN=1, SETUP=0
+
+#define rHRSL       0xf8    //31<<3
+
+/* HRSL Bits */
+#define bmRCVTOGRD  0x10
+#define bmSNDTOGRD  0x20
+#define bmKSTATUS   0x40
+#define bmJSTATUS   0x80
+#define bmSE0       0x00    //SE0 - disconnect state
+#define bmSE1       0xc0    //SE1 - illegal state
+
+/* Host error result codes, the 4 LSB's in the HRSL register */
+#define hrSUCCESS   0x00
+#define hrBUSY      0x01
+#define hrBADREQ    0x02
+#define hrUNDEF     0x03
+#define hrNAK       0x04
+#define hrSTALL     0x05
+#define hrTOGERR    0x06
+#define hrWRONGPID  0x07
+#define hrBADBC     0x08
+#define hrPIDERR    0x09
+#define hrPKTERR    0x0A
+#define hrCRCERR    0x0B
+#define hrKERR      0x0C
+#define hrJERR      0x0D
+#define hrTIMEOUT   0x0E
+#define hrBABBLE    0x0F
+
+#define MODE_FS_HOST    (bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB)
+#define MODE_LS_HOST    (bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB)
+
+
+#endif //_max3421e_h_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/max_LCD.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,256 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#include "max_LCD.h"
+#include <string.h>
+
+// pin definition and set/clear
+
+#define RS  0x04    // RS pin
+#define E   0x08    // E pin
+
+#define SET_RS  lcdPins |= RS
+#define CLR_RS  lcdPins &= ~RS
+#define SET_E   lcdPins |= E
+#define CLR_E   lcdPins &= ~E
+
+#define SENDlcdPins()   pUsb->gpioWr( lcdPins )
+
+#define LCD_sendcmd(a)  {   CLR_RS;             \
+                            sendbyte(a);    \
+                        }
+
+#define LCD_sendchar(a) {   SET_RS;             \
+                            sendbyte(a);    \
+                        }
+
+static uint8_t lcdPins; //copy of LCD pins
+
+Max_LCD::Max_LCD(USB *pusb) : pUsb(pusb) {
+        lcdPins = 0;
+}
+
+void Max_LCD::init() {
+        _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
+
+        //   MAX3421E::gpioWr(0x55);
+
+        begin(16, 1);
+}
+
+void Max_LCD::begin(uint8_t cols __attribute__((unused)), uint8_t lines, uint8_t dotsize) {
+        if(lines > 1) {
+                _displayfunction |= LCD_2LINE;
+        }
+        _numlines = lines;
+        _currline = 0;
+
+        // for some 1 line displays you can select a 10 pixel high font
+        if((dotsize != 0) && (lines == 1)) {
+                _displayfunction |= LCD_5x10DOTS;
+        }
+
+        // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
+        // according to datasheet, we need at least 40ms after power rises above 2.7V
+        // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
+        delayMicroseconds(50000);
+        lcdPins = 0x30;
+        SET_E;
+        SENDlcdPins();
+        CLR_E;
+        SENDlcdPins();
+        delayMicroseconds(10000); // wait min 4.1ms
+        //second try
+        SET_E;
+        SENDlcdPins();
+        CLR_E;
+        SENDlcdPins();
+        delayMicroseconds(10000); // wait min 4.1ms
+        // third go!
+        SET_E;
+        SENDlcdPins();
+        CLR_E;
+        SENDlcdPins();
+        delayMicroseconds(10000);
+        // finally, set to 4-bit interface
+        lcdPins = 0x20;
+        //SET_RS;
+        SET_E;
+        SENDlcdPins();
+        //CLR_RS;
+        CLR_E;
+        SENDlcdPins();
+        delayMicroseconds(10000);
+        // finally, set # lines, font size, etc.
+        command(LCD_FUNCTIONSET | _displayfunction);
+
+        // turn the display on with no cursor or blinking default
+        _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
+        display();
+
+        // clear it off
+        clear();
+
+        // Initialize to default text direction (for romance languages)
+        _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
+        // set the entry mode
+        command(LCD_ENTRYMODESET | _displaymode);
+}
+
+/********** high level commands, for the user! */
+void Max_LCD::clear() {
+        command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero
+        delayMicroseconds(2000); // this command takes a long time!
+}
+
+void Max_LCD::home() {
+        command(LCD_RETURNHOME); // set cursor position to zero
+        delayMicroseconds(2000); // this command takes a long time!
+}
+
+void Max_LCD::setCursor(uint8_t col, uint8_t row) {
+        int row_offsets[] = {0x00, 0x40, 0x14, 0x54};
+        if(row > _numlines) {
+                row = _numlines - 1; // we count rows starting w/0
+        }
+
+        command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
+}
+
+// Turn the display on/off (quickly)
+
+void Max_LCD::noDisplay() {
+        _displaycontrol &= ~LCD_DISPLAYON;
+        command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+void Max_LCD::display() {
+        _displaycontrol |= LCD_DISPLAYON;
+        command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+// Turns the underline cursor on/off
+
+void Max_LCD::noCursor() {
+        _displaycontrol &= ~LCD_CURSORON;
+        command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+void Max_LCD::cursor() {
+        _displaycontrol |= LCD_CURSORON;
+        command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+
+// Turn on and off the blinking cursor
+
+void Max_LCD::noBlink() {
+        _displaycontrol &= ~LCD_BLINKON;
+        command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+void Max_LCD::blink() {
+        _displaycontrol |= LCD_BLINKON;
+        command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+// These commands scroll the display without changing the RAM
+
+void Max_LCD::scrollDisplayLeft(void) {
+        command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
+}
+
+void Max_LCD::scrollDisplayRight(void) {
+        command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
+}
+
+// This is for text that flows Left to Right
+
+void Max_LCD::leftToRight(void) {
+        _displaymode |= LCD_ENTRYLEFT;
+        command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This is for text that flows Right to Left
+
+void Max_LCD::rightToLeft(void) {
+        _displaymode &= ~LCD_ENTRYLEFT;
+        command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This will 'right justify' text from the cursor
+
+void Max_LCD::autoscroll(void) {
+        _displaymode |= LCD_ENTRYSHIFTINCREMENT;
+        command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This will 'left justify' text from the cursor
+
+void Max_LCD::noAutoscroll(void) {
+        _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
+        command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// Allows us to fill the first 8 CGRAM locations
+// with custom characters
+
+void Max_LCD::createChar(uint8_t location, uint8_t charmap[]) {
+        location &= 0x7; // we only have 8 locations 0-7
+        command(LCD_SETCGRAMADDR | (location << 3));
+        for(int i = 0; i < 8; i++) {
+                write(charmap[i]);
+        }
+}
+
+/*********** mid level commands, for sending data/cmds */
+
+inline void Max_LCD::command(uint8_t value) {
+        LCD_sendcmd(value);
+        delayMicroseconds(100);
+}
+
+#if defined(ARDUINO) && ARDUINO >=100
+
+inline size_t Max_LCD::write(uint8_t value) {
+        LCD_sendchar(value);
+        return 1; // Assume success
+}
+#else
+
+inline void Max_LCD::write(uint8_t value) {
+        LCD_sendchar(value);
+}
+#endif
+
+void Max_LCD::sendbyte(uint8_t val) {
+        lcdPins &= 0x0f; //prepare place for the upper nibble
+        lcdPins |= (val & 0xf0); //copy upper nibble to LCD variable
+        SET_E; //send
+        SENDlcdPins();
+        delayMicroseconds(2);
+        CLR_E;
+        delayMicroseconds(2);
+        SENDlcdPins();
+        lcdPins &= 0x0f; //prepare place for the lower nibble
+        lcdPins |= (val << 4) & 0xf0; //copy lower nibble to LCD variable
+        SET_E; //send
+        SENDlcdPins();
+        CLR_E;
+        SENDlcdPins();
+        delayMicroseconds(100);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/max_LCD.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,107 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+//HD44780 compatible LCD display via MAX3421E GPOUT support header
+//pinout: D[4-7] -> GPOUT[4-7], RS-> GPOUT[2], E ->GPOUT[3]
+//
+
+#ifndef _Max_LCD_h_
+#define _Max_LCD_h_
+
+#include "Usb.h"
+#include "Print.h"
+
+// commands
+#define LCD_CLEARDISPLAY        0x01
+#define LCD_RETURNHOME          0x02
+#define LCD_ENTRYMODESET        0x04
+#define LCD_DISPLAYCONTROL      0x08
+#define LCD_CURSORSHIFT         0x10
+#define LCD_FUNCTIONSET         0x20
+#define LCD_SETCGRAMADDR        0x40
+#define LCD_SETDDRAMADDR        0x80
+
+// flags for display entry mode
+#define LCD_ENTRYRIGHT          0x00
+#define LCD_ENTRYLEFT           0x02
+#define LCD_ENTRYSHIFTINCREMENT 0x01
+#define LCD_ENTRYSHIFTDECREMENT 0x00
+
+// flags for display on/off control
+#define LCD_DISPLAYON           0x04
+#define LCD_DISPLAYOFF          0x00
+#define LCD_CURSORON            0x02
+#define LCD_CURSOROFF           0x00
+#define LCD_BLINKON             0x01
+#define LCD_BLINKOFF            0x00
+
+// flags for display/cursor shift
+#define LCD_DISPLAYMOVE         0x08
+#define LCD_CURSORMOVE          0x00
+#define LCD_MOVERIGHT           0x04
+#define LCD_MOVELEFT            0x00
+
+// flags for function set
+#define LCD_8BITMODE            0x10
+#define LCD_4BITMODE            0x00
+#define LCD_2LINE               0x08
+#define LCD_1LINE               0x00
+#define LCD_5x10DOTS            0x04
+#define LCD_5x8DOTS             0x00
+
+class Max_LCD : public Print {
+        USB *pUsb;
+
+public:
+        Max_LCD(USB *pusb);
+        void init();
+        void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);
+        void clear();
+        void home();
+        void noDisplay();
+        void display();
+        void noBlink();
+        void blink();
+        void noCursor();
+        void cursor();
+        void scrollDisplayLeft();
+        void scrollDisplayRight();
+        void leftToRight();
+        void rightToLeft();
+        void autoscroll();
+        void noAutoscroll();
+        void createChar(uint8_t, uint8_t[]);
+        void setCursor(uint8_t, uint8_t);
+        void command(uint8_t);
+
+#if defined(ARDUINO) && ARDUINO >=100
+        size_t write(uint8_t);
+        using Print::write;
+#else
+        void write(uint8_t);
+#endif
+
+private:
+        void sendbyte(uint8_t val);
+        uint8_t _displayfunction; //tokill
+        uint8_t _displaycontrol;
+        uint8_t _displaymode;
+        uint8_t _initialized;
+        uint8_t _numlines, _currline;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/message.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,124 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#include "Usb.h"
+// 0x80 is the default (i.e. trace) to turn off set this global to something lower.
+// this allows for 126 other debugging levels.
+// TO-DO: Allow assignment to a different serial port by software
+int UsbDEBUGlvl = 0x80;
+
+void E_Notifyc(char c, int lvl) {
+        if(UsbDEBUGlvl < lvl) return;
+#if defined(ARDUINO) && ARDUINO >=100
+        ////USB_HOST_SERIAL.print(c);
+#else
+        //USB_HOST_SERIAL.print(c, BYTE);
+#endif
+        ////USB_HOST_SERIAL.flush();
+}
+
+void E_Notify(char const * msg, int lvl) {
+        if(UsbDEBUGlvl < lvl) return;
+        if(!msg) return;
+        char c;
+
+        while((c = pgm_read_byte(msg++))) E_Notifyc(c, lvl);
+}
+
+void E_NotifyStr(char const * msg, int lvl) {
+        if(UsbDEBUGlvl < lvl) return;
+        if(!msg) return;
+        char c;
+
+        while((c = *msg++)) E_Notifyc(c, lvl);
+}
+
+void E_Notify(uint8_t b, int lvl) {
+        if(UsbDEBUGlvl < lvl) return;
+#if defined(ARDUINO) && ARDUINO >=100
+        //USB_HOST_SERIAL.print(b);
+#else
+        //USB_HOST_SERIAL.print(b, DEC);
+#endif
+        ////USB_HOST_SERIAL.flush();
+}
+
+void E_Notify(double d, int lvl) {
+        if(UsbDEBUGlvl < lvl) return;
+        //USB_HOST_SERIAL.print(d);
+        ////USB_HOST_SERIAL.flush();
+}
+
+#ifdef DEBUG_USB_HOST
+
+void NotifyFailGetDevDescr(void) {
+        Notify(PSTR("\r\ngetDevDescr "), 0x80);
+}
+
+void NotifyFailSetDevTblEntry(void) {
+        Notify(PSTR("\r\nsetDevTblEn "), 0x80);
+}
+
+void NotifyFailGetConfDescr(void) {
+        Notify(PSTR("\r\ngetConf "), 0x80);
+}
+
+void NotifyFailSetConfDescr(void) {
+        Notify(PSTR("\r\nsetConf "), 0x80);
+}
+
+void NotifyFailGetDevDescr(uint8_t reason) {
+        NotifyFailGetDevDescr();
+        NotifyFail(reason);
+}
+
+void NotifyFailSetDevTblEntry(uint8_t reason) {
+        NotifyFailSetDevTblEntry();
+        NotifyFail(reason);
+
+}
+
+void NotifyFailGetConfDescr(uint8_t reason) {
+        NotifyFailGetConfDescr();
+        NotifyFail(reason);
+}
+
+void NotifyFailSetConfDescr(uint8_t reason) {
+        NotifyFailSetConfDescr();
+        NotifyFail(reason);
+}
+
+void NotifyFailUnknownDevice(uint16_t VID, uint16_t PID) {
+        Notify(PSTR("\r\nUnknown Device Connected - VID: "), 0x80);
+        D_PrintHex<uint16_t > (VID, 0x80);
+        Notify(PSTR(" PID: "), 0x80);
+        D_PrintHex<uint16_t > (PID, 0x80);
+}
+
+void NotifyFail(uint8_t rcode) {
+        D_PrintHex<uint8_t > (rcode, 0x80);
+        Notify(PSTR("\r\n"), 0x80);
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/message.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,86 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(_usb_h_) || defined(__MESSAGE_H__)
+#error "Never include message.h directly; include Usb.h instead"
+#else
+#define __MESSAGE_H__
+
+extern int UsbDEBUGlvl;
+
+void E_Notify(char const * msg, int lvl);
+void E_Notify(uint8_t b, int lvl);
+void E_NotifyStr(char const * msg, int lvl);
+void E_Notifyc(char c, int lvl);
+
+#ifdef DEBUG_USB_HOST
+#define Notify E_Notify
+#define NotifyStr E_NotifyStr
+#define Notifyc E_Notifyc
+void NotifyFailGetDevDescr(uint8_t reason);
+void NotifyFailSetDevTblEntry(uint8_t reason);
+void NotifyFailGetConfDescr(uint8_t reason);
+void NotifyFailSetConfDescr(uint8_t reason);
+void NotifyFailGetDevDescr(void);
+void NotifyFailSetDevTblEntry(void);
+void NotifyFailGetConfDescr(void);
+void NotifyFailSetConfDescr(void);
+void NotifyFailUnknownDevice(uint16_t VID, uint16_t PID);
+void NotifyFail(uint8_t rcode);
+#else
+#define Notify(...) ((void)0)
+#define NotifyStr(...) ((void)0)
+#define Notifyc(...) ((void)0)
+#define NotifyFailGetDevDescr(...) ((void)0)
+#define NotifyFailSetDevTblEntry(...) ((void)0)
+#define NotifyFailGetConfDescr(...) ((void)0)
+#define NotifyFailGetDevDescr(...) ((void)0)
+#define NotifyFailSetDevTblEntry(...) ((void)0)
+#define NotifyFailGetConfDescr(...) ((void)0)
+#define NotifyFailSetConfDescr(...) ((void)0)
+#define NotifyFailUnknownDevice(...) ((void)0)
+#define NotifyFail(...) ((void)0)
+#endif
+
+template <class ERROR_TYPE>
+void ErrorMessage(uint8_t level, char const * msg, ERROR_TYPE rcode = 0) {
+#ifdef DEBUG_USB_HOST
+        Notify(msg, level);
+        Notify(PSTR(": "), level);
+        D_PrintHex<ERROR_TYPE > (rcode, level);
+        Notify(PSTR("\r\n"), level);
+#endif
+}
+
+template <class ERROR_TYPE>
+void ErrorMessage(char const * msg __attribute__((unused)), ERROR_TYPE rcode __attribute__((unused)) = 0) {
+#ifdef DEBUG_USB_HOST
+        Notify(msg, 0x80);
+        Notify(PSTR(": "), 0x80);
+        D_PrintHex<ERROR_TYPE > (rcode, 0x80);
+        Notify(PSTR("\r\n"), 0x80);
+#endif
+}
+
+#endif // __MESSAGE_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/parsetools.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,75 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#include "Usb.h"
+
+bool MultiByteValueParser::Parse(uint8_t **pp, uint16_t *pcntdn) {
+        if(!pBuf) {
+                Notify(PSTR("Buffer pointer is NULL!\r\n"), 0x80);
+                return false;
+        }
+        for(; countDown && (*pcntdn); countDown--, (*pcntdn)--, (*pp)++)
+                pBuf[valueSize - countDown] = (**pp);
+
+        if(countDown)
+                return false;
+
+        countDown = valueSize;
+        return true;
+}
+
+bool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me) {
+        switch(nStage) {
+                case 0:
+                        pBuf->valueSize = lenSize;
+                        theParser.Initialize(pBuf);
+                        nStage = 1;
+
+                case 1:
+                        if(!theParser.Parse(pp, pcntdn))
+                                return false;
+
+                        arLen = 0;
+                        arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue));
+                        arLenCntdn = arLen;
+                        nStage = 2;
+
+                case 2:
+                        pBuf->valueSize = valSize;
+                        theParser.Initialize(pBuf);
+                        nStage = 3;
+
+                case 3:
+                        for(; arLenCntdn; arLenCntdn--) {
+                                if(!theParser.Parse(pp, pcntdn))
+                                        return false;
+
+                                if(pf)
+                                        pf(pBuf, (arLen - arLenCntdn), me);
+                        }
+
+                        nStage = 0;
+        }
+        return true;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/parsetools.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,153 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(_usb_h_) || defined(__PARSETOOLS_H__)
+#error "Never include parsetools.h directly; include Usb.h instead"
+#else
+#define __PARSETOOLS_H__
+
+struct MultiValueBuffer {
+        uint8_t valueSize;
+        void *pValue;
+
+public:
+
+        MultiValueBuffer() : valueSize(0), pValue(NULL) {
+        };
+} __attribute__((packed));
+
+class MultiByteValueParser {
+        uint8_t * pBuf;
+        uint8_t countDown;
+        uint8_t valueSize;
+
+public:
+
+        MultiByteValueParser() : pBuf(NULL), countDown(0), valueSize(0) {
+        };
+
+        const uint8_t* GetBuffer() {
+                return pBuf;
+        };
+
+        void Initialize(MultiValueBuffer * const pbuf) {
+                pBuf = (uint8_t*)pbuf->pValue;
+                countDown = valueSize = pbuf->valueSize;
+        };
+
+        bool Parse(uint8_t **pp, uint16_t *pcntdn);
+};
+
+class ByteSkipper {
+        uint8_t *pBuf;
+        uint8_t nStage;
+        uint16_t countDown;
+
+public:
+
+        ByteSkipper() : pBuf(NULL), nStage(0), countDown(0) {
+        };
+
+        void Initialize(MultiValueBuffer *pbuf) {
+                pBuf = (uint8_t*)pbuf->pValue;
+                countDown = 0;
+        };
+
+        bool Skip(uint8_t **pp, uint16_t *pcntdn, uint16_t bytes_to_skip) {
+                switch(nStage) {
+                        case 0:
+                                countDown = bytes_to_skip;
+                                nStage++;
+                        case 1:
+                                for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--);
+
+                                if(!countDown)
+                                        nStage = 0;
+                };
+                return (!countDown);
+        };
+};
+
+// Pointer to a callback function triggered for each element of PTP array when used with PTPArrayParser
+typedef void (*PTP_ARRAY_EL_FUNC)(const MultiValueBuffer * const p, uint32_t count, const void *me);
+
+class PTPListParser {
+public:
+
+        enum ParseMode {
+                modeArray, modeRange/*, modeEnum*/
+        };
+
+private:
+        uint8_t nStage;
+        uint8_t enStage;
+
+        uint32_t arLen;
+        uint32_t arLenCntdn;
+
+        uint8_t lenSize; // size of the array length field in bytes
+        uint8_t valSize; // size of the array element in bytes
+
+        MultiValueBuffer *pBuf;
+
+        // The only parser for both size and array element parsing
+        MultiByteValueParser theParser;
+
+        uint8_t /*ParseMode*/ prsMode;
+
+public:
+
+        PTPListParser() :
+        nStage(0),
+        enStage(0),
+        arLen(0),
+        arLenCntdn(0),
+        lenSize(0),
+        valSize(0),
+        pBuf(NULL),
+        prsMode(modeArray) {
+        };
+
+        void Initialize(const uint8_t len_size, const uint8_t val_size, MultiValueBuffer * const p, const uint8_t mode = modeArray) {
+                pBuf = p;
+                lenSize = len_size;
+                valSize = val_size;
+                prsMode = mode;
+
+                if(prsMode == modeRange) {
+                        arLenCntdn = arLen = 3;
+                        nStage = 2;
+                } else {
+                        arLenCntdn = arLen = 0;
+                        nStage = 0;
+                }
+                enStage = 0;
+                theParser.Initialize(p);
+        };
+
+        bool Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me = NULL);
+};
+
+#endif // __PARSETOOLS_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/printhex.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,92 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(_usb_h_) || defined(__PRINTHEX_H__)
+#error "Never include printhex.h directly; include Usb.h instead"
+#else
+#define __PRINTHEX_H__
+
+void E_Notifyc(char c, int lvl);
+
+template <class T>
+void PrintHex(T val, int lvl) {
+        int num_nibbles = sizeof (T) * 2;
+
+        do {
+                char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0f);
+                if(v > 57) v += 7;
+                E_Notifyc(v, lvl);
+        } while(--num_nibbles);
+}
+
+template <class T>
+void PrintBin(T val, int lvl) {
+        for(T mask = (((T)1) << ((sizeof (T) << 3) - 1)); mask; mask >>= 1)
+                if(val & mask)
+                        E_Notifyc('1', lvl);
+                else
+                        E_Notifyc('0', lvl);
+}
+
+template <class T>
+void SerialPrintHex(T val) {
+        int num_nibbles = sizeof (T) * 2;
+
+        do {
+                char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0f);
+                if(v > 57) v += 7;
+                USB_HOST_SERIAL.print(v);
+        } while(--num_nibbles);
+}
+
+template <class T>
+void PrintHex2(Print *prn, T val) {
+        T mask = (((T)1) << (((sizeof (T) << 1) - 1) << 2));
+
+        while(mask > 1) {
+                if(val < mask)
+                        prn->print("0");
+
+                mask >>= 4;
+        }
+        prn->print((T)val, HEX);
+}
+
+template <class T> void D_PrintHex(T val __attribute__((unused)), int lvl __attribute__((unused))) {
+#ifdef DEBUG_USB_HOST
+        PrintHex<T > (val, lvl);
+#endif
+}
+
+template <class T>
+void D_PrintBin(T val, int lvl) {
+#ifdef DEBUG_USB_HOST
+        PrintBin<T > (val, lvl);
+#endif
+}
+
+
+
+#endif // __PRINTHEX_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/settings.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,225 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+/*
+warning
+#define _usb_h_
+#define MBED_H
+*/
+
+#ifndef USB_HOST_SHIELD_SETTINGS_H
+#define USB_HOST_SHIELD_SETTINGS_H
+#include "macros.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// SPI Configuration
+////////////////////////////////////////////////////////////////////////////////
+#ifndef USB_SPI
+#define USB_SPI SPI
+//#define USB_SPI SPI1
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// DEBUGGING
+////////////////////////////////////////////////////////////////////////////////
+
+/* Set this to 1 to activate serial debugging */
+#define ENABLE_UHS_DEBUGGING 0
+
+/* This can be used to select which serial port to use for debugging if
+ * multiple serial ports are available.
+ * For example Serial3.
+ */
+
+#define USB_HOST_SERIAL ; //
+#ifndef USB_HOST_SERIAL
+#define USB_HOST_SERIAL //
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Manual board activation
+////////////////////////////////////////////////////////////////////////////////
+
+/* Set this to 1 if you are using an Arduino Mega ADK board with MAX3421e built-in */
+#define USE_UHS_MEGA_ADK 0 // If you are using Arduino 1.5.5 or newer there is no need to do this manually
+
+/* Set this to 1 if you are using a Black Widdow */
+#define USE_UHS_BLACK_WIDDOW 0
+
+/* Set this to a one to use the xmem2 lock. This is needed for multitasking and threading */
+#define USE_XMEM_SPI_LOCK 0
+
+////////////////////////////////////////////////////////////////////////////////
+// Wii IR camera
+////////////////////////////////////////////////////////////////////////////////
+
+/* Set this to 1 to activate code for the Wii IR camera */
+#define ENABLE_WII_IR_CAMERA 0
+
+////////////////////////////////////////////////////////////////////////////////
+// MASS STORAGE
+////////////////////////////////////////////////////////////////////////////////
+// <<<<<<<<<<<<<<<< IMPORTANT >>>>>>>>>>>>>>>
+// Set this to 1 to support single LUN devices, and save RAM. -- I.E. thumb drives.
+// Each LUN needs ~13 bytes to be able to track the state of each unit.
+#ifndef MASS_MAX_SUPPORTED_LUN
+#define MASS_MAX_SUPPORTED_LUN 8
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Set to 1 to use the faster spi4teensy3 driver.
+////////////////////////////////////////////////////////////////////////////////
+#ifndef USE_SPI4TEENSY3
+#define USE_SPI4TEENSY3 1
+#endif
+
+// Disabled on the Teensy LC, as it is incompatible for now
+#if defined(__MKL26Z64__)
+#undef USE_SPI4TEENSY3
+#define USE_SPI4TEENSY3 0
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// AUTOMATIC Settings
+////////////////////////////////////////////////////////////////////////////////
+
+// No user serviceable parts below this line.
+// DO NOT change anything below here unless you are a developer!
+
+#include "version_helper.h"
+
+#if defined(__GNUC__) && defined(__AVR__)
+#ifndef GCC_VERSION
+#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+#endif
+#if GCC_VERSION < 40602 // Test for GCC < 4.6.2
+#ifdef PROGMEM
+#undef PROGMEM
+#define PROGMEM __attribute__((section(".progmem.data"))) // Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734#c4
+#ifdef PSTR
+#undef PSTR
+#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0]; })) // Copied from pgmspace.h in avr-libc source
+#endif
+#endif
+#endif
+#endif
+
+#if !defined(DEBUG_USB_HOST) && ENABLE_UHS_DEBUGGING
+#define DEBUG_USB_HOST
+#endif
+
+#if !defined(WIICAMERA) && ENABLE_WII_IR_CAMERA
+#define WIICAMERA
+#endif
+
+// To use some other locking (e.g. freertos),
+// define XMEM_ACQUIRE_SPI and XMEM_RELEASE_SPI to point to your lock and unlock.
+// NOTE: NO argument is passed. You have to do this within your routine for
+// whatever you are using to lock and unlock.
+#if !defined(XMEM_ACQUIRE_SPI)
+#if USE_XMEM_SPI_LOCK || defined(USE_MULTIPLE_APP_API)
+#include <xmem.h>
+#else
+#define XMEM_ACQUIRE_SPI() (void(0))
+#define XMEM_RELEASE_SPI() (void(0))
+#endif
+#endif
+
+#if !defined(EXT_RAM) && defined(EXT_RAM_STACK) || defined(EXT_RAM_HEAP)
+#include <xmem.h>
+#else
+#define EXT_RAM 0
+#endif
+
+#if defined(CORE_TEENSY) && defined(KINETISK)
+#define USING_SPI4TEENSY3 USE_SPI4TEENSY3
+#else
+#define USING_SPI4TEENSY3 0
+#endif
+
+#if ((defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(__ARDUINO_X86__) || ARDUINO >= 10600) && !USING_SPI4TEENSY3
+#include <SPI.h> // Use the Arduino SPI library for the Arduino Due, Intel Galileo 1 & 2, Intel Edison or if the SPI library with transaction is available
+#endif
+#ifdef RBL_NRF51822
+#include <nrf_gpio.h>
+#include <SPI_Master.h>
+#define SPI SPI_Master
+#define MFK_CASTUINT8T (uint8_t) // RBLs return type for sizeof needs casting to uint8_t
+#endif
+#if defined(__PIC32MX__) || defined(__PIC32MZ__)
+#include <../../../../hardware/pic32/libraries/SPI/SPI.h> // Hack to use the SPI library
+#endif
+
+#if defined(ESP8266) || defined(ESP32)
+#define MFK_CASTUINT8T (uint8_t) // ESP return type for sizeof needs casting to uint8_t
+#endif
+
+/*
+#ifdef STM32F4
+#include "stm32f4xx_hal.h"
+extern SPI_HandleTypeDef SPI_Handle; // Needed to be declared in your main.cpp
+#endif
+*/
+
+// Fix defines on Arduino Due
+#ifdef ARDUINO_SAM_DUE
+#ifdef tokSETUP
+#undef tokSETUP
+#endif
+#ifdef tokIN
+#undef tokIN
+#endif
+#ifdef tokOUT
+#undef tokOUT
+#endif
+#ifdef tokINHS
+#undef tokINHS
+#endif
+#ifdef tokOUTHS
+#undef tokOUTHS
+#endif
+#endif
+
+// Set defaults
+#ifndef MFK_CASTUINT8T
+#define MFK_CASTUINT8T
+#endif
+
+// Workaround issue: https://github.com/esp8266/Arduino/issues/2078
+#ifdef ESP8266
+#undef PROGMEM
+#define PROGMEM
+#undef PSTR
+#define PSTR(s) (s)
+#undef pgm_read_byte
+#define pgm_read_byte(addr) (*reinterpret_cast<const uint8_t *>(addr))
+#undef pgm_read_word
+#define pgm_read_word(addr) (*reinterpret_cast<const uint16_t *>(addr))
+#endif
+
+#ifdef ARDUINO_ESP8266_WIFIO
+#error "This board is currently not supported"
+#endif
+
+#endif /* SETTINGS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/sink_parser.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,50 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(_usb_h_) || defined(__SINK_PARSER_H__)
+#error "Never include hexdump.h directly; include Usb.h instead"
+#else
+#define __SINK_PARSER_H__
+
+extern int UsbDEBUGlvl;
+
+// This parser does absolutely nothing with the data, just swallows it.
+
+template <class BASE_CLASS, class LEN_TYPE, class OFFSET_TYPE>
+class SinkParser : public BASE_CLASS {
+public:
+
+        SinkParser() {
+        };
+
+        void Initialize() {
+        };
+
+        void Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset) {
+        };
+};
+
+
+#endif // __HEXDUMP_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usb_ch9.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,174 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#if !defined(_usb_h_) || defined(_ch9_h_)
+#error "Never include usb_ch9.h directly; include Usb.h instead"
+#else
+
+/* USB chapter 9 structures */
+#define _ch9_h_
+
+/* Misc.USB constants */
+#define DEV_DESCR_LEN   18      //device descriptor length
+#define CONF_DESCR_LEN  9       //configuration descriptor length
+#define INTR_DESCR_LEN  9       //interface descriptor length
+#define EP_DESCR_LEN    7       //endpoint descriptor length
+
+/* Standard Device Requests */
+
+#define USB_REQUEST_GET_STATUS                  0       // Standard Device Request - GET STATUS
+#define USB_REQUEST_CLEAR_FEATURE               1       // Standard Device Request - CLEAR FEATURE
+#define USB_REQUEST_SET_FEATURE                 3       // Standard Device Request - SET FEATURE
+#define USB_REQUEST_SET_ADDRESS                 5       // Standard Device Request - SET ADDRESS
+#define USB_REQUEST_GET_DESCRIPTOR              6       // Standard Device Request - GET DESCRIPTOR
+#define USB_REQUEST_SET_DESCRIPTOR              7       // Standard Device Request - SET DESCRIPTOR
+#define USB_REQUEST_GET_CONFIGURATION           8       // Standard Device Request - GET CONFIGURATION
+#define USB_REQUEST_SET_CONFIGURATION           9       // Standard Device Request - SET CONFIGURATION
+#define USB_REQUEST_GET_INTERFACE               10      // Standard Device Request - GET INTERFACE
+#define USB_REQUEST_SET_INTERFACE               11      // Standard Device Request - SET INTERFACE
+#define USB_REQUEST_SYNCH_FRAME                 12      // Standard Device Request - SYNCH FRAME
+
+#define USB_FEATURE_ENDPOINT_HALT               0       // CLEAR/SET FEATURE - Endpoint Halt
+#define USB_FEATURE_DEVICE_REMOTE_WAKEUP        1       // CLEAR/SET FEATURE - Device remote wake-up
+#define USB_FEATURE_TEST_MODE                   2       // CLEAR/SET FEATURE - Test mode
+
+/* Setup Data Constants */
+
+#define USB_SETUP_HOST_TO_DEVICE                0x00    // Device Request bmRequestType transfer direction - host to device transfer
+#define USB_SETUP_DEVICE_TO_HOST                0x80    // Device Request bmRequestType transfer direction - device to host transfer
+#define USB_SETUP_TYPE_STANDARD                 0x00    // Device Request bmRequestType type - standard
+#define USB_SETUP_TYPE_CLASS                    0x20    // Device Request bmRequestType type - class
+#define USB_SETUP_TYPE_VENDOR                   0x40    // Device Request bmRequestType type - vendor
+#define USB_SETUP_RECIPIENT_DEVICE              0x00    // Device Request bmRequestType recipient - device
+#define USB_SETUP_RECIPIENT_INTERFACE           0x01    // Device Request bmRequestType recipient - interface
+#define USB_SETUP_RECIPIENT_ENDPOINT            0x02    // Device Request bmRequestType recipient - endpoint
+#define USB_SETUP_RECIPIENT_OTHER               0x03    // Device Request bmRequestType recipient - other
+
+/* USB descriptors  */
+
+#define USB_DESCRIPTOR_DEVICE                   0x01    // bDescriptorType for a Device Descriptor.
+#define USB_DESCRIPTOR_CONFIGURATION            0x02    // bDescriptorType for a Configuration Descriptor.
+#define USB_DESCRIPTOR_STRING                   0x03    // bDescriptorType for a String Descriptor.
+#define USB_DESCRIPTOR_INTERFACE                0x04    // bDescriptorType for an Interface Descriptor.
+#define USB_DESCRIPTOR_ENDPOINT                 0x05    // bDescriptorType for an Endpoint Descriptor.
+#define USB_DESCRIPTOR_DEVICE_QUALIFIER         0x06    // bDescriptorType for a Device Qualifier.
+#define USB_DESCRIPTOR_OTHER_SPEED              0x07    // bDescriptorType for a Other Speed Configuration.
+#define USB_DESCRIPTOR_INTERFACE_POWER          0x08    // bDescriptorType for Interface Power.
+#define USB_DESCRIPTOR_OTG                      0x09    // bDescriptorType for an OTG Descriptor.
+
+#define HID_DESCRIPTOR_HID                      0x21
+
+
+
+/* OTG SET FEATURE Constants    */
+#define OTG_FEATURE_B_HNP_ENABLE                3       // SET FEATURE OTG - Enable B device to perform HNP
+#define OTG_FEATURE_A_HNP_SUPPORT               4       // SET FEATURE OTG - A device supports HNP
+#define OTG_FEATURE_A_ALT_HNP_SUPPORT           5       // SET FEATURE OTG - Another port on the A device supports HNP
+
+/* USB Endpoint Transfer Types  */
+#define USB_TRANSFER_TYPE_CONTROL               0x00    // Endpoint is a control endpoint.
+#define USB_TRANSFER_TYPE_ISOCHRONOUS           0x01    // Endpoint is an isochronous endpoint.
+#define USB_TRANSFER_TYPE_BULK                  0x02    // Endpoint is a bulk endpoint.
+#define USB_TRANSFER_TYPE_INTERRUPT             0x03    // Endpoint is an interrupt endpoint.
+#define bmUSB_TRANSFER_TYPE                     0x03    // bit mask to separate transfer type from ISO attributes
+
+
+/* Standard Feature Selectors for CLEAR_FEATURE Requests    */
+#define USB_FEATURE_ENDPOINT_STALL              0       // Endpoint recipient
+#define USB_FEATURE_DEVICE_REMOTE_WAKEUP        1       // Device recipient
+#define USB_FEATURE_TEST_MODE                   2       // Device recipient
+
+/* descriptor data structures */
+
+/* Device descriptor structure */
+typedef struct {
+        uint8_t bLength; // Length of this descriptor.
+        uint8_t bDescriptorType; // DEVICE descriptor type (USB_DESCRIPTOR_DEVICE).
+        uint16_t bcdUSB; // USB Spec Release Number (BCD).
+        uint8_t bDeviceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific.
+        uint8_t bDeviceSubClass; // Subclass code (assigned by the USB-IF).
+        uint8_t bDeviceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific.
+        uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0.
+        uint16_t idVendor; // Vendor ID (assigned by the USB-IF).
+        uint16_t idProduct; // Product ID (assigned by the manufacturer).
+        uint16_t bcdDevice; // Device release number (BCD).
+        uint8_t iManufacturer; // Index of String Descriptor describing the manufacturer.
+        uint8_t iProduct; // Index of String Descriptor describing the product.
+        uint8_t iSerialNumber; // Index of String Descriptor with the device's serial number.
+        uint8_t bNumConfigurations; // Number of possible configurations.
+} __attribute__((packed)) USB_DEVICE_DESCRIPTOR;
+
+/* Configuration descriptor structure */
+typedef struct {
+        uint8_t bLength; // Length of this descriptor.
+        uint8_t bDescriptorType; // CONFIGURATION descriptor type (USB_DESCRIPTOR_CONFIGURATION).
+        uint16_t wTotalLength; // Total length of all descriptors for this configuration.
+        uint8_t bNumInterfaces; // Number of interfaces in this configuration.
+        uint8_t bConfigurationValue; // Value of this configuration (1 based).
+        uint8_t iConfiguration; // Index of String Descriptor describing the configuration.
+        uint8_t bmAttributes; // Configuration characteristics.
+        uint8_t bMaxPower; // Maximum power consumed by this configuration.
+} __attribute__((packed)) USB_CONFIGURATION_DESCRIPTOR;
+
+/* Interface descriptor structure */
+typedef struct {
+        uint8_t bLength; // Length of this descriptor.
+        uint8_t bDescriptorType; // INTERFACE descriptor type (USB_DESCRIPTOR_INTERFACE).
+        uint8_t bInterfaceNumber; // Number of this interface (0 based).
+        uint8_t bAlternateSetting; // Value of this alternate interface setting.
+        uint8_t bNumEndpoints; // Number of endpoints in this interface.
+        uint8_t bInterfaceClass; // Class code (assigned by the USB-IF).  0xFF-Vendor specific.
+        uint8_t bInterfaceSubClass; // Subclass code (assigned by the USB-IF).
+        uint8_t bInterfaceProtocol; // Protocol code (assigned by the USB-IF).  0xFF-Vendor specific.
+        uint8_t iInterface; // Index of String Descriptor describing the interface.
+} __attribute__((packed)) USB_INTERFACE_DESCRIPTOR;
+
+/* Endpoint descriptor structure */
+typedef struct {
+        uint8_t bLength; // Length of this descriptor.
+        uint8_t bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT).
+        uint8_t bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN).
+        uint8_t bmAttributes; // Endpoint transfer type.
+        uint16_t wMaxPacketSize; // Maximum packet size.
+        uint8_t bInterval; // Polling interval in frames.
+} __attribute__((packed)) USB_ENDPOINT_DESCRIPTOR;
+
+/* HID descriptor */
+typedef struct {
+        uint8_t bLength;
+        uint8_t bDescriptorType;
+        uint16_t bcdHID; // HID class specification release
+        uint8_t bCountryCode;
+        uint8_t bNumDescriptors; // Number of additional class specific descriptors
+        uint8_t bDescrType; // Type of class descriptor
+        uint16_t wDescriptorLength; // Total size of the Report descriptor
+} __attribute__((packed)) USB_HID_DESCRIPTOR;
+
+typedef struct {
+        uint8_t bDescrType; // Type of class descriptor
+        uint16_t wDescriptorLength; // Total size of the Report descriptor
+} __attribute__((packed)) HID_CLASS_DESCRIPTOR_LEN_AND_TYPE;
+
+#endif // _ch9_h_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usbh_midi.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,649 @@
+/*
+ *******************************************************************************
+ * USB-MIDI class driver for USB Host Shield 2.0 Library
+ * Copyright (c) 2012-2018 Yuuichi Akagawa
+ *
+ * Idea from LPK25 USB-MIDI to Serial MIDI converter
+ *   by Collin Cunningham - makezine.com, narbotic.com
+ *
+ * for use with USB Host Shield 2.0 from Circuitsathome.com
+ * https://github.com/felis/USB_Host_Shield_2.0
+ *******************************************************************************
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *******************************************************************************
+ */
+
+#include "usbh_midi.h"
+//////////////////////////
+// MIDI MESAGES
+// midi.org/techspecs/
+//////////////////////////
+// STATUS BYTES
+// 0x8n == noteOff
+// 0x9n == noteOn
+// 0xAn == afterTouch
+// 0xBn == controlChange
+//    n == Channel(0x0-0xf)
+//////////////////////////
+//DATA BYTE 1
+// note# == (0-127)
+// or
+// control# == (0-119)
+//////////////////////////
+// DATA BYTE 2
+// velocity == (0-127)
+// or
+// controlVal == (0-127)
+///////////////////////////////////////////////////////////////////////////////
+// USB-MIDI Event Packets
+// usb.org - Universal Serial Bus Device Class Definition for MIDI Devices 1.0
+///////////////////////////////////////////////////////////////////////////////
+//+-------------+-------------+-------------+-------------+
+//|   Byte 0    |   Byte 1    |   Byte 2    |   Byte 3    |
+//+------+------+-------------+-------------+-------------+
+//|Cable | Code |             |             |             |
+//|Number|Index |   MIDI_0    |   MIDI_1    |   MIDI_2    |
+//|      |Number|             |             |             |
+//|(4bit)|(4bit)|   (8bit)    |   (8bit)    |   (8bit)    |
+//+------+------+-------------+-------------+-------------+
+// CN == 0x0-0xf
+//+-----+-----------+-------------------------------------------------------------------
+//| CIN |MIDI_x size|Description
+//+-----+-----------+-------------------------------------------------------------------
+//| 0x0 | 1, 2 or 3 |Miscellaneous function codes. Reserved for future extensions.
+//| 0x1 | 1, 2 or 3 |Cable events. Reserved for future expansion.
+//| 0x2 |     2     |Two-byte System Common messages like MTC, SongSelect, etc.
+//| 0x3 |     3     |Three-byte System Common messages like SPP, etc.
+//| 0x4 |     3     |SysEx starts or continues
+//| 0x5 |     1     |Single-byte System Common Message or SysEx ends with following single byte.
+//| 0x6 |     2     |SysEx ends with following two bytes.
+//| 0x7 |     3     |SysEx ends with following three bytes.
+//| 0x8 |     3     |Note-off
+//| 0x9 |     3     |Note-on
+//| 0xA |     3     |Poly-KeyPress
+//| 0xB |     3     |Control Change
+//| 0xC |     2     |Program Change
+//| 0xD |     2     |Channel Pressure
+//| 0xE |     3     |PitchBend Change
+//| 0xF |     1     |Single Byte
+//+-----+-----------+-------------------------------------------------------------------
+
+const uint8_t USBH_MIDI::epDataInIndex  = 1;
+const uint8_t USBH_MIDI::epDataOutIndex = 2;
+const uint8_t USBH_MIDI::epDataInIndexVSP  = 3;
+const uint8_t USBH_MIDI::epDataOutIndexVSP = 4;
+
+USBH_MIDI::USBH_MIDI(USB *p) :
+pUsb(p),
+bAddress(0),
+bNumEP(1),
+bPollEnable(false),
+isMidiFound(false),
+readPtr(0) {
+        // initialize endpoint data structures
+        for(uint8_t i=0; i<MIDI_MAX_ENDPOINTS; i++) {
+                epInfo[i].epAddr      = 0;
+                epInfo[i].maxPktSize  = (i) ? 0 : 8;
+                epInfo[i].bmNakPower  = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;
+
+        }
+        // register in USB subsystem
+        if (pUsb) {
+                pUsb->RegisterDeviceClass(this);
+        }
+}
+
+/* Connection initialization of an MIDI Device */
+uint8_t USBH_MIDI::Init(uint8_t parent, uint8_t port, bool lowspeed)
+{
+        uint8_t    buf[sizeof (USB_DEVICE_DESCRIPTOR)];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        uint8_t    rcode;
+        UsbDevice  *p = NULL;
+        EpInfo     *oldep_ptr = NULL;
+        uint8_t    num_of_conf;  // number of configurations
+
+        USBTRACE("\rMIDI Init\r\n");
+
+        //for reconnect
+        for(uint8_t i=epDataInIndex; i<=epDataOutIndex; i++) {
+                epInfo[i].epAddr      = (i==epDataInIndex) ? 0x81 : 0x01;
+                epInfo[i].maxPktSize  = 0;
+                epInfo[i].bmSndToggle = 0;
+                epInfo[i].bmRcvToggle = 0;
+        }
+
+        // get memory address of USB device address pool
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        // check if address has already been assigned to an instance
+        if (bAddress) {
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+        }
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(bAddress);
+        if (!p) {
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+        if (!p->epinfo) {
+                return USB_ERROR_EPINFO_IS_NULL;
+        }
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr( 0, 0, sizeof(USB_DEVICE_DESCRIPTOR), (uint8_t*)buf );
+        vid = udd->idVendor;
+        pid = udd->idProduct;
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if( rcode ){
+                goto FailGetDevDescr;
+        }
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, false, port);
+        if (!bAddress) {
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+        }
+
+        // Extract Max Packet Size from device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr( 0, 0, bAddress );
+        if (rcode) {
+                p->lowspeed = false;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                return rcode;
+        }//if (rcode...
+        USBTRACE2("Addr:", bAddress);
+
+        p->lowspeed = false;
+
+        //get pointer to assigned address record
+        p = addrPool.GetUsbDevicePtr(bAddress);
+        if (!p) {
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+        }
+        p->lowspeed = lowspeed;
+
+        num_of_conf = udd->bNumConfigurations;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);
+        if (rcode) {
+                USBTRACE("setEpInfoEntry failed");
+                goto FailSetDevTblEntry;
+        }
+
+        USBTRACE("VID:"), D_PrintHex(vid, 0x80);
+        USBTRACE(" PID:"), D_PrintHex(pid, 0x80);
+        USBTRACE2(" #Conf:", num_of_conf);
+
+        //Setup for well known vendor/device specific configuration
+        bTransferTypeMask = bmUSB_TRANSFER_TYPE;
+        setupDeviceSpecific();
+
+        isMidiFound  = false;
+        for (uint8_t i=0; i<num_of_conf; i++) {
+                rcode = parseConfigDescr(bAddress, i);
+                if( rcode )
+                        goto FailGetConfDescr;
+                if (bNumEP > 1)
+                        break;
+        } // for
+
+        USBTRACE2("\r\nNumEP:", bNumEP);
+
+        if( bNumEP < 2 ){  //Device not found.
+                rcode = 0xff;
+                goto FailGetConfDescr;
+        }
+
+        if( !isMidiFound ){ //MIDI Device not found. Try last Bulk transfer device
+                USBTRACE("MIDI not found. Attempts bulk device\r\n");
+                epInfo[epDataInIndex].epAddr      = epInfo[epDataInIndexVSP].epAddr;
+                epInfo[epDataInIndex].maxPktSize  = epInfo[epDataInIndexVSP].maxPktSize;
+                epInfo[epDataOutIndex].epAddr     = epInfo[epDataOutIndexVSP].epAddr;
+                epInfo[epDataOutIndex].maxPktSize = epInfo[epDataOutIndexVSP].maxPktSize;
+        }
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);
+        USBTRACE2("Conf:", bConfNum);
+        USBTRACE2("EPin :", (uint8_t)(epInfo[epDataInIndex].epAddr + 0x80));
+        USBTRACE2("EPout:", epInfo[epDataOutIndex].epAddr);
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, bConfNum);
+        if (rcode) {
+                goto FailSetConfDescr;
+        }
+        bPollEnable = true;
+        USBTRACE("Init done.\r\n");
+        return 0;
+FailGetDevDescr:
+FailSetDevTblEntry:
+FailGetConfDescr:
+FailSetConfDescr:
+        Release();
+        return rcode;
+}
+
+/* get and parse config descriptor */
+uint8_t USBH_MIDI::parseConfigDescr( uint8_t addr, uint8_t conf )
+{
+        uint8_t buf[ DESC_BUFF_SIZE ];
+        uint8_t* buf_ptr = buf;
+        uint8_t rcode;
+        uint8_t descr_length;
+        uint8_t descr_type;
+        uint16_t total_length;
+        USB_ENDPOINT_DESCRIPTOR *epDesc;
+        bool isMidi = false;
+
+        // get configuration descriptor (get descriptor size only)
+        rcode = pUsb->getConfDescr( addr, 0, 4, conf, buf );
+        if( rcode ){
+                return rcode;
+        }
+        total_length = buf[2] | ((int)buf[3] << 8);
+        if( total_length > DESC_BUFF_SIZE ) {    //check if total length is larger than buffer
+                total_length = DESC_BUFF_SIZE;
+        }
+
+        // get configuration descriptor (all)
+        rcode = pUsb->getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor
+        if( rcode ){
+                return rcode;
+        }
+
+        //parsing descriptors
+        while( buf_ptr < buf + total_length ) {
+                descr_length = *( buf_ptr );
+                descr_type   = *( buf_ptr + 1 );
+                switch( descr_type ) {
+                  case USB_DESCRIPTOR_CONFIGURATION :
+                        bConfNum = buf_ptr[5];
+                        break;
+                  case  USB_DESCRIPTOR_INTERFACE :
+                        USBTRACE("\r\nConf:"), D_PrintHex(bConfNum, 0x80);
+                        USBTRACE(" Int:"), D_PrintHex(buf_ptr[2], 0x80);
+                        USBTRACE(" Alt:"), D_PrintHex(buf_ptr[3], 0x80);
+                        USBTRACE(" EPs:"), D_PrintHex(buf_ptr[4], 0x80);
+                        USBTRACE(" IntCl:"), D_PrintHex(buf_ptr[5], 0x80);
+                        USBTRACE(" IntSubCl:"), D_PrintHex(buf_ptr[6], 0x80);
+                        USBTRACE("\r\n");
+
+                        if( buf_ptr[5] == USB_CLASS_AUDIO && buf_ptr[6] == USB_SUBCLASS_MIDISTREAMING ) {  //p[5]; bInterfaceClass = 1(Audio), p[6]; bInterfaceSubClass = 3(MIDI Streaming)
+                                isMidiFound = true; //MIDI device found.
+                                isMidi      = true;
+                                USBTRACE("MIDI Device\r\n");
+                        }else{
+                                isMidi = false;
+                                USBTRACE("No MIDI Device\r\n");
+                        }
+                        break;
+                  case USB_DESCRIPTOR_ENDPOINT :
+                        epDesc = (USB_ENDPOINT_DESCRIPTOR *)buf_ptr;
+                        USBTRACE("-EPAddr:"), D_PrintHex(epDesc->bEndpointAddress, 0x80);
+                        USBTRACE(" bmAttr:"), D_PrintHex(epDesc->bmAttributes, 0x80);
+                        USBTRACE2(" MaxPktSz:", (uint8_t)epDesc->wMaxPacketSize);
+                        if ((epDesc->bmAttributes & bTransferTypeMask) == USB_TRANSFER_TYPE_BULK) {//bulk
+                                uint8_t index;
+                                if( isMidi )
+                                        index = ((epDesc->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;
+                                else
+                                        index = ((epDesc->bEndpointAddress & 0x80) == 0x80) ? epDataInIndexVSP : epDataOutIndexVSP;
+                                epInfo[index].epAddr     = (epDesc->bEndpointAddress & 0x0F);
+                                epInfo[index].maxPktSize = (uint8_t)epDesc->wMaxPacketSize;
+                                bNumEP ++;
+#ifdef DEBUG_USB_HOST
+                                PrintEndpointDescriptor(epDesc);
+#endif
+                        }
+                        break;
+                  default:
+                        break;
+                }//switch( descr_type
+                buf_ptr += descr_length;    //advance buffer pointer
+        }//while( buf_ptr <=...
+        return 0;
+}
+
+/* Performs a cleanup after failed Init() attempt */
+uint8_t USBH_MIDI::Release()
+{
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+        bNumEP       = 1;               //must have to be reset to 1
+        bAddress     = 0;
+        bPollEnable  = false;
+        readPtr      = 0;
+        return 0;
+}
+
+/* Setup for well known vendor/device specific configuration */
+void USBH_MIDI::setupDeviceSpecific()
+{
+        // Novation
+        if( vid == 0x1235 ) {
+                // LaunchPad's endpoint attirbute is interrupt (0x20:S, 0x36:Mini, 0x51:Pro, 0x69:MK2, 0x7b:Launchkey25 MK2)
+                if(pid == 0x20 || pid == 0x36 || pid == 0x51 || pid == 0x69 || pid == 0x7b ) {
+                        bTransferTypeMask = 2;
+                }
+        }
+}
+
+/* Receive data from MIDI device */
+uint8_t USBH_MIDI::RecvData(uint16_t *bytes_rcvd, uint8_t *dataptr)
+{
+        *bytes_rcvd = (uint16_t)epInfo[epDataInIndex].maxPktSize;
+        uint8_t  r = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr);
+
+        if( *bytes_rcvd < (MIDI_EVENT_PACKET_SIZE-4)){
+                dataptr[*bytes_rcvd]     = '\0';
+                dataptr[(*bytes_rcvd)+1] = '\0';
+        }
+        return r;
+}
+
+/* Receive data from MIDI device */
+uint8_t USBH_MIDI::RecvData(uint8_t *outBuf, bool isRaw)
+{
+        uint8_t rcode = 0;     //return code
+        uint16_t  rcvd;
+
+        if( bPollEnable == false ) return 0;
+
+        //Checking unprocessed message in buffer.
+        if( readPtr != 0 && readPtr < MIDI_EVENT_PACKET_SIZE ){
+                if(recvBuf[readPtr] == 0 && recvBuf[readPtr+1] == 0) {
+                        //no unprocessed message left in the buffer.
+                }else{
+                        goto RecvData_return_from_buffer;
+                }
+        }
+
+        readPtr = 0;
+        rcode = RecvData( &rcvd, recvBuf);
+        if( rcode != 0 ) {
+                return 0;
+        }
+
+        //if all data is zero, no valid data received.
+        if( recvBuf[0] == 0 && recvBuf[1] == 0 && recvBuf[2] == 0 && recvBuf[3] == 0 ) {
+                return 0;
+        }
+
+RecvData_return_from_buffer:
+        uint8_t m;
+        uint8_t cin = recvBuf[readPtr];
+        if( isRaw == true ) {
+                *(outBuf++) = cin;
+        }
+        readPtr++;
+        *(outBuf++) = m = recvBuf[readPtr++];
+        *(outBuf++) =     recvBuf[readPtr++];
+        *(outBuf++) =     recvBuf[readPtr++];
+        return lookupMsgSize(m, cin);
+}
+
+/* Receive raw data from MIDI device */
+uint8_t USBH_MIDI::RecvRawData(uint8_t *outBuf)
+{
+        return RecvData(outBuf, true);
+}
+
+/* Send data to MIDI device */
+uint8_t USBH_MIDI::SendData(uint8_t *dataptr, uint8_t nCable)
+{
+        uint8_t buf[4];
+        uint8_t msg;
+
+        msg = dataptr[0];
+        // SysEx long message ?
+        if( msg == 0xf0 )
+        {
+                return SendSysEx(dataptr, countSysExDataSize(dataptr), nCable);
+        }
+
+        buf[0] = (nCable << 4) | (msg >> 4);
+        if( msg < 0xf0 ) msg = msg & 0xf0;
+
+
+        //Building USB-MIDI Event Packets
+        buf[1] = dataptr[0];
+        buf[2] = dataptr[1];
+        buf[3] = dataptr[2];
+
+        switch(lookupMsgSize(msg)) {
+          //3 bytes message
+          case 3 :
+                if(msg == 0xf2) {//system common message(SPP)
+                        buf[0] = (nCable << 4) | 3;
+                }
+                break;
+
+          //2 bytes message
+          case 2 :
+                if(msg == 0xf1 || msg == 0xf3) {//system common message(MTC/SongSelect)
+                        buf[0] = (nCable << 4) | 2;
+                }
+                buf[3] = 0;
+                break;
+
+          //1 byte message
+          case 1 :
+          default :
+                buf[2] = 0;
+                buf[3] = 0;
+                break;
+        }
+        return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, 4, buf);
+}
+
+#ifdef DEBUG_USB_HOST
+void USBH_MIDI::PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr )
+{
+        USBTRACE("Endpoint descriptor:\r\n");
+        USBTRACE2(" Length:\t", ep_ptr->bLength);
+        USBTRACE2(" Type:\t\t", ep_ptr->bDescriptorType);
+        USBTRACE2(" Address:\t", ep_ptr->bEndpointAddress);
+        USBTRACE2(" Attributes:\t", ep_ptr->bmAttributes);
+        USBTRACE2(" MaxPktSize:\t", ep_ptr->wMaxPacketSize);
+        USBTRACE2(" Poll Intrv:\t", ep_ptr->bInterval);
+}
+#endif
+
+/* look up a MIDI message size from spec */
+/*Return                                 */
+/*  0 : undefined message                */
+/*  0<: Vaild message size(1-3)          */
+uint8_t USBH_MIDI::lookupMsgSize(uint8_t midiMsg, uint8_t cin)
+{
+        uint8_t msgSize = 0;
+
+        //SysEx message?
+        cin = cin & 0x0f;
+        if( (cin & 0xc) == 4 ) {
+                if( cin == 4 || cin == 7 ) return 3;
+                if( cin == 6 )             return 2;
+                if( cin == 5 )             return 1;
+        }
+
+        if( midiMsg < 0xf0 ) midiMsg &= 0xf0;
+        switch(midiMsg) {
+          //3 bytes messages
+          case 0xf2 : //system common message(SPP)
+          case 0x80 : //Note off
+          case 0x90 : //Note on
+          case 0xa0 : //Poly KeyPress
+          case 0xb0 : //Control Change
+          case 0xe0 : //PitchBend Change
+                msgSize = 3;
+                break;
+
+          //2 bytes messages
+          case 0xf1 : //system common message(MTC)
+          case 0xf3 : //system common message(SongSelect)
+          case 0xc0 : //Program Change
+          case 0xd0 : //Channel Pressure
+                msgSize = 2;
+                break;
+
+          //1 byte messages
+          case 0xf8 : //system realtime message
+          case 0xf9 : //system realtime message
+          case 0xfa : //system realtime message
+          case 0xfb : //system realtime message
+          case 0xfc : //system realtime message
+          case 0xfe : //system realtime message
+          case 0xff : //system realtime message
+                msgSize = 1;
+                break;
+
+          //undefine messages
+          default :
+                break;
+        }
+        return msgSize;
+}
+
+/* SysEx data size counter */
+uint16_t USBH_MIDI::countSysExDataSize(uint8_t *dataptr)
+{
+        uint16_t c = 1;
+
+        if( *dataptr != 0xf0 ){ //not SysEx
+                return 0;
+        }
+
+        //Search terminator(0xf7)
+        while(*dataptr != 0xf7)
+        {
+                dataptr++;
+                c++;
+
+                //Limiter (default: 256 bytes)
+                if(c > MIDI_MAX_SYSEX_SIZE){
+                        c = 0;
+                        break;
+                }
+        }
+        return c;
+}
+
+/* Send SysEx message to MIDI device */
+uint8_t USBH_MIDI::SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable)
+{
+        uint8_t buf[MIDI_EVENT_PACKET_SIZE];
+        uint8_t rc = 0;
+        uint16_t n = datasize;
+        uint16_t pktSize = (n*10/3+7)/10*4;   //Calculate total USB MIDI packet size
+        uint8_t wptr = 0;
+        uint8_t maxpkt = epInfo[epDataInIndex].maxPktSize;
+
+        if( maxpkt > MIDI_EVENT_PACKET_SIZE ) maxpkt = MIDI_EVENT_PACKET_SIZE;
+
+        USBTRACE("SendSysEx:\r\t");
+        USBTRACE2(" Length:\t", datasize);
+        USBTRACE2(" Total pktSize:\t", pktSize);
+
+        while(n > 0) {
+                //Byte 0
+                buf[wptr] = (nCable << 4) | 0x4;             //x4 SysEx starts or continues
+
+                switch ( n ) {
+                    case 1 :
+                        buf[wptr++] = (nCable << 4) | 0x5;   //x5 SysEx ends with following single byte.
+                        buf[wptr++] = *(dataptr++);
+                        buf[wptr++] = 0x00;
+                        buf[wptr++] = 0x00;
+                        n = n - 1;
+                        break;
+                    case 2 :
+                        buf[wptr++] = (nCable << 4) | 0x6;   //x6 SysEx ends with following two bytes.
+                        buf[wptr++] = *(dataptr++);
+                        buf[wptr++] = *(dataptr++);
+                        buf[wptr++] = 0x00;
+                        n = n - 2;
+                        break;
+                    case 3 :
+                        buf[wptr]   = (nCable << 4) | 0x7;   //x7 SysEx ends with following three bytes.
+                    default :
+                        wptr++;
+                        buf[wptr++] = *(dataptr++);
+                        buf[wptr++] = *(dataptr++);
+                        buf[wptr++] = *(dataptr++);
+                        n = n - 3;
+                        break;
+                }
+
+                if( wptr >= maxpkt || n == 0 ){ //Reach a maxPktSize or data end.
+                        USBTRACE2(" wptr:\t", wptr);
+                        if( (rc = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, wptr, buf)) != 0 ){
+                                break;
+                        }
+                        wptr = 0;  //rewind data pointer
+                }
+        }
+        return(rc);
+}
+
+/* Send raw data to MIDI device */
+uint8_t USBH_MIDI::SendRawData(uint16_t bytes_send, uint8_t *dataptr)
+{
+        return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes_send, dataptr);
+
+}
+
+uint8_t USBH_MIDI::extractSysExData(uint8_t *p, uint8_t *buf)
+{
+        uint8_t rc = 0;
+        uint8_t cin = *(p) & 0x0f;
+
+        //SysEx message?
+        if( (cin & 0xc) != 4 ) return rc;
+
+        switch(cin) {
+            case 4:
+            case 7:
+                *buf++ = *(p+1);
+                *buf++ = *(p+2);
+                *buf++ = *(p+3);
+                rc = 3;
+                break;
+            case 6:
+                *buf++ = *(p+1);
+                *buf++ = *(p+2);
+                rc = 2;
+                break;
+            case 5:
+                *buf++ = *(p+1);
+                rc = 1;
+                break;
+            default:
+                break;
+        }
+        return(rc);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usbh_midi.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,93 @@
+/*
+ *******************************************************************************
+ * USB-MIDI class driver for USB Host Shield 2.0 Library
+ * Copyright (c) 2012-2018 Yuuichi Akagawa
+ *
+ * Idea from LPK25 USB-MIDI to Serial MIDI converter
+ *   by Collin Cunningham - makezine.com, narbotic.com
+ *
+ * for use with USB Host Shield 2.0 from Circuitsathome.com
+ * https://github.com/felis/USB_Host_Shield_2.0
+ *******************************************************************************
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *******************************************************************************
+ */
+
+#if !defined(_USBH_MIDI_H_)
+#define _USBH_MIDI_H_
+//#define DEBUG_USB_HOST
+#include "Usb.h"
+
+#define MIDI_MAX_ENDPOINTS 5 //endpoint 0, bulk_IN(MIDI), bulk_OUT(MIDI), bulk_IN(VSP), bulk_OUT(VSP)
+#define USB_SUBCLASS_MIDISTREAMING 3
+#define DESC_BUFF_SIZE        256
+#define MIDI_EVENT_PACKET_SIZE 64
+#define MIDI_MAX_SYSEX_SIZE   256
+class USBH_MIDI;
+
+class USBH_MIDI : public USBDeviceConfig
+{
+protected:
+        static const uint8_t    epDataInIndex;          // DataIn endpoint index(MIDI)
+        static const uint8_t    epDataOutIndex;         // DataOUT endpoint index(MIDI)
+        static const uint8_t    epDataInIndexVSP;       // DataIn endpoint index(Vendor Specific Protocl)
+        static const uint8_t    epDataOutIndexVSP;      // DataOUT endpoint index(Vendor Specific Protocl)
+
+        /* mandatory members */
+        USB      *pUsb;
+        uint8_t  bAddress;
+        uint8_t  bConfNum;    // configuration number
+        uint8_t  bNumEP;      // total number of EP in the configuration
+        bool     bPollEnable;
+        bool     isMidiFound;
+        uint16_t pid, vid;    // ProductID, VendorID
+        uint8_t  bTransferTypeMask;
+        /* Endpoint data structure */
+        EpInfo  epInfo[MIDI_MAX_ENDPOINTS];
+        /* MIDI Event packet buffer */
+        uint8_t recvBuf[MIDI_EVENT_PACKET_SIZE];
+        uint8_t readPtr;
+
+        uint8_t parseConfigDescr(uint8_t addr, uint8_t conf);
+        uint16_t countSysExDataSize(uint8_t *dataptr);
+        void setupDeviceSpecific();
+#ifdef DEBUG_USB_HOST
+        void PrintEndpointDescriptor( const USB_ENDPOINT_DESCRIPTOR* ep_ptr );
+#endif
+public:
+        USBH_MIDI(USB *p);
+        // Misc functions
+        operator bool() { return (pUsb->getUsbTaskState()==USB_STATE_RUNNING); }
+        uint16_t idVendor() { return vid; }
+        uint16_t idProduct() { return pid; }
+        // Methods for recieving and sending data
+        uint8_t RecvData(uint16_t *bytes_rcvd, uint8_t *dataptr);
+        uint8_t RecvData(uint8_t *outBuf, bool isRaw=false);
+        uint8_t RecvRawData(uint8_t *outBuf);
+        uint8_t SendData(uint8_t *dataptr, uint8_t nCable=0);
+        uint8_t lookupMsgSize(uint8_t midiMsg, uint8_t cin=0);
+        uint8_t SendSysEx(uint8_t *dataptr, uint16_t datasize, uint8_t nCable=0);
+        uint8_t extractSysExData(uint8_t *p, uint8_t *buf);
+        uint8_t SendRawData(uint16_t bytes_send, uint8_t *dataptr);
+        // backward compatibility functions
+        inline uint8_t RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) { return RecvData(bytes_rcvd, dataptr); };
+        inline uint8_t RcvData(uint8_t *outBuf) { return RecvData(outBuf); };
+
+        // USBDeviceConfig implementation
+        virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        virtual uint8_t Release();
+        virtual uint8_t GetAddress() { return bAddress; };
+};
+#endif //_USBH_MIDI_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usbhid.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,113 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+#include "usbhid.h"
+
+//get HID report descriptor
+
+/* WRONG! Endpoint is _ALWAYS_ ZERO for HID! We want the _INTERFACE_ value here!
+uint8_t USBHID::GetReportDescr(uint8_t ep, USBReadParser *parser) {
+        const uint8_t constBufLen = 64;
+        uint8_t buf[constBufLen];
+
+        uint8_t rcode = pUsb->ctrlReq(bAddress, ep, bmREQ_HID_REPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00,
+                HID_DESCRIPTOR_REPORT, 0x0000, 128, constBufLen, buf, (USBReadParser*)parser);
+
+        //return ((rcode != hrSTALL) ? rcode : 0);
+        return rcode;
+}
+ */
+uint8_t USBHID::GetReportDescr(uint16_t wIndex, USBReadParser *parser) {
+        const uint8_t constBufLen = 64;
+        uint8_t buf[constBufLen];
+
+        uint8_t rcode = pUsb->ctrlReq(bAddress, 0x00, bmREQ_HID_REPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00,
+                HID_DESCRIPTOR_REPORT, wIndex, 128, constBufLen, buf, (USBReadParser*)parser);
+
+        //return ((rcode != hrSTALL) ? rcode : 0);
+        return rcode;
+}
+
+//uint8_t USBHID::getHidDescr( uint8_t ep, uint16_t nbytes, uint8_t* dataptr )
+//{
+//    return( pUsb->ctrlReq( bAddress, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, HID_DESCRIPTOR_HID, 0x0000, nbytes, dataptr ));
+//}
+
+uint8_t USBHID::SetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr) {
+        return ( pUsb->ctrlReq(bAddress, ep, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, report_id, report_type, iface, nbytes, nbytes, dataptr, NULL));
+}
+
+uint8_t USBHID::GetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr) {
+        return ( pUsb->ctrlReq(bAddress, ep, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, report_id, report_type, iface, nbytes, nbytes, dataptr, NULL));
+}
+
+uint8_t USBHID::GetIdle(uint8_t iface, uint8_t reportID, uint8_t* dataptr) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_IN, HID_REQUEST_GET_IDLE, reportID, 0, iface, 0x0001, 0x0001, dataptr, NULL));
+}
+
+uint8_t USBHID::SetIdle(uint8_t iface, uint8_t reportID, uint8_t duration) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_OUT, HID_REQUEST_SET_IDLE, reportID, duration, iface, 0x0000, 0x0000, NULL, NULL));
+}
+
+uint8_t USBHID::SetProtocol(uint8_t iface, uint8_t protocol) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_OUT, HID_REQUEST_SET_PROTOCOL, protocol, 0x00, iface, 0x0000, 0x0000, NULL, NULL));
+}
+
+uint8_t USBHID::GetProtocol(uint8_t iface, uint8_t* dataptr) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_IN, HID_REQUEST_GET_PROTOCOL, 0x00, 0x00, iface, 0x0001, 0x0001, dataptr, NULL));
+}
+
+void USBHID::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {
+        Notify(PSTR("Endpoint descriptor:"), 0x80);
+        Notify(PSTR("\r\nLength:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);
+        Notify(PSTR("\r\nType:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);
+        Notify(PSTR("\r\nAddress:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);
+        Notify(PSTR("\r\nAttributes:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);
+        Notify(PSTR("\r\nMaxPktSize:\t"), 0x80);
+        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);
+        Notify(PSTR("\r\nPoll Intrv:\t"), 0x80);
+        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);
+}
+
+void USBHID::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) {
+        Notify(PSTR("\r\n\r\nHID Descriptor:\r\n"), 0x80);
+        Notify(PSTR("bDescLength:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (pDesc->bLength, 0x80);
+
+        Notify(PSTR("\r\nbDescriptorType:\t"), 0x80);
+        D_PrintHex<uint8_t > (pDesc->bDescriptorType, 0x80);
+
+        Notify(PSTR("\r\nbcdHID:\t\t\t"), 0x80);
+        D_PrintHex<uint16_t > (pDesc->bcdHID, 0x80);
+
+        Notify(PSTR("\r\nbCountryCode:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (pDesc->bCountryCode, 0x80);
+
+        Notify(PSTR("\r\nbNumDescriptors:\t"), 0x80);
+        D_PrintHex<uint8_t > (pDesc->bNumDescriptors, 0x80);
+
+        Notify(PSTR("\r\nbDescrType:\t\t"), 0x80);
+        D_PrintHex<uint8_t > (pDesc->bDescrType, 0x80);
+
+        Notify(PSTR("\r\nwDescriptorLength:\t"), 0x80);
+        D_PrintHex<uint16_t > (pDesc->wDescriptorLength, 0x80);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usbhid.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,189 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__USBHID_H__)
+#define __USBHID_H__
+
+#include "Usb.h"
+#include "hidusagestr.h"
+
+#define MAX_REPORT_PARSERS                      2
+#define HID_MAX_HID_CLASS_DESCRIPTORS           5
+
+#define DATA_SIZE_MASK                          0x03
+#define TYPE_MASK                               0x0C
+#define TAG_MASK                                0xF0
+
+#define DATA_SIZE_0                             0x00
+#define DATA_SIZE_1                             0x01
+#define DATA_SIZE_2                             0x02
+#define DATA_SIZE_4                             0x03
+
+#define TYPE_MAIN                               0x00
+#define TYPE_GLOBAL                             0x04
+#define TYPE_LOCAL                              0x08
+
+#define TAG_MAIN_INPUT                          0x80
+#define TAG_MAIN_OUTPUT                         0x90
+#define TAG_MAIN_COLLECTION                     0xA0
+#define TAG_MAIN_FEATURE                        0xB0
+#define TAG_MAIN_ENDCOLLECTION                  0xC0
+
+#define TAG_GLOBAL_USAGEPAGE                    0x00
+#define TAG_GLOBAL_LOGICALMIN                   0x10
+#define TAG_GLOBAL_LOGICALMAX                   0x20
+#define TAG_GLOBAL_PHYSMIN                      0x30
+#define TAG_GLOBAL_PHYSMAX                      0x40
+#define TAG_GLOBAL_UNITEXP                      0x50
+#define TAG_GLOBAL_UNIT                         0x60
+#define TAG_GLOBAL_REPORTSIZE                   0x70
+#define TAG_GLOBAL_REPORTID                     0x80
+#define TAG_GLOBAL_REPORTCOUNT                  0x90
+#define TAG_GLOBAL_PUSH                         0xA0
+#define TAG_GLOBAL_POP                          0xB0
+
+#define TAG_LOCAL_USAGE                         0x00
+#define TAG_LOCAL_USAGEMIN                      0x10
+#define TAG_LOCAL_USAGEMAX                      0x20
+
+/* HID requests */
+#define bmREQ_HID_OUT                           USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+#define bmREQ_HID_IN                            USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE
+#define bmREQ_HID_REPORT                        USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_INTERFACE
+
+/* HID constants. Not part of chapter 9 */
+/* Class-Specific Requests */
+#define HID_REQUEST_GET_REPORT                  0x01
+#define HID_REQUEST_GET_IDLE                    0x02
+#define HID_REQUEST_GET_PROTOCOL                0x03
+#define HID_REQUEST_SET_REPORT                  0x09
+#define HID_REQUEST_SET_IDLE                    0x0A
+#define HID_REQUEST_SET_PROTOCOL                0x0B
+
+/* Class Descriptor Types */
+#define HID_DESCRIPTOR_HID                      0x21
+#define HID_DESCRIPTOR_REPORT                   0x22
+#define HID_DESRIPTOR_PHY                       0x23
+
+/* Protocol Selection */
+#define USB_HID_BOOT_PROTOCOL                   0x00
+#define HID_RPT_PROTOCOL                        0x01
+
+/* HID Interface Class Code */
+#define HID_INTF                                0x03
+
+/* HID Interface Class SubClass Codes */
+#define HID_BOOT_INTF_SUBCLASS                  0x01
+
+/* HID Interface Class Protocol Codes */
+#define USB_HID_PROTOCOL_NONE                       0x00
+#define USB_HID_PROTOCOL_KEYBOARD                   0x01
+#define USB_HID_PROTOCOL_MOUSE                      0x02
+
+#define HID_ITEM_TYPE_MAIN                      0
+#define HID_ITEM_TYPE_GLOBAL                    1
+#define HID_ITEM_TYPE_LOCAL                     2
+#define HID_ITEM_TYPE_RESERVED                  3
+
+#define HID_LONG_ITEM_PREFIX                    0xfe    // Long item prefix value
+
+#define bmHID_MAIN_ITEM_TAG                     0xfc    // Main item tag mask
+
+#define bmHID_MAIN_ITEM_INPUT                   0x80    // Main item Input tag value
+#define bmHID_MAIN_ITEM_OUTPUT                  0x90    // Main item Output tag value
+#define bmHID_MAIN_ITEM_FEATURE                 0xb0    // Main item Feature tag value
+#define bmHID_MAIN_ITEM_COLLECTION              0xa0    // Main item Collection tag value
+#define bmHID_MAIN_ITEM_END_COLLECTION          0xce    // Main item End Collection tag value
+
+#define HID_MAIN_ITEM_COLLECTION_PHYSICAL       0
+#define HID_MAIN_ITEM_COLLECTION_APPLICATION    1
+#define HID_MAIN_ITEM_COLLECTION_LOGICAL        2
+#define HID_MAIN_ITEM_COLLECTION_REPORT         3
+#define HID_MAIN_ITEM_COLLECTION_NAMED_ARRAY    4
+#define HID_MAIN_ITEM_COLLECTION_USAGE_SWITCH   5
+#define HID_MAIN_ITEM_COLLECTION_USAGE_MODIFIER 6
+
+struct HidItemPrefix {
+        uint8_t bSize : 2;
+        uint8_t bType : 2;
+        uint8_t bTag : 4;
+};
+
+struct MainItemIOFeature {
+        uint8_t bmIsConstantOrData : 1;
+        uint8_t bmIsArrayOrVariable : 1;
+        uint8_t bmIsRelativeOrAbsolute : 1;
+        uint8_t bmIsWrapOrNoWrap : 1;
+        uint8_t bmIsNonLonearOrLinear : 1;
+        uint8_t bmIsNoPreferedOrPrefered : 1;
+        uint8_t bmIsNullOrNoNull : 1;
+        uint8_t bmIsVolatileOrNonVolatile : 1;
+};
+
+class USBHID;
+
+class HIDReportParser {
+public:
+        virtual void Parse(USBHID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) = 0;
+};
+
+class USBHID : public USBDeviceConfig, public UsbConfigXtracter {
+protected:
+        USB *pUsb; // USB class instance pointer
+        uint8_t bAddress; // address
+
+protected:
+        static const uint8_t epInterruptInIndex = 1; // InterruptIN  endpoint index
+        static const uint8_t epInterruptOutIndex = 2; // InterruptOUT endpoint index
+
+        static const uint8_t maxHidInterfaces = 3;
+        static const uint8_t maxEpPerInterface = 2;
+        static const uint8_t totalEndpoints = (maxHidInterfaces * maxEpPerInterface + 1); // We need to make room for the control endpoint
+
+        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);
+        void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc);
+
+        virtual HIDReportParser* GetReportParser(uint8_t id __attribute__((unused))) {
+                return NULL;
+        };
+
+public:
+
+        USBHID(USB *pusb) : pUsb(pusb) {
+        };
+
+        const USB* GetUsb() {
+                return pUsb;
+        };
+
+        virtual bool SetReportParser(uint8_t id __attribute__((unused)), HIDReportParser *prs __attribute__((unused))) {
+                return false;
+        };
+
+        uint8_t SetProtocol(uint8_t iface, uint8_t protocol);
+        uint8_t GetProtocol(uint8_t iface, uint8_t* dataptr);
+        uint8_t GetIdle(uint8_t iface, uint8_t reportID, uint8_t* dataptr);
+        uint8_t SetIdle(uint8_t iface, uint8_t reportID, uint8_t duration);
+
+        uint8_t GetReportDescr(uint16_t wIndex, USBReadParser *parser = NULL);
+
+        uint8_t GetHidDescr(uint8_t ep, uint16_t nbytes, uint8_t* dataptr);
+        uint8_t GetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr);
+        uint8_t SetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr);
+};
+
+#endif // __USBHID_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usbhost.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,334 @@
+#include "Usb.h"
+#include "usbhost.h"
+
+//#define DEBUGMODE
+#ifdef DEBUGMODE
+#define DEBUG(x, ...) printf("[%s:%d]" x "\n", __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
+#else
+#define DEBUG(...) while (0);
+#endif
+
+
+
+/* constructor */
+MAX3421E::MAX3421E(PinName mosi, PinName miso, PinName sclk, PinName ssel, PinName intr) : SPI(mosi, miso, sclk), _ss(ssel), _intr(intr)
+{
+    _ss = 1;
+    
+    //_spi.mode = SPI_MODE_MASTER;
+    format(8, 0);
+    frequency(26000000);
+}
+
+/* write single byte into MAX3421 register */
+void MAX3421E::regWr(uint8_t reg, uint8_t data)
+{
+    uint8_t c[2];
+
+    _ss = 0;
+
+    c[0] = reg | 0x02;
+    c[1] = data;
+    
+    SPI::write(c[0]);
+    SPI::write(c[1]);
+    
+    //HAL_SPI_Transmit(&SPI_Handle, c, 2, HAL_MAX_DELAY);
+    DEBUG("-              wirte %2x, %2x\n", c[0], c[1]);
+    
+    //DEBUG("w %d, %d\n", c[0], c[1]);
+    
+    _ss = 1;
+}
+
+/* multiple-byte write                            */
+
+/* returns a pointer to memory position after last written */
+uint8_t *MAX3421E::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t *data_p)
+{
+    //uint8_t data = reg | 0x02;
+
+    _ss = 0;
+    
+    uint8_t data = reg | 0x02;
+    
+    SPI::write(data);
+    while (nbytes)
+    {
+        SPI::write(*data_p);
+        nbytes--;
+        data_p++; // advance data pointer
+    }
+
+    _ss = 1;
+    return data_p;
+}
+/* GPIO write */
+/*GPIO byte is split between 2 registers, so two writes are needed to write one byte */
+/* GPOUT bits are in the low nibble. 0-3 in IOPINS1, 4-7 in IOPINS2 */
+
+void MAX3421E::gpioWr(uint8_t data)
+{
+    regWr(rIOPINS1, data);
+    data >>= 4;
+    regWr(rIOPINS2, data);
+    return;
+}
+
+/* single host register read    */
+
+uint8_t MAX3421E::regRd(uint8_t reg)
+{
+    _ss = 0;
+    
+    SPI::write(reg);
+    uint8_t rv = SPI::write(0);
+
+    DEBUG("-              read %2x\n", rv);
+    _ss = 1;
+
+    return rv;
+}
+/* multiple-byte register read  */
+
+/* returns a pointer to a memory position after last read   */
+uint8_t *MAX3421E::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t *data_p)
+{
+    _ss = 0;
+    
+
+    SPI::write(reg);
+    memset(data_p, 0, nbytes);
+    while (nbytes)
+    {
+        *data_p = SPI::write(0);
+        data_p++;
+        nbytes--;
+    }
+    
+    _ss = 1;
+
+    return data_p;
+}
+
+/* GPIO read. See gpioWr for explanation */
+
+/** @brief  Reads the current GPI input values
+*   @retval uint8_t Bitwise value of all 8 GPI inputs
+*/
+/* GPIN pins are in high nibbles of IOPINS1, IOPINS2    */
+
+uint8_t MAX3421E::gpioRd()
+{
+    uint8_t gpin = 0;
+    gpin = regRd(rIOPINS2);         //pins 4-7
+    gpin &= 0xf0;                   //clean lower nibble
+    gpin |= (regRd(rIOPINS1) >> 4); //shift low bits and OR with upper from previous operation.
+    return (gpin);
+}
+
+/** @brief  Reads the current GPI output values
+*   @retval uint8_t Bitwise value of all 8 GPI outputs
+*/
+/* GPOUT pins are in low nibbles of IOPINS1, IOPINS2    */
+
+uint8_t MAX3421E::gpioRdOutput()
+{
+    uint8_t gpout = 0;
+    gpout = regRd(rIOPINS1);         //pins 0-3
+    gpout &= 0x0f;                   //clean upper nibble
+    gpout |= (regRd(rIOPINS2) << 4); //shift high bits and OR with lower from previous operation.
+    return (gpout);
+}
+
+/* reset MAX3421E. Returns number of cycles it took for PLL to stabilize after reset
+  or zero if PLL haven't stabilized in 65535 cycles */
+
+uint16_t MAX3421E::reset()
+{
+    uint16_t i = 0;
+    regWr(rUSBCTL, bmCHIPRES);
+    regWr(rUSBCTL, 0x00);
+    while (++i)
+    {
+        if ((regRd(rUSBIRQ) & bmOSCOKIRQ))
+        {
+            break;
+        }
+    }
+    return (i);
+}
+
+/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */
+
+int8_t MAX3421E::Init()
+{
+    XMEM_ACQUIRE_SPI();
+    // Moved here.
+    // you really should not init hardware in the constructor when it involves locks.
+    // Also avoids the vbus flicker issue confusing some devices.
+    /* pin and peripheral setup */
+
+    _ss = 1;
+
+    XMEM_RELEASE_SPI();
+    /* MAX3421E - full-duplex SPI, level interrupt */
+    // GPX pin on. Moved here, otherwise we flicker the vbus.
+    regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL));
+
+    if (reset() == 0)
+    { //OSCOKIRQ hasn't asserted in time
+        return (-1);
+    }
+
+    regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host
+
+    regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection
+
+    /* check if device is connected */
+    regWr(rHCTL, bmSAMPLEBUS); // sample USB bus
+    while (!(regRd(rHCTL) & bmSAMPLEBUS))
+        ; //wait for sample operation to finish
+
+    busprobe(); //check if anything is connected
+
+    regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt
+    regWr(rCPUCTL, 0x01);      //enable interrupt pin
+
+    return (0);
+}
+
+/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */
+
+int8_t MAX3421E::Init(int mseconds)
+{
+    XMEM_ACQUIRE_SPI();
+    // Moved here.
+    // you really should not init hardware in the constructor when it involves locks.
+    // Also avoids the vbus flicker issue confusing some devices.
+    /* pin and peripheral setup */
+
+    _ss = 1;
+
+    XMEM_RELEASE_SPI();
+    /* MAX3421E - full-duplex SPI, level interrupt, vbus off */
+    regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | GPX_VBDET));
+
+    if (reset() == 0)
+    { //OSCOKIRQ hasn't asserted in time
+        return (-1);
+    }
+
+    // Delay a minimum of 1 second to ensure any capacitors are drained.
+    // 1 second is required to make sure we do not smoke a Microdrive!
+    if (mseconds < 1000)
+        mseconds = 1000;
+    delay(mseconds);
+
+    regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host
+
+    regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection
+
+    /* check if device is connected */
+    regWr(rHCTL, bmSAMPLEBUS); // sample USB bus
+    while (!(regRd(rHCTL) & bmSAMPLEBUS))
+        ; //wait for sample operation to finish
+
+    busprobe(); //check if anything is connected
+
+    regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt
+    regWr(rCPUCTL, 0x01);      //enable interrupt pin
+
+    // GPX pin on. This is done here so that busprobe will fail if we have a switch connected.
+    regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL));
+
+    return (0);
+}
+
+/* probe bus to determine device presence and speed and switch host to this speed */
+
+void MAX3421E::busprobe()
+{
+    uint8_t bus_sample;
+    DEBUG("busprobe()\n");
+    bus_sample = regRd(rHRSL);             //Get J,K status
+    bus_sample &= (bmJSTATUS | bmKSTATUS); //zero the rest of the byte
+    switch (bus_sample)
+    { //start full-speed or low-speed host
+    case (bmJSTATUS):
+        if ((regRd(rMODE) & bmLOWSPEED) == 0)
+        {
+            regWr(rMODE, MODE_FS_HOST); //start full-speed host
+            vbusState = FSHOST;
+        }
+        else
+        {
+            regWr(rMODE, MODE_LS_HOST); //start low-speed host
+            vbusState = LSHOST;
+        }
+        break;
+    case (bmKSTATUS):
+        if ((regRd(rMODE) & bmLOWSPEED) == 0)
+        {
+            regWr(rMODE, MODE_LS_HOST); //start low-speed host
+            vbusState = LSHOST;
+        }
+        else
+        {
+            regWr(rMODE, MODE_FS_HOST); //start full-speed host
+            vbusState = FSHOST;
+        }
+        break;
+    case (bmSE1): //illegal state
+        vbusState = SE1;
+        break;
+    case (bmSE0): //disconnected state
+        regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ);
+        vbusState = SE0;
+        break;
+    } //end switch( bus_sample )
+}
+
+/* MAX3421 state change task and interrupt handler */
+
+uint8_t MAX3421E::Task(void)
+{
+    uint8_t rcode = 0;
+    uint8_t pinvalue;
+    //USB_HOST_SERIAL.print("Vbus state: ");
+    //USB_HOST_SERIAL.println( vbusState, HEX );
+
+    pinvalue = _intr.read();
+
+    //pinvalue = digitalRead( MAX_INT );
+    if (pinvalue == 0)
+    {
+        rcode = IntHandler();
+    }
+    //    pinvalue = digitalRead( MAX_GPX );
+    //    if( pinvalue == LOW ) {
+    //        GpxHandler();
+    //    }
+    //    usbSM();                                //USB state machine
+    return (rcode);
+}
+
+uint8_t MAX3421E::IntHandler()
+{
+    uint8_t HIRQ;
+    uint8_t HIRQ_sendback = 0x00;
+
+    DEBUG("IntHandler\n");
+    HIRQ = regRd(rHIRQ); //determine interrupt source
+    //if( HIRQ & bmFRAMEIRQ ) {               //->1ms SOF interrupt handler
+    //    HIRQ_sendback |= bmFRAMEIRQ;
+    //}//end FRAMEIRQ handling
+    if (HIRQ & bmCONDETIRQ)
+    {
+        busprobe();
+        HIRQ_sendback |= bmCONDETIRQ;
+    }
+    // End HIRQ interrupts handling, clear serviced IRQs
+    regWr(rHIRQ, HIRQ_sendback);
+    return (HIRQ_sendback);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usbhost.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,97 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+/* MAX3421E-based USB Host Library header file */
+
+//warning
+//#define _usb_h_
+//#define MBED_H
+
+#ifndef usbhost_h
+#define usbhost_h
+
+//#define DEBUGMODE
+#ifdef DEBUGMODE
+#define DEBUG(x, ...) printf("[%s:%d]" x "\n", __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
+#else
+#define DEBUG(...) while (0);
+#endif
+
+#if !defined(_usb_h_) // || defined(_USBHOST_H_)
+#error "Never include usbhost.h directly; include Usb.h instead"
+#else
+#define _USBHOST_H_
+
+#if USING_SPI4TEENSY3
+#include <spi4teensy3.h>
+#include <sys/types.h>
+#endif
+
+typedef enum
+{
+    vbus_on = 0,
+    vbus_off = GPX_VBDET
+} VBUS_t;
+
+class MAX3421E : public SPI
+{
+    //static
+    uint8_t vbusState;
+
+public:
+    MAX3421E(PinName mosi, PinName miso, PinName sclk, PinName ssel, PinName intr);
+    void regWr(uint8_t reg, uint8_t data);
+    uint8_t *bytesWr(uint8_t reg, uint8_t nbytes, uint8_t *data_p);
+    void gpioWr(uint8_t data);
+    uint8_t regRd(uint8_t reg);
+    uint8_t *bytesRd(uint8_t reg, uint8_t nbytes, uint8_t *data_p);
+    uint8_t gpioRd();
+    uint8_t gpioRdOutput();
+    uint16_t reset();
+    int8_t Init();
+    int8_t Init(int mseconds);
+
+    void vbusPower(VBUS_t state)
+    {
+        regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | state));
+    }
+
+    uint8_t getVbusState(void)
+    {
+        return vbusState;
+    };
+    void busprobe();
+    uint8_t GpxHandler();
+    uint8_t IntHandler();
+    uint8_t Task();
+
+
+private:
+    DigitalOut _ss;
+    DigitalIn _intr;
+
+};
+
+
+#endif // _USBHOST_H_
+#endif //usbhost_h
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usbhub.cpp	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,428 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#include "usbhub.h"
+
+bool USBHub::bResetInitiated = false;
+
+USBHub::USBHub(USB *p) :
+pUsb(p),
+bAddress(0),
+bNbrPorts(0),
+//bInitState(0),
+qNextPollTime(0),
+bPollEnable(false) {
+        epInfo[0].epAddr = 0;
+        epInfo[0].maxPktSize = 8;
+        epInfo[0].bmSndToggle = 0;
+        epInfo[0].bmRcvToggle = 0;
+        epInfo[0].bmNakPower = USB_NAK_MAX_POWER;
+
+        epInfo[1].epAddr = 1;
+        epInfo[1].maxPktSize = 8; //kludge
+        epInfo[1].bmSndToggle = 0;
+        epInfo[1].bmRcvToggle = 0;
+        epInfo[1].bmNakPower = USB_NAK_NOWAIT;
+
+        if(pUsb)
+                pUsb->RegisterDeviceClass(this);
+}
+
+uint8_t USBHub::Init(uint8_t parent, uint8_t port, bool lowspeed) {
+        uint8_t buf[32];
+        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);
+        HubDescriptor* hd = reinterpret_cast<HubDescriptor*>(buf);
+        USB_CONFIGURATION_DESCRIPTOR * ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR*>(buf);
+        uint8_t rcode;
+        UsbDevice *p = NULL;
+        EpInfo *oldep_ptr = NULL;
+        uint8_t len = 0;
+        uint16_t cd_len = 0;
+
+        //USBTRACE("\r\nHub Init Start ");
+        //D_PrintHex<uint8_t > (bInitState, 0x80);
+
+        AddressPool &addrPool = pUsb->GetAddressPool();
+
+        //switch (bInitState) {
+        //        case 0:
+        if(bAddress)
+                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;
+
+        // Get pointer to pseudo device with address 0 assigned
+        p = addrPool.GetUsbDevicePtr(0);
+
+        if(!p)
+                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;
+
+        if(!p->epinfo)
+                return USB_ERROR_EPINFO_IS_NULL;
+
+        // Save old pointer to EP_RECORD of address 0
+        oldep_ptr = p->epinfo;
+
+        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence
+        p->epinfo = epInfo;
+
+        p->lowspeed = lowspeed;
+
+        // Get device descriptor
+        rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf);
+
+        p->lowspeed = false;
+
+        if(!rcode)
+                len = (buf[0] > 32) ? 32 : buf[0];
+
+        if(rcode) {
+                // Restore p->epinfo
+                p->epinfo = oldep_ptr;
+                return rcode;
+        }
+
+        // Extract device class from device descriptor
+        // If device class is not a hub return
+        if(udd->bDeviceClass != 0x09)
+                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;
+
+        // Allocate new address according to device class
+        bAddress = addrPool.AllocAddress(parent, (udd->bDeviceClass == 0x09) ? true : false, port);
+
+        if(!bAddress)
+                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;
+
+        // Extract Max Packet Size from the device descriptor
+        epInfo[0].maxPktSize = udd->bMaxPacketSize0;
+
+        // Assign new address to the device
+        rcode = pUsb->setAddr(0, 0, bAddress);
+
+        if(rcode) {
+                // Restore p->epinfo
+                p->epinfo = oldep_ptr;
+                addrPool.FreeAddress(bAddress);
+                bAddress = 0;
+                return rcode;
+        }
+
+        //USBTRACE2("\r\nHub address: ", bAddress );
+
+        // Restore p->epinfo
+        p->epinfo = oldep_ptr;
+
+        if(len)
+                rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf);
+
+        if(rcode)
+                goto FailGetDevDescr;
+
+        // Assign epInfo to epinfo pointer
+        rcode = pUsb->setEpInfoEntry(bAddress, 2, epInfo);
+
+        if(rcode)
+                goto FailSetDevTblEntry;
+
+        //                bInitState = 1;
+
+        //        case 1:
+        // Get hub descriptor
+        rcode = GetHubDescriptor(0, 8, buf);
+
+        if(rcode)
+                goto FailGetHubDescr;
+
+        // Save number of ports for future use
+        bNbrPorts = hd->bNbrPorts;
+
+        //                bInitState = 2;
+
+        //        case 2:
+        // Read configuration Descriptor in Order To Obtain Proper Configuration Value
+        rcode = pUsb->getConfDescr(bAddress, 0, 8, 0, buf);
+
+        if(!rcode) {
+                cd_len = ucd->wTotalLength;
+                rcode = pUsb->getConfDescr(bAddress, 0, cd_len, 0, buf);
+        }
+        if(rcode)
+                goto FailGetConfDescr;
+
+        // The following code is of no practical use in real life applications.
+        // It only intended for the usb protocol sniffer to properly parse hub-class requests.
+        {
+                uint8_t buf2[24];
+
+                rcode = pUsb->getConfDescr(bAddress, 0, buf[0], 0, buf2);
+
+                if(rcode)
+                        goto FailGetConfDescr;
+        }
+
+        // Set Configuration Value
+        rcode = pUsb->setConf(bAddress, 0, buf[5]);
+
+        if(rcode)
+                goto FailSetConfDescr;
+
+        //                bInitState = 3;
+
+        //        case 3:
+        // Power on all ports
+        for(uint8_t j = 1; j <= bNbrPorts; j++)
+                SetPortFeature(HUB_FEATURE_PORT_POWER, j, 0); //HubPortPowerOn(j);
+
+        pUsb->SetHubPreMask();
+        bPollEnable = true;
+        //                bInitState = 0;
+        //}
+        //bInitState = 0;
+        //USBTRACE("...OK\r\n");
+        return 0;
+
+        // Oleg, No debugging?? -- xxxajk
+FailGetDevDescr:
+        goto Fail;
+
+FailSetDevTblEntry:
+        goto Fail;
+
+FailGetHubDescr:
+        goto Fail;
+
+FailGetConfDescr:
+        goto Fail;
+
+FailSetConfDescr:
+        goto Fail;
+
+Fail:
+        USBTRACE("...FAIL\r\n");
+        return rcode;
+}
+
+uint8_t USBHub::Release() {
+        pUsb->GetAddressPool().FreeAddress(bAddress);
+
+        if(bAddress == 0x41)
+                pUsb->SetHubPreMask();
+
+        bAddress = 0;
+        bNbrPorts = 0;
+        qNextPollTime = 0;
+        bPollEnable = false;
+        return 0;
+}
+
+uint8_t USBHub::Poll() {
+        uint8_t rcode = 0;
+
+        if(!bPollEnable)
+                return 0;
+
+        if(((int32_t)((uint32_t)millis() - qNextPollTime) >= 0L)) {
+                rcode = CheckHubStatus();
+                qNextPollTime = (uint32_t)millis() + 100;
+        }
+        return rcode;
+}
+
+uint8_t USBHub::CheckHubStatus() {
+        uint8_t rcode;
+        uint8_t buf[8];
+        uint16_t read = 1;
+
+        rcode = pUsb->inTransfer(bAddress, 1, &read, buf);
+
+        if(rcode)
+                return rcode;
+
+        //if (buf[0] & 0x01) // Hub Status Change
+        //{
+        //        pUsb->PrintHubStatus(addr);
+        //        rcode = GetHubStatus(1, 0, 1, 4, buf);
+        //        if (rcode)
+        //        {
+        //                ////USB_HOST_SERIAL.print("GetHubStatus Error");
+        //                ////USB_HOST_SERIAL.println(rcode, HEX);
+        //                return rcode;
+        //        }
+        //}
+        for(uint8_t port = 1, mask = 0x02; port < 8; mask <<= 1, port++) {
+                if(buf[0] & mask) {
+                        HubEvent evt;
+                        evt.bmEvent = 0;
+
+                        rcode = GetPortStatus(port, 4, evt.evtBuff);
+
+                        if(rcode)
+                                continue;
+
+                        rcode = PortStatusChange(port, evt);
+
+                        if(rcode == HUB_ERROR_PORT_HAS_BEEN_RESET)
+                                return 0;
+
+                        if(rcode)
+                                return rcode;
+                }
+        } // for
+
+        for(uint8_t port = 1; port <= bNbrPorts; port++) {
+                HubEvent evt;
+                evt.bmEvent = 0;
+
+                rcode = GetPortStatus(port, 4, evt.evtBuff);
+
+                if(rcode)
+                        continue;
+
+                if((evt.bmStatus & bmHUB_PORT_STATE_CHECK_DISABLED) != bmHUB_PORT_STATE_DISABLED)
+                        continue;
+
+                // Emulate connection event for the port
+                evt.bmChange |= bmHUB_PORT_STATUS_C_PORT_CONNECTION;
+
+                rcode = PortStatusChange(port, evt);
+
+                if(rcode == HUB_ERROR_PORT_HAS_BEEN_RESET)
+                        return 0;
+
+                if(rcode)
+                        return rcode;
+        } // for
+        return 0;
+}
+
+void USBHub::ResetHubPort(uint8_t port) {
+        HubEvent evt;
+        evt.bmEvent = 0;
+        uint8_t rcode;
+
+        ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0);
+        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);
+        SetPortFeature(HUB_FEATURE_PORT_RESET, port, 0);
+
+
+        for(int i = 0; i < 3; i++) {
+                rcode = GetPortStatus(port, 4, evt.evtBuff);
+                if(rcode) break; // Some kind of error, bail.
+                if(evt.bmEvent == bmHUB_PORT_EVENT_RESET_COMPLETE || evt.bmEvent == bmHUB_PORT_EVENT_LS_RESET_COMPLETE) {
+                        break;
+                }
+                delay(100); // simulate polling.
+        }
+        ClearPortFeature(HUB_FEATURE_C_PORT_RESET, port, 0);
+        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);
+        delay(20);
+}
+
+uint8_t USBHub::PortStatusChange(uint8_t port, HubEvent &evt) {
+        switch(evt.bmEvent) {
+                        // Device connected event
+                case bmHUB_PORT_EVENT_CONNECT:
+                case bmHUB_PORT_EVENT_LS_CONNECT:
+                        if(bResetInitiated)
+                                return 0;
+
+                        ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0);
+                        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);
+                        SetPortFeature(HUB_FEATURE_PORT_RESET, port, 0);
+                        bResetInitiated = true;
+                        return HUB_ERROR_PORT_HAS_BEEN_RESET;
+
+                        // Device disconnected event
+                case bmHUB_PORT_EVENT_DISCONNECT:
+                        ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0);
+                        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);
+                        bResetInitiated = false;
+
+                        UsbDeviceAddress a;
+                        a.devAddress = 0;
+                        a.bmHub = 0;
+                        a.bmParent = bAddress;
+                        a.bmAddress = port;
+                        pUsb->ReleaseDevice(a.devAddress);
+                        return 0;
+
+                        // Reset complete event
+                case bmHUB_PORT_EVENT_RESET_COMPLETE:
+                case bmHUB_PORT_EVENT_LS_RESET_COMPLETE:
+                        ClearPortFeature(HUB_FEATURE_C_PORT_RESET, port, 0);
+                        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);
+
+                        delay(20);
+
+                        a.devAddress = bAddress;
+
+                        pUsb->Configuring(a.bmAddress, port, (evt.bmStatus & bmHUB_PORT_STATUS_PORT_LOW_SPEED));
+                        bResetInitiated = false;
+                        break;
+
+        } // switch (evt.bmEvent)
+        return 0;
+}
+
+void PrintHubPortStatus(USBHub *hubptr, uint8_t addr __attribute__((unused)), uint8_t port, bool print_changes) {
+        uint8_t rcode = 0;
+        HubEvent evt;
+
+        rcode = hubptr->GetPortStatus(port, 4, evt.evtBuff);
+
+        if(rcode) {
+                ////////USB_HOST_SERIAL.println("ERROR!");
+                return;
+        }
+        //USB_HOST_SERIAL.print("\r\nPort ");
+        //USB_HOST_SERIAL.println(port, DEC);
+
+        //USB_HOST_SERIAL.println("Status");
+        //USB_HOST_SERIAL.print("CONNECTION:\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_CONNECTION) > 0, DEC);
+        //USB_HOST_SERIAL.print("ENABLE:\t\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_ENABLE) > 0, DEC);
+        //USB_HOST_SERIAL.print("SUSPEND:\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_SUSPEND) > 0, DEC);
+        //USB_HOST_SERIAL.print("OVER_CURRENT:\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_OVER_CURRENT) > 0, DEC);
+        //USB_HOST_SERIAL.print("RESET:\t\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_RESET) > 0, DEC);
+        //USB_HOST_SERIAL.print("POWER:\t\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_POWER) > 0, DEC);
+        //USB_HOST_SERIAL.print("LOW_SPEED:\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_LOW_SPEED) > 0, DEC);
+        //USB_HOST_SERIAL.print("HIGH_SPEED:\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_HIGH_SPEED) > 0, DEC);
+        //USB_HOST_SERIAL.print("TEST:\t\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_TEST) > 0, DEC);
+        //USB_HOST_SERIAL.print("INDICATOR:\t");
+        //USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_INDICATOR) > 0, DEC);
+
+        if(!print_changes)
+                return;
+
+        //USB_HOST_SERIAL.println("\r\nChange");
+        //USB_HOST_SERIAL.print("CONNECTION:\t");
+        //USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_CONNECTION) > 0, DEC);
+        //USB_HOST_SERIAL.print("ENABLE:\t\t");
+        //USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_ENABLE) > 0, DEC);
+        //USB_HOST_SERIAL.print("SUSPEND:\t");
+        //USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_SUSPEND) > 0, DEC);
+        //USB_HOST_SERIAL.print("OVER_CURRENT:\t");
+        //USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_OVER_CURRENT) > 0, DEC);
+        //USB_HOST_SERIAL.print("RESET:\t\t");
+        //USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_RESET) > 0, DEC);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/usbhub.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,253 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This software may be distributed and modified under the terms of the GNU
+General Public License version 2 (GPL2) as published by the Free Software
+Foundation and appearing in the file GPL2.TXT included in the packaging of
+this file. Please note that GPL2 Section 2[b] requires that all works based
+on this software must also be made publicly available under the terms of
+the GPL2 ("Copyleft").
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+#if !defined(__USBHUB_H__)
+#define __USBHUB_H__
+
+#include "Usb.h"
+
+#define USB_DESCRIPTOR_HUB                      0x09 // Hub descriptor type
+
+// Hub Requests
+#define bmREQ_CLEAR_HUB_FEATURE                 USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
+#define bmREQ_CLEAR_PORT_FEATURE                USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
+#define bmREQ_CLEAR_TT_BUFFER                   USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
+#define bmREQ_GET_HUB_DESCRIPTOR                USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
+#define bmREQ_GET_HUB_STATUS                    USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
+#define bmREQ_GET_PORT_STATUS                   USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
+#define bmREQ_RESET_TT                          USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
+#define bmREQ_SET_HUB_DESCRIPTOR                USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
+#define bmREQ_SET_HUB_FEATURE                   USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE
+#define bmREQ_SET_PORT_FEATURE                  USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
+#define bmREQ_GET_TT_STATE                      USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
+#define bmREQ_STOP_TT                           USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER
+
+// Hub Class Requests
+#define HUB_REQUEST_CLEAR_TT_BUFFER             8
+#define HUB_REQUEST_RESET_TT                    9
+#define HUB_REQUEST_GET_TT_STATE                10
+#define HUB_REQUEST_STOP_TT                     11
+
+// Hub Features
+#define HUB_FEATURE_C_HUB_LOCAL_POWER           0
+#define HUB_FEATURE_C_HUB_OVER_CURRENT          1
+#define HUB_FEATURE_PORT_CONNECTION             0
+#define HUB_FEATURE_PORT_ENABLE                 1
+#define HUB_FEATURE_PORT_SUSPEND                2
+#define HUB_FEATURE_PORT_OVER_CURRENT           3
+#define HUB_FEATURE_PORT_RESET                  4
+#define HUB_FEATURE_PORT_POWER                  8
+#define HUB_FEATURE_PORT_LOW_SPEED              9
+#define HUB_FEATURE_C_PORT_CONNECTION           16
+#define HUB_FEATURE_C_PORT_ENABLE               17
+#define HUB_FEATURE_C_PORT_SUSPEND              18
+#define HUB_FEATURE_C_PORT_OVER_CURRENT         19
+#define HUB_FEATURE_C_PORT_RESET                20
+#define HUB_FEATURE_PORT_TEST                   21
+#define HUB_FEATURE_PORT_INDICATOR              22
+
+// Hub Port Test Modes
+#define HUB_PORT_TEST_MODE_J                    1
+#define HUB_PORT_TEST_MODE_K                    2
+#define HUB_PORT_TEST_MODE_SE0_NAK              3
+#define HUB_PORT_TEST_MODE_PACKET               4
+#define HUB_PORT_TEST_MODE_FORCE_ENABLE         5
+
+// Hub Port Indicator Color
+#define HUB_PORT_INDICATOR_AUTO                 0
+#define HUB_PORT_INDICATOR_AMBER                1
+#define HUB_PORT_INDICATOR_GREEN                2
+#define HUB_PORT_INDICATOR_OFF                  3
+
+// Hub Port Status Bitmasks
+#define bmHUB_PORT_STATUS_PORT_CONNECTION       0x0001
+#define bmHUB_PORT_STATUS_PORT_ENABLE           0x0002
+#define bmHUB_PORT_STATUS_PORT_SUSPEND          0x0004
+#define bmHUB_PORT_STATUS_PORT_OVER_CURRENT     0x0008
+#define bmHUB_PORT_STATUS_PORT_RESET            0x0010
+#define bmHUB_PORT_STATUS_PORT_POWER            0x0100
+#define bmHUB_PORT_STATUS_PORT_LOW_SPEED        0x0200
+#define bmHUB_PORT_STATUS_PORT_HIGH_SPEED       0x0400
+#define bmHUB_PORT_STATUS_PORT_TEST             0x0800
+#define bmHUB_PORT_STATUS_PORT_INDICATOR        0x1000
+
+// Hub Port Status Change Bitmasks (used one byte instead of two)
+#define bmHUB_PORT_STATUS_C_PORT_CONNECTION     0x0001
+#define bmHUB_PORT_STATUS_C_PORT_ENABLE         0x0002
+#define bmHUB_PORT_STATUS_C_PORT_SUSPEND        0x0004
+#define bmHUB_PORT_STATUS_C_PORT_OVER_CURRENT   0x0008
+#define bmHUB_PORT_STATUS_C_PORT_RESET          0x0010
+
+// Hub Status Bitmasks (used one byte instead of two)
+#define bmHUB_STATUS_LOCAL_POWER_SOURCE         0x01
+#define bmHUB_STATUS_OVER_CURRENT               0x12
+
+// Hub Status Change Bitmasks (used one byte instead of two)
+#define bmHUB_STATUS_C_LOCAL_POWER_SOURCE       0x01
+#define bmHUB_STATUS_C_OVER_CURRENT             0x12
+
+
+// Hub Port Configuring Substates
+#define USB_STATE_HUB_PORT_CONFIGURING          0xb0
+#define USB_STATE_HUB_PORT_POWERED_OFF          0xb1
+#define USB_STATE_HUB_PORT_WAIT_FOR_POWER_GOOD  0xb2
+#define USB_STATE_HUB_PORT_DISCONNECTED         0xb3
+#define USB_STATE_HUB_PORT_DISABLED             0xb4
+#define USB_STATE_HUB_PORT_RESETTING            0xb5
+#define USB_STATE_HUB_PORT_ENABLED              0xb6
+
+// Additional Error Codes
+#define HUB_ERROR_PORT_HAS_BEEN_RESET           0xb1
+
+// The bit mask to check for all necessary state bits
+#define bmHUB_PORT_STATUS_ALL_MAIN              ((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION | bmHUB_PORT_STATUS_C_PORT_ENABLE | bmHUB_PORT_STATUS_C_PORT_SUSPEND | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_SUSPEND)
+
+// Bit mask to check for DISABLED state in HubEvent::bmStatus field
+#define bmHUB_PORT_STATE_CHECK_DISABLED         (0x0000 | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_SUSPEND)
+
+// Hub Port States
+#define bmHUB_PORT_STATE_DISABLED               (0x0000 | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION)
+
+// Hub Port Events
+#define bmHUB_PORT_EVENT_CONNECT                (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION)
+#define bmHUB_PORT_EVENT_DISCONNECT             (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER)
+#define bmHUB_PORT_EVENT_RESET_COMPLETE         (((0UL | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION)
+
+#define bmHUB_PORT_EVENT_LS_CONNECT             (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED)
+#define bmHUB_PORT_EVENT_LS_RESET_COMPLETE      (((0UL | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED)
+#define bmHUB_PORT_EVENT_LS_PORT_ENABLED        (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION | bmHUB_PORT_STATUS_C_PORT_ENABLE) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED)
+
+struct HubDescriptor {
+        uint8_t bDescLength; // descriptor length
+        uint8_t bDescriptorType; // descriptor type
+        uint8_t bNbrPorts; // number of ports a hub equiped with
+
+        struct {
+                uint16_t LogPwrSwitchMode : 2;
+                uint16_t CompoundDevice : 1;
+                uint16_t OverCurrentProtectMode : 2;
+                uint16_t TTThinkTime : 2;
+                uint16_t PortIndicatorsSupported : 1;
+                uint16_t Reserved : 8;
+        } __attribute__((packed));
+
+        uint8_t bPwrOn2PwrGood;
+        uint8_t bHubContrCurrent;
+} __attribute__((packed));
+
+struct HubEvent {
+
+        union {
+
+                struct {
+                        uint16_t bmStatus; // port status bits
+                        uint16_t bmChange; // port status change bits
+                } __attribute__((packed));
+                uint32_t bmEvent;
+                uint8_t evtBuff[4];
+        };
+} __attribute__((packed));
+
+class USBHub : USBDeviceConfig {
+        static bool bResetInitiated; // True when reset is triggered
+
+        USB *pUsb; // USB class instance pointer
+
+        EpInfo epInfo[2]; // interrupt endpoint info structure
+
+        uint8_t bAddress; // address
+        uint8_t bNbrPorts; // number of ports
+        //        uint8_t bInitState; // initialization state variable
+        uint32_t qNextPollTime; // next poll time
+        bool bPollEnable; // poll enable flag
+
+        uint8_t CheckHubStatus();
+        uint8_t PortStatusChange(uint8_t port, HubEvent &evt);
+
+public:
+        USBHub(USB *p);
+
+        uint8_t ClearHubFeature(uint8_t fid);
+        uint8_t ClearPortFeature(uint8_t fid, uint8_t port, uint8_t sel = 0);
+        uint8_t GetHubDescriptor(uint8_t index, uint16_t nbytes, uint8_t *dataptr);
+        uint8_t GetHubStatus(uint16_t nbytes, uint8_t* dataptr);
+        uint8_t GetPortStatus(uint8_t port, uint16_t nbytes, uint8_t* dataptr);
+        uint8_t SetHubDescriptor(uint8_t port, uint16_t nbytes, uint8_t* dataptr);
+        uint8_t SetHubFeature(uint8_t fid);
+        uint8_t SetPortFeature(uint8_t fid, uint8_t port, uint8_t sel = 0);
+
+        void PrintHubStatus();
+
+        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);
+        uint8_t Release();
+        uint8_t Poll();
+        void ResetHubPort(uint8_t port);
+
+        virtual uint8_t GetAddress() {
+                return bAddress;
+        };
+
+        virtual bool DEVCLASSOK(uint8_t klass) {
+                return (klass == 0x09);
+        }
+
+};
+
+// Clear Hub Feature
+
+inline uint8_t USBHub::ClearHubFeature(uint8_t fid) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CLEAR_HUB_FEATURE, USB_REQUEST_CLEAR_FEATURE, fid, 0, 0, 0, 0, NULL, NULL));
+}
+// Clear Port Feature
+
+inline uint8_t USBHub::ClearPortFeature(uint8_t fid, uint8_t port, uint8_t sel) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CLEAR_PORT_FEATURE, USB_REQUEST_CLEAR_FEATURE, fid, 0, ((0x0000 | port) | (sel << 8)), 0, 0, NULL, NULL));
+}
+// Get Hub Descriptor
+
+inline uint8_t USBHub::GetHubDescriptor(uint8_t index, uint16_t nbytes, uint8_t *dataptr) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_HUB_DESCRIPTOR, USB_REQUEST_GET_DESCRIPTOR, index, 0x29, 0, nbytes, nbytes, dataptr, NULL));
+}
+// Get Hub Status
+
+inline uint8_t USBHub::GetHubStatus(uint16_t nbytes, uint8_t* dataptr) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_HUB_STATUS, USB_REQUEST_GET_STATUS, 0, 0, 0x0000, nbytes, nbytes, dataptr, NULL));
+}
+// Get Port Status
+
+inline uint8_t USBHub::GetPortStatus(uint8_t port, uint16_t nbytes, uint8_t* dataptr) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_PORT_STATUS, USB_REQUEST_GET_STATUS, 0, 0, port, nbytes, nbytes, dataptr, NULL));
+}
+// Set Hub Descriptor
+
+inline uint8_t USBHub::SetHubDescriptor(uint8_t port, uint16_t nbytes, uint8_t* dataptr) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_HUB_DESCRIPTOR, USB_REQUEST_SET_DESCRIPTOR, 0, 0, port, nbytes, nbytes, dataptr, NULL));
+}
+// Set Hub Feature
+
+inline uint8_t USBHub::SetHubFeature(uint8_t fid) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_HUB_FEATURE, USB_REQUEST_SET_FEATURE, fid, 0, 0, 0, 0, NULL, NULL));
+}
+// Set Port Feature
+
+inline uint8_t USBHub::SetPortFeature(uint8_t fid, uint8_t port, uint8_t sel) {
+        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_PORT_FEATURE, USB_REQUEST_SET_FEATURE, fid, 0, (((0x0000 | sel) << 8) | port), 0, 0, NULL, NULL));
+}
+
+void PrintHubPortStatus(USB *usbptr, uint8_t addr, uint8_t port, bool print_changes = false);
+
+#endif // __USBHUB_H__
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/version_helper.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,203 @@
+/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Contact information
+-------------------
+
+Circuits At Home, LTD
+Web      :  http://www.circuitsathome.com
+e-mail   :  support@circuitsathome.com
+ */
+
+/*
+ * Universal Arduino(tm) "IDE" fixups.
+ * Includes fixes for versions as low as 0023, used by Digilent.
+ */
+
+#define ARDUINO 100
+#if defined(ARDUINO) && ARDUINO >=100
+#include <Arduino.h>
+#else
+#include <WProgram.h>
+#include <pins_arduino.h>
+#ifdef __AVR__
+#include <avr/pgmspace.h>
+#include <avr/io.h>
+#else
+#endif
+#endif
+
+#ifndef __PGMSPACE_H_
+#define __PGMSPACE_H_ 1
+
+#include <inttypes.h>
+
+#ifndef PROGMEM
+#define PROGMEM
+#endif
+#ifndef PGM_P
+#define PGM_P  const char *
+#endif
+#ifndef PSTR
+#define PSTR(str) (str)
+#endif
+#ifndef F
+#define F(str) (str)
+#endif
+#ifndef _SFR_BYTE
+#define _SFR_BYTE(n) (n)
+#endif
+
+#ifndef memchr_P
+#define memchr_P(str, c, len) memchr((str), (c), (len))
+#endif
+#ifndef memcmp_P
+#define memcmp_P(a, b, n) memcmp((a), (b), (n))
+#endif
+#ifndef memcpy_P
+#define memcpy_P(dest, src, num) memcpy((dest), (src), (num))
+#endif
+#ifndef memmem_P
+#define memmem_P(a, alen, b, blen) memmem((a), (alen), (b), (blen))
+#endif
+#ifndef memrchr_P
+#define memrchr_P(str, val, len) memrchr((str), (val), (len))
+#endif
+#ifndef strcat_P
+#define strcat_P(dest, src) strcat((dest), (src))
+#endif
+#ifndef strchr_P
+#define strchr_P(str, c) strchr((str), (c))
+#endif
+#ifndef strchrnul_P
+#define strchrnul_P(str, c) strchrnul((str), (c))
+#endif
+#ifndef strcmp_P
+#define strcmp_P(a, b) strcmp((a), (b))
+#endif
+#ifndef strcpy_P
+#define strcpy_P(dest, src) strcpy((dest), (src))
+#endif
+#ifndef strcasecmp_P
+#define strcasecmp_P(a, b) strcasecmp((a), (b))
+#endif
+#ifndef strcasestr_P
+#define strcasestr_P(a, b) strcasestr((a), (b))
+#endif
+#ifndef strlcat_P
+#define strlcat_P(dest, src, len) strlcat((dest), (src), (len))
+#endif
+#ifndef strlcpy_P
+#define strlcpy_P(dest, src, len) strlcpy((dest), (src), (len))
+#endif
+#ifndef strlen_P
+#define strlen_P(s) strlen((const char *)(s))
+#endif
+#ifndef strnlen_P
+#define strnlen_P(str, len) strnlen((str), (len))
+#endif
+#ifndef strncmp_P
+#define strncmp_P(a, b, n) strncmp((a), (b), (n))
+#endif
+#ifndef strncasecmp_P
+#define strncasecmp_P(a, b, n) strncasecmp((a), (b), (n))
+#endif
+#ifndef strncat_P
+#define strncat_P(a, b, n) strncat((a), (b), (n))
+#endif
+#ifndef strncpy_P
+#define strncpy_P(a, b, n) strncpy((a), (b), (n))
+#endif
+#ifndef strpbrk_P
+#define strpbrk_P(str, chrs) strpbrk((str), (chrs))
+#endif
+#ifndef strrchr_P
+#define strrchr_P(str, c) strrchr((str), (c))
+#endif
+#ifndef strsep_P
+#define strsep_P(strp, delim) strsep((strp), (delim))
+#endif
+#ifndef strspn_P
+#define strspn_P(str, chrs) strspn((str), (chrs))
+#endif
+#ifndef strstr_P
+#define strstr_P(a, b) strstr((a), (b))
+#endif
+#ifndef sprintf_P
+#define sprintf_P(s, ...) sprintf((s), __VA_ARGS__)
+#endif
+#ifndef vfprintf_P
+#define vfprintf_P(s, ...) vfprintf((s), __VA_ARGS__)
+#endif
+#ifndef printf_P
+#define printf_P(...) printf(__VA_ARGS__)
+#endif
+#ifndef snprintf_P
+#define snprintf_P(s, n, ...) ((s), (n), __VA_ARGS__)
+#endif
+#ifndef vsprintf_P
+#define vsprintf_P(s, ...) ((s),__VA_ARGS__)
+#endif
+#ifndef vsnprintf_P
+#define vsnprintf_P(s, n, ...) ((s), (n),__VA_ARGS__)
+#endif
+#ifndef fprintf_P
+#define fprintf_P(s, ...) ((s), __VA_ARGS__)
+#endif
+
+#ifndef pgm_read_byte
+#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
+#endif
+#ifndef pgm_read_word
+#define pgm_read_word(addr) (*(const unsigned short *)(addr))
+#endif
+#ifndef pgm_read_dword
+#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
+#endif
+#ifndef pgm_read_float
+#define pgm_read_float(addr) (*(const float *)(addr))
+#endif
+
+#ifndef pgm_read_byte_near
+#define pgm_read_byte_near(addr) pgm_read_byte(addr)
+#endif
+#ifndef pgm_read_word_near
+#define pgm_read_word_near(addr) pgm_read_word(addr)
+#endif
+#ifndef pgm_read_dword_near
+#define pgm_read_dword_near(addr) pgm_read_dword(addr)
+#endif
+#ifndef pgm_read_float_near
+#define pgm_read_float_near(addr) pgm_read_float(addr)
+#endif
+#ifndef pgm_read_byte_far
+#define pgm_read_byte_far(addr) pgm_read_byte(addr)
+#endif
+#ifndef pgm_read_word_far
+#define pgm_read_word_far(addr) pgm_read_word(addr)
+#endif
+#ifndef pgm_read_dword_far
+#define pgm_read_dword_far(addr) pgm_read_dword(addr)
+#endif
+#ifndef pgm_read_float_far
+#define pgm_read_float_far(addr) pgm_read_float(addr)
+#endif
+
+#ifndef pgm_read_pointer
+#define pgm_read_pointer
+#endif
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USB_Host/xboxEnums.h	Sat Jan 18 15:06:35 2020 +0000
@@ -0,0 +1,66 @@
+/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.
+
+ This software may be distributed and modified under the terms of the GNU
+ General Public License version 2 (GPL2) as published by the Free Software
+ Foundation and appearing in the file GPL2.TXT included in the packaging of
+ this file. Please note that GPL2 Section 2[b] requires that all works based
+ on this software must also be made publicly available under the terms of
+ the GPL2 ("Copyleft").
+
+ Contact information
+ -------------------
+
+ Kristian Lauszus, TKJ Electronics
+ Web      :  http://www.tkjelectronics.com
+ e-mail   :  kristianl@tkjelectronics.com
+ */
+
+#ifndef _xboxenums_h
+#define _xboxenums_h
+
+#include "controllerEnums.h"
+
+/** Enum used to set special LED modes supported by the Xbox controller. */
+enum LEDModeEnum {
+        ROTATING = 0x0A,
+        FASTBLINK = 0x0B,
+        SLOWBLINK = 0x0C,
+        ALTERNATING = 0x0D,
+};
+
+/** Used to set the LEDs on the controllers */
+const uint8_t XBOX_LEDS[] PROGMEM = {
+        0x00, // OFF
+        0x02, // LED1
+        0x03, // LED2
+        0x04, // LED3
+        0x05, // LED4
+        0x01, // ALL - Used to blink all LEDs
+};
+/** Buttons on the controllers */
+const uint16_t XBOX_BUTTONS[] PROGMEM = {
+        0x0100, // UP
+        0x0800, // RIGHT
+        0x0200, // DOWN
+        0x0400, // LEFT
+
+        0x2000, // BACK
+        0x1000, // START
+        0x4000, // L3
+        0x8000, // R3
+
+        0, 0, // Skip L2 and R2 as these are analog buttons
+        0x0001, // L1
+        0x0002, // R1
+
+        0x0020, // B
+        0x0010, // A
+        0x0040, // X
+        0x0080, // Y
+
+        0x0004, // XBOX
+        0x0008, // SYNC
+};
+
+#endif
+