Squashed 'third_party/pico-sdk/lib/tinyusb/' content from commit 868948f67c

Change-Id: I5d33c2566dd597be9d4b1c30d4b3723c5ef4a265
git-subtree-dir: third_party/pico-sdk/lib/tinyusb
git-subtree-split: 868948f67c90fa7c2553cdcd604b52862cf55720
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c
new file mode 100644
index 0000000..c6cd388
--- /dev/null
+++ b/src/class/net/ecm_rndis_device.c
@@ -0,0 +1,445 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Peter Lawrence
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_ECM_RNDIS )
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+
+#include "net_device.h"
+#include "rndis_protocol.h"
+
+void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+typedef struct
+{
+  uint8_t itf_num;      // Index number of Management Interface, +1 for Data Interface
+  uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
+
+  uint8_t ep_notif;
+  uint8_t ep_in;
+  uint8_t ep_out;
+
+  bool ecm_mode;
+
+  // Endpoint descriptor use to open/close when receving SetInterface
+  // TODO since configuration descriptor may not be long-lived memory, we should
+  // keep a copy of endpoint attribute instead
+  uint8_t const * ecm_desc_epdata;
+
+} netd_interface_t;
+
+#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
+#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
+
+struct ecm_notify_struct
+{
+  tusb_control_request_t header;
+  uint32_t downlink, uplink;
+};
+
+static const struct ecm_notify_struct ecm_notify_nc =
+{
+  .header = {
+    .bmRequestType = 0xA1,
+    .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
+    .wValue = 1 /* Connected */,
+    .wLength = 0,
+  },
+};
+
+static const struct ecm_notify_struct ecm_notify_csc =
+{
+  .header = {
+    .bmRequestType = 0xA1,
+    .bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
+    .wLength = 8,
+  },
+  .downlink = 9728000,
+  .uplink = 9728000,
+};
+
+// TODO remove CFG_TUSB_MEM_SECTION, control internal buffer is already in this special section
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static union
+{
+  uint8_t rndis_buf[120];
+  struct ecm_notify_struct ecm_buf;
+} notify;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+// TODO remove CFG_TUSB_MEM_SECTION
+CFG_TUSB_MEM_SECTION static netd_interface_t _netd_itf;
+
+static bool can_xmit;
+
+void tud_network_recv_renew(void)
+{
+  usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_out, received, sizeof(received));
+}
+
+static void do_in_xfer(uint8_t *buf, uint16_t len)
+{
+  can_xmit = false;
+  usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_in, buf, len);
+}
+
+void netd_report(uint8_t *buf, uint16_t len)
+{
+  // skip if previous report not yet acknowledged by host
+  if ( usbd_edpt_busy(TUD_OPT_RHPORT, _netd_itf.ep_notif) ) return;
+  usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_notif, buf, len);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+void netd_init(void)
+{
+  tu_memclr(&_netd_itf, sizeof(_netd_itf));
+}
+
+void netd_reset(uint8_t rhport)
+{
+  (void) rhport;
+
+  netd_init();
+}
+
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+  bool const is_rndis = (TUD_RNDIS_ITF_CLASS    == itf_desc->bInterfaceClass    &&
+                         TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass &&
+                         TUD_RNDIS_ITF_PROTOCOL == itf_desc->bInterfaceProtocol);
+
+  bool const is_ecm = (TUSB_CLASS_CDC                           == itf_desc->bInterfaceClass &&
+                       CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
+                       0x00                                     == itf_desc->bInterfaceProtocol);
+
+  TU_VERIFY(is_rndis || is_ecm, 0);
+
+  // confirm interface hasn't already been allocated
+  TU_ASSERT(0 == _netd_itf.ep_notif, 0);
+
+  // sanity check the descriptor
+  _netd_itf.ecm_mode = is_ecm;
+
+  //------------- Management Interface -------------//
+  _netd_itf.itf_num = itf_desc->bInterfaceNumber;
+
+  uint16_t drv_len = sizeof(tusb_desc_interface_t);
+  uint8_t const * p_desc = tu_desc_next( itf_desc );
+
+  // Communication Functional Descriptors
+  while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
+  {
+    drv_len += tu_desc_len(p_desc);
+    p_desc   = tu_desc_next(p_desc);
+  }
+
+  // notification endpoint (if any)
+  if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
+  {
+    TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+
+    _netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+    drv_len += tu_desc_len(p_desc);
+    p_desc   = tu_desc_next(p_desc);
+  }
+
+  //------------- Data Interface -------------//
+  // - RNDIS Data followed immediately by a pair of endpoints
+  // - CDC-ECM data interface has 2 alternate settings
+  //   - 0 : zero endpoints for inactive (default)
+  //   - 1 : IN & OUT endpoints for active networking
+  TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+
+  do
+  {
+    tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
+    TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
+
+    drv_len += tu_desc_len(p_desc);
+    p_desc   = tu_desc_next(p_desc);
+  }while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) );
+
+  // Pair of endpoints
+  TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+
+  if ( _netd_itf.ecm_mode )
+  {
+    // ECM by default is in-active, save the endpoint attribute
+    // to open later when received setInterface
+    _netd_itf.ecm_desc_epdata = p_desc;
+  }else
+  {
+    // Open endpoint pair for RNDIS
+    TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 );
+
+    tud_network_init_cb();
+
+    // we are ready to transmit a packet
+    can_xmit = true;
+
+    // prepare for incoming packets
+    tud_network_recv_renew();
+  }
+
+  drv_len += 2*sizeof(tusb_desc_endpoint_t);
+
+  return drv_len;
+}
+
+static void ecm_report(bool nc)
+{
+  notify.ecm_buf = (nc) ? ecm_notify_nc : ecm_notify_csc;
+  notify.ecm_buf.header.wIndex = _netd_itf.itf_num;
+  netd_report((uint8_t *)&notify.ecm_buf, (nc) ? sizeof(notify.ecm_buf.header) : sizeof(notify.ecm_buf));
+}
+
+// Invoked when a control transfer occurred on an interface of this class
+// Driver response accordingly to the request and the transfer stage (setup/data/ack)
+// return false to stall control endpoint (e.g unsupported request)
+bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+  if ( stage == CONTROL_STAGE_SETUP )
+  {
+    switch ( request->bmRequestType_bit.type )
+    {
+      case TUSB_REQ_TYPE_STANDARD:
+        switch ( request->bRequest )
+        {
+          case TUSB_REQ_GET_INTERFACE:
+          {
+            uint8_t const req_itfnum = (uint8_t) request->wIndex;
+            TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum);
+
+            tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1);
+          }
+          break;
+
+          case TUSB_REQ_SET_INTERFACE:
+          {
+            uint8_t const req_itfnum = (uint8_t) request->wIndex;
+            uint8_t const req_alt    = (uint8_t) request->wValue;
+
+            // Only valid for Data Interface with Alternate is either 0 or 1
+            TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2);
+
+            // ACM-ECM only: qequest to enable/disable network activities
+            TU_VERIFY(_netd_itf.ecm_mode);
+
+            _netd_itf.itf_data_alt = req_alt;
+
+            if ( _netd_itf.itf_data_alt )
+            {
+              // TODO since we don't actually close endpoint
+              // hack here to not re-open it
+              if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 )
+              {
+                TU_ASSERT(_netd_itf.ecm_desc_epdata);
+                TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
+
+                // TODO should be merge with RNDIS's after endpoint opened
+                // Also should have opposite callback for application to disable network !!
+                tud_network_init_cb();
+                can_xmit = true; // we are ready to transmit a packet
+                tud_network_recv_renew(); // prepare for incoming packets
+              }
+            }else
+            {
+              // TODO close the endpoint pair
+              // For now pretend that we did, this should have no harm since host won't try to
+              // communicate with the endpoints again
+              // _netd_itf.ep_in = _netd_itf.ep_out = 0
+            }
+
+            tud_control_status(rhport, request);
+          }
+          break;
+
+          // unsupported request
+          default: return false;
+        }
+      break;
+
+      case TUSB_REQ_TYPE_CLASS:
+        TU_VERIFY (_netd_itf.itf_num == request->wIndex);
+
+        if (_netd_itf.ecm_mode)
+        {
+          /* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
+          if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
+          {
+            tud_control_xfer(rhport, request, NULL, 0);
+            ecm_report(true);
+          }
+        }
+        else
+        {
+          if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
+          {
+            rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf);
+            uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
+            TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
+            tud_control_xfer(rhport, request, notify.rndis_buf, msglen);
+          }
+          else
+          {
+            tud_control_xfer(rhport, request, notify.rndis_buf, sizeof(notify.rndis_buf));
+          }
+        }
+      break;
+
+      // unsupported request
+      default: return false;
+    }
+  }
+  else if ( stage == CONTROL_STAGE_DATA )
+  {
+    // Handle RNDIS class control OUT only
+    if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
+        request->bmRequestType_bit.direction == TUSB_DIR_OUT   &&
+        _netd_itf.itf_num == request->wIndex)
+    {
+      if ( !_netd_itf.ecm_mode )
+      {
+        rndis_class_set_handler(notify.rndis_buf, request->wLength);
+      }
+    }
+  }
+
+  return true;
+}
+
+static void handle_incoming_packet(uint32_t len)
+{
+  uint8_t *pnt = received;
+  uint32_t size = 0;
+
+  if (_netd_itf.ecm_mode)
+  {
+    size = len;
+  }
+  else
+  {
+    rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt);
+    if (len >= sizeof(rndis_data_packet_t))
+      if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len))
+        if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len)
+        {
+          pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
+          size = r->DataLength;
+        }
+  }
+
+  if (!tud_network_recv_cb(pnt, size))
+  {
+    /* if a buffer was never handled by user code, we must renew on the user's behalf */
+    tud_network_recv_renew();
+  }
+}
+
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+  (void) rhport;
+  (void) result;
+
+  /* new packet received */
+  if ( ep_addr == _netd_itf.ep_out )
+  {
+    handle_incoming_packet(xferred_bytes);
+  }
+
+  /* data transmission finished */
+  if ( ep_addr == _netd_itf.ep_in )
+  {
+    /* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */
+
+    if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) )
+    {
+      do_in_xfer(NULL, 0); /* a ZLP is needed */
+    }
+    else
+    {
+      /* we're finally finished */
+      can_xmit = true;
+    }
+  }
+
+  if ( _netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif) )
+  {
+    if (sizeof(notify.ecm_buf.header) == xferred_bytes) ecm_report(false);
+  }
+
+  return true;
+}
+
+bool tud_network_can_xmit(uint16_t size)
+{
+  (void)size;
+
+  return can_xmit;
+}
+
+void tud_network_xmit(void *ref, uint16_t arg)
+{
+  uint8_t *data;
+  uint16_t len;
+
+  if (!can_xmit)
+    return;
+
+  len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
+  data = transmitted + len;
+
+  len += tud_network_xmit_cb(data, ref, arg);
+
+  if (!_netd_itf.ecm_mode)
+  {
+    rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted);
+    memset(hdr, 0, sizeof(rndis_data_packet_t));
+    hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
+    hdr->MessageLength = len;
+    hdr->DataOffset = sizeof(rndis_data_packet_t) - offsetof(rndis_data_packet_t, DataOffset);
+    hdr->DataLength = len - sizeof(rndis_data_packet_t);
+  }
+
+  do_in_xfer(transmitted, len);
+}
+
+#endif
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
new file mode 100644
index 0000000..96ba11f
--- /dev/null
+++ b/src/class/net/ncm.h
@@ -0,0 +1,69 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+
+#ifndef _TUSB_NCM_H_
+#define _TUSB_NCM_H_
+
+#include "common/tusb_common.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// Table 4.3 Data Class Interface Protocol Codes
+typedef enum
+{
+  NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
+} ncm_data_interface_protocol_code_t;
+
+
+// Table 6.2 Class-Specific Request Codes for Network Control Model subclass
+typedef enum
+{
+  NCM_SET_ETHERNET_MULTICAST_FILTERS               = 0x40,
+  NCM_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41,
+  NCM_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42,
+  NCM_SET_ETHERNET_PACKET_FILTER                   = 0x43,
+  NCM_GET_ETHERNET_STATISTIC                       = 0x44,
+  NCM_GET_NTB_PARAMETERS                           = 0x80,
+  NCM_GET_NET_ADDRESS                              = 0x81,
+  NCM_SET_NET_ADDRESS                              = 0x82,
+  NCM_GET_NTB_FORMAT                               = 0x83,
+  NCM_SET_NTB_FORMAT                               = 0x84,
+  NCM_GET_NTB_INPUT_SIZE                           = 0x85,
+  NCM_SET_NTB_INPUT_SIZE                           = 0x86,
+  NCM_GET_MAX_DATAGRAM_SIZE                        = 0x87,
+  NCM_SET_MAX_DATAGRAM_SIZE                        = 0x88,
+  NCM_GET_CRC_MODE                                 = 0x89,
+  NCM_SET_CRC_MODE                                 = 0x8A,
+} ncm_request_code_t;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
new file mode 100644
index 0000000..3e131a8
--- /dev/null
+++ b/src/class/net/ncm_device.c
@@ -0,0 +1,510 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jacob Berg Potter
+ * Copyright (c) 2020 Peter Lawrence
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_NCM )
+
+#include "device/usbd.h"
+#include "device/usbd_pvt.h"
+#include "net_device.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF
+//--------------------------------------------------------------------+
+
+#define NTH16_SIGNATURE      0x484D434E
+#define NDP16_SIGNATURE_NCM0 0x304D434E
+#define NDP16_SIGNATURE_NCM1 0x314D434E
+
+typedef struct TU_ATTR_PACKED
+{
+  uint16_t wLength;
+  uint16_t bmNtbFormatsSupported;
+  uint32_t dwNtbInMaxSize;
+  uint16_t wNdbInDivisor;
+  uint16_t wNdbInPayloadRemainder;
+  uint16_t wNdbInAlignment;
+  uint16_t wReserved;
+  uint32_t dwNtbOutMaxSize;
+  uint16_t wNdbOutDivisor;
+  uint16_t wNdbOutPayloadRemainder;
+  uint16_t wNdbOutAlignment;
+  uint16_t wNtbOutMaxDatagrams;
+} ntb_parameters_t;
+
+typedef struct TU_ATTR_PACKED
+{
+  uint32_t dwSignature;
+  uint16_t wHeaderLength;
+  uint16_t wSequence;
+  uint16_t wBlockLength;
+  uint16_t wNdpIndex;
+} nth16_t;
+
+typedef struct TU_ATTR_PACKED
+{
+  uint16_t wDatagramIndex;
+  uint16_t wDatagramLength;
+} ndp16_datagram_t;
+
+typedef struct TU_ATTR_PACKED
+{
+  uint32_t dwSignature;
+  uint16_t wLength;
+  uint16_t wNextNdpIndex;
+  ndp16_datagram_t datagram[];
+} ndp16_t;
+
+typedef union TU_ATTR_PACKED {
+  struct {
+    nth16_t nth;
+    ndp16_t ndp;
+  };
+  uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
+} transmit_ntb_t;
+
+struct ecm_notify_struct
+{
+  tusb_control_request_t header;
+  uint32_t downlink, uplink;
+};
+
+typedef struct
+{
+  uint8_t itf_num;      // Index number of Management Interface, +1 for Data Interface
+  uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
+
+  uint8_t ep_notif;
+  uint8_t ep_in;
+  uint8_t ep_out;
+
+  const ndp16_t *ndp;
+  uint8_t num_datagrams, current_datagram_index;
+
+  enum {
+    REPORT_SPEED,
+    REPORT_CONNECTED,
+    REPORT_DONE
+  } report_state;
+  bool report_pending;
+
+  uint8_t  current_ntb;           // Index in transmit_ntb[] that is currently being filled with datagrams
+  uint8_t  datagram_count;        // Number of datagrams in transmit_ntb[current_ntb]
+  uint16_t next_datagram_offset;  // Offset in transmit_ntb[current_ntb].data to place the next datagram
+  uint16_t ntb_in_size;           // Maximum size of transmitted (IN to host) NTBs; initially CFG_TUD_NCM_IN_NTB_MAX_SIZE
+  uint8_t  max_datagrams_per_ntb; // Maximum number of datagrams per NTB; initially CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
+
+  uint16_t nth_sequence;          // Sequence number counter for transmitted NTBs
+
+  bool transferring;
+
+} ncm_interface_t;
+
+//--------------------------------------------------------------------+
+// INTERNAL OBJECT & FUNCTION DECLARATION
+//--------------------------------------------------------------------+
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static const ntb_parameters_t ntb_parameters = {
+    .wLength                 = sizeof(ntb_parameters_t),
+    .bmNtbFormatsSupported   = 0x01,
+    .dwNtbInMaxSize          = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
+    .wNdbInDivisor           = 4,
+    .wNdbInPayloadRemainder  = 0,
+    .wNdbInAlignment         = CFG_TUD_NCM_ALIGNMENT,
+    .wReserved               = 0,
+    .dwNtbOutMaxSize         = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
+    .wNdbOutDivisor          = 4,
+    .wNdbOutPayloadRemainder = 0,
+    .wNdbOutAlignment        = CFG_TUD_NCM_ALIGNMENT,
+    .wNtbOutMaxDatagrams     = 0
+};
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static transmit_ntb_t transmit_ntb[2];
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t receive_ntb[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
+
+static ncm_interface_t ncm_interface;
+
+/*
+ * Set up the NTB state in ncm_interface to be ready to add datagrams.
+ */
+static void ncm_prepare_for_tx(void) {
+  ncm_interface.datagram_count = 0;
+  // datagrams start after all the headers
+  ncm_interface.next_datagram_offset = sizeof(nth16_t) + sizeof(ndp16_t)
+      + ((CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB + 1) * sizeof(ndp16_datagram_t));
+}
+
+/*
+ * If not already transmitting, start sending the current NTB to the host and swap buffers
+ * to start filling the other one with datagrams.
+ */
+static void ncm_start_tx(void) {
+  if (ncm_interface.transferring) {
+    return;
+  }
+
+  transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
+  size_t ntb_length = ncm_interface.next_datagram_offset;
+
+  // Fill in NTB header
+  ntb->nth.dwSignature = NTH16_SIGNATURE;
+  ntb->nth.wHeaderLength = sizeof(nth16_t);
+  ntb->nth.wSequence = ncm_interface.nth_sequence++;
+  ntb->nth.wBlockLength = ntb_length;
+  ntb->nth.wNdpIndex = sizeof(nth16_t);
+
+  // Fill in NDP16 header and terminator
+  ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
+  ntb->ndp.wLength = sizeof(ndp16_t) + (ncm_interface.datagram_count + 1) * sizeof(ndp16_datagram_t);
+  ntb->ndp.wNextNdpIndex = 0;
+  ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = 0;
+  ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = 0;
+
+  // Kick off an endpoint transfer
+  usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_in, ntb->data, ntb_length);
+  ncm_interface.transferring = true;
+
+  // Swap to the other NTB and clear it out
+  ncm_interface.current_ntb = 1 - ncm_interface.current_ntb;
+  ncm_prepare_for_tx();
+}
+
+static struct ecm_notify_struct ncm_notify_connected =
+{
+    .header = {
+        .bmRequestType_bit = {
+          .recipient = TUSB_REQ_RCPT_INTERFACE,
+          .type      = TUSB_REQ_TYPE_CLASS,
+          .direction = TUSB_DIR_IN
+        },
+        .bRequest      = CDC_NOTIF_NETWORK_CONNECTION,
+        .wValue        = 1 /* Connected */,
+        .wLength       = 0,
+    },
+};
+
+static struct ecm_notify_struct ncm_notify_speed_change =
+{
+    .header = {
+        .bmRequestType_bit = {
+          .recipient = TUSB_REQ_RCPT_INTERFACE,
+          .type      = TUSB_REQ_TYPE_CLASS,
+          .direction = TUSB_DIR_IN
+        },
+        .bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE,
+        .wLength = 8,
+    },
+    .downlink = 10000000,
+    .uplink = 10000000,
+};
+
+void tud_network_recv_renew(void)
+{
+  if (!ncm_interface.num_datagrams)
+  {
+    usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb));
+    return;
+  }
+
+  const ndp16_t *ndp = ncm_interface.ndp;
+  const int i = ncm_interface.current_datagram_index;
+  ncm_interface.current_datagram_index++;
+  ncm_interface.num_datagrams--;
+
+  tud_network_recv_cb(receive_ntb + ndp->datagram[i].wDatagramIndex, ndp->datagram[i].wDatagramLength);
+}
+
+//--------------------------------------------------------------------+
+// USBD Driver API
+//--------------------------------------------------------------------+
+
+void netd_init(void)
+{
+  tu_memclr(&ncm_interface, sizeof(ncm_interface));
+  ncm_interface.ntb_in_size = CFG_TUD_NCM_IN_NTB_MAX_SIZE;
+  ncm_interface.max_datagrams_per_ntb = CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB;
+  ncm_prepare_for_tx();
+}
+
+void netd_reset(uint8_t rhport)
+{
+  (void) rhport;
+
+  netd_init();
+}
+
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+{
+  // confirm interface hasn't already been allocated
+  TU_ASSERT(0 == ncm_interface.ep_notif, 0);
+
+  //------------- Management Interface -------------//
+  ncm_interface.itf_num = itf_desc->bInterfaceNumber;
+
+  uint16_t drv_len = sizeof(tusb_desc_interface_t);
+  uint8_t const * p_desc = tu_desc_next( itf_desc );
+
+  // Communication Functional Descriptors
+  while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
+  {
+    drv_len += tu_desc_len(p_desc);
+    p_desc   = tu_desc_next(p_desc);
+  }
+
+  // notification endpoint (if any)
+  if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
+  {
+    TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+
+    ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+
+    drv_len += tu_desc_len(p_desc);
+    p_desc   = tu_desc_next(p_desc);
+  }
+
+  //------------- Data Interface -------------//
+  // - CDC-NCM data interface has 2 alternate settings
+  //   - 0 : zero endpoints for inactive (default)
+  //   - 1 : IN & OUT endpoints for transfer of NTBs
+  TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
+
+  do
+  {
+    tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
+    TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
+
+    drv_len += tu_desc_len(p_desc);
+    p_desc   = tu_desc_next(p_desc);
+  } while((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len));
+
+  // Pair of endpoints
+  TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+
+  TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in) );
+
+  drv_len += 2*sizeof(tusb_desc_endpoint_t);
+
+  return drv_len;
+}
+
+static void ncm_report(void)
+{
+  if (ncm_interface.report_state == REPORT_SPEED) {
+    ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
+    usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change));
+    ncm_interface.report_state = REPORT_CONNECTED;
+    ncm_interface.report_pending = true;
+  } else if (ncm_interface.report_state == REPORT_CONNECTED) {
+    ncm_notify_connected.header.wIndex = ncm_interface.itf_num;
+    usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected));
+    ncm_interface.report_state = REPORT_DONE;
+    ncm_interface.report_pending = true;
+  }
+}
+
+TU_ATTR_WEAK void tud_network_link_state_cb(bool state)
+{
+  (void)state;
+}
+
+// Handle class control request
+// return false to stall control endpoint (e.g unsupported request)
+bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
+{
+  if ( stage != CONTROL_STAGE_SETUP ) return true;
+
+  switch ( request->bmRequestType_bit.type )
+  {
+    case TUSB_REQ_TYPE_STANDARD:
+      switch ( request->bRequest )
+      {
+        case TUSB_REQ_GET_INTERFACE:
+        {
+          uint8_t const req_itfnum = (uint8_t) request->wIndex;
+          TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum);
+
+          tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
+        }
+          break;
+
+        case TUSB_REQ_SET_INTERFACE:
+        {
+          uint8_t const req_itfnum = (uint8_t) request->wIndex;
+          uint8_t const req_alt    = (uint8_t) request->wValue;
+
+          // Only valid for Data Interface with Alternate is either 0 or 1
+          TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2);
+
+          if (req_alt != ncm_interface.itf_data_alt) {
+            ncm_interface.itf_data_alt = req_alt;
+
+            if (ncm_interface.itf_data_alt) {
+              if (!usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
+                tud_network_recv_renew(); // prepare for incoming datagrams
+              }
+              if (!ncm_interface.report_pending) {
+                ncm_report();
+              }
+            }
+
+            tud_network_link_state_cb(ncm_interface.itf_data_alt);
+          }
+
+          tud_control_status(rhport, request);
+        }
+          break;
+
+          // unsupported request
+        default: return false;
+      }
+      break;
+
+    case TUSB_REQ_TYPE_CLASS:
+      TU_VERIFY (ncm_interface.itf_num == request->wIndex);
+
+      if (NCM_GET_NTB_PARAMETERS == request->bRequest)
+      {
+        tud_control_xfer(rhport, request, (void*)&ntb_parameters, sizeof(ntb_parameters));
+      }
+
+      break;
+
+      // unsupported request
+    default: return false;
+  }
+
+  return true;
+}
+
+static void handle_incoming_datagram(uint32_t len)
+{
+  uint32_t size = len;
+
+  if (len == 0) {
+    return;
+  }
+
+  TU_ASSERT(size >= sizeof(nth16_t), );
+
+  const nth16_t *hdr = (const nth16_t *)receive_ntb;
+  TU_ASSERT(hdr->dwSignature == NTH16_SIGNATURE, );
+  TU_ASSERT(hdr->wNdpIndex >= sizeof(nth16_t) && (hdr->wNdpIndex + sizeof(ndp16_t)) <= len, );
+
+  const ndp16_t *ndp = (const ndp16_t *)(receive_ntb + hdr->wNdpIndex);
+  TU_ASSERT(ndp->dwSignature == NDP16_SIGNATURE_NCM0 || ndp->dwSignature == NDP16_SIGNATURE_NCM1, );
+  TU_ASSERT(hdr->wNdpIndex + ndp->wLength <= len, );
+
+  int num_datagrams = (ndp->wLength - 12) / 4;
+  ncm_interface.current_datagram_index = 0;
+  ncm_interface.num_datagrams = 0;
+  ncm_interface.ndp = ndp;
+  for (int i = 0; i < num_datagrams && ndp->datagram[i].wDatagramIndex && ndp->datagram[i].wDatagramLength; i++)
+  {
+    ncm_interface.num_datagrams++;
+  }
+  
+  tud_network_recv_renew();
+}
+
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+{
+  (void) rhport;
+  (void) result;
+
+  /* new datagram receive_ntb */
+  if (ep_addr == ncm_interface.ep_out )
+  {
+    handle_incoming_datagram(xferred_bytes);
+  }
+
+  /* data transmission finished */
+  if (ep_addr == ncm_interface.ep_in )
+  {
+    if (ncm_interface.transferring) {
+      ncm_interface.transferring = false;
+    }
+
+    // If there are datagrams queued up that we tried to send while this NTB was being emitted, send them now
+    if (ncm_interface.datagram_count && ncm_interface.itf_data_alt == 1) {
+      ncm_start_tx();
+    }
+  }
+
+  if (ep_addr == ncm_interface.ep_notif )
+  {
+    ncm_interface.report_pending = false;
+    ncm_report();
+  }
+
+  return true;
+}
+
+// poll network driver for its ability to accept another packet to transmit
+bool tud_network_can_xmit(uint16_t size)
+{
+  TU_VERIFY(ncm_interface.itf_data_alt == 1);
+
+  if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) {
+    TU_LOG2("NTB full [by count]\r\n");
+    return false;
+  }
+
+  size_t next_datagram_offset = ncm_interface.next_datagram_offset;
+  if (next_datagram_offset + size > ncm_interface.ntb_in_size) {
+    TU_LOG2("ntb full [by size]\r\n");
+    return false;
+  }
+
+  return true;
+}
+
+void tud_network_xmit(void *ref, uint16_t arg)
+{
+  transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
+  size_t next_datagram_offset = ncm_interface.next_datagram_offset;
+
+  uint16_t size = tud_network_xmit_cb(ntb->data + next_datagram_offset, ref, arg);
+
+  ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = ncm_interface.next_datagram_offset;
+  ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = size;
+
+  ncm_interface.datagram_count++;
+  next_datagram_offset += size;
+
+  // round up so the next datagram is aligned correctly
+  next_datagram_offset += (CFG_TUD_NCM_ALIGNMENT - 1);
+  next_datagram_offset -= (next_datagram_offset % CFG_TUD_NCM_ALIGNMENT);
+
+  ncm_interface.next_datagram_offset = next_datagram_offset;
+
+  ncm_start_tx();
+}
+
+#endif
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
new file mode 100644
index 0000000..6e29446
--- /dev/null
+++ b/src/class/net/net_device.h
@@ -0,0 +1,118 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Peter Lawrence
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_NET_DEVICE_H_
+#define _TUSB_NET_DEVICE_H_
+
+#include "class/cdc/cdc.h"
+
+#if CFG_TUD_ECM_RNDIS && CFG_TUD_NCM
+#error "Cannot enable both ECM_RNDIS and NCM network drivers"
+#endif
+
+#include "ncm.h"
+
+/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
+#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+/* Maximum Transmission Unit (in bytes) of the network, including Ethernet header */
+#ifndef CFG_TUD_NET_MTU
+#define CFG_TUD_NET_MTU           1514
+#endif
+
+#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
+#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
+#endif
+
+#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
+#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
+#endif
+
+#ifndef CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
+#define CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB 8
+#endif
+
+#ifndef CFG_TUD_NCM_ALIGNMENT
+#define CFG_TUD_NCM_ALIGNMENT 4
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Application API
+//--------------------------------------------------------------------+
+
+// indicate to network driver that client has finished with the packet provided to network_recv_cb()
+void tud_network_recv_renew(void);
+
+// poll network driver for its ability to accept another packet to transmit
+bool tud_network_can_xmit(uint16_t size);
+
+// if network_can_xmit() returns true, network_xmit() can be called once
+void tud_network_xmit(void *ref, uint16_t arg);
+
+//--------------------------------------------------------------------+
+// Application Callbacks (WEAK is optional)
+//--------------------------------------------------------------------+
+
+// client must provide this: return false if the packet buffer was not accepted
+bool tud_network_recv_cb(const uint8_t *src, uint16_t size);
+
+// client must provide this: copy from network stack packet pointer to dst
+uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg);
+
+//------------- ECM/RNDIS -------------//
+
+// client must provide this: initialize any network state back to the beginning
+void tud_network_init_cb(void);
+
+// client must provide this: 48-bit MAC address
+// TODO removed later since it is not part of tinyusb stack
+extern const uint8_t tud_network_mac_address[6];
+
+//------------- NCM -------------//
+
+// callback to client providing optional indication of internal state of network driver
+void tud_network_link_state_cb(bool state);
+
+//--------------------------------------------------------------------+
+// INTERNAL USBD-CLASS DRIVER API
+//--------------------------------------------------------------------+
+void     netd_init            (void);
+void     netd_reset           (uint8_t rhport);
+uint16_t netd_open            (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
+bool     netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool     netd_xfer_cb         (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
+void     netd_report          (uint8_t *buf, uint16_t len);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _TUSB_NET_DEVICE_H_ */