summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
authorHardy Griech <[email protected]>2023-08-20 18:22:07 +0200
committerHardy Griech <[email protected]>2023-08-20 18:23:32 +0200
commitfca08c939c0d04b9485413166b69f8abac6fb083 (patch)
tree5f6ceee119daac3d976fea4ea4bef4f26c02cbe8 /src/class
parent92457ec99f1690b772ef9fa6b348256701c7fcf7 (diff)
files taken from yapicoprobe
Diffstat (limited to 'src/class')
-rw-r--r--src/class/net/ncm.h106
-rw-r--r--src/class/net/ncm_device.c1247
-rw-r--r--src/class/net/net_device.h24
3 files changed, 980 insertions, 397 deletions
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
index 96ba11fbc..313237747 100644
--- a/src/class/net/ncm.h
+++ b/src/class/net/ncm.h
@@ -30,10 +30,47 @@
#include "common/tusb_common.h"
-#ifdef __cplusplus
- extern "C" {
+
+#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
+ /// must be >> MTU
+ #define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
+#endif
+#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
+ /// must be >> MTU
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
+#endif
+
+#ifndef CFG_TUD_NCM_OUT_NTB_N
+ /// number of ntb buffers for reception side
+ /// 1 - good performance
+ /// 2 - up to 30% more performance with iperf with small packets
+ /// >2 - no performance gain
+ #define CFG_TUD_NCM_OUT_NTB_N 2
+#endif
+
+#ifndef CFG_TUD_NCM_IN_NTB_N
+ /// number of ntb buffers for transmission side
+ /// 1 - good performance but SystemView shows lost events (on load test)
+ /// 2 - up to 50% more performance with iperf with small packets, "tud_network_can_xmit: request blocked"
+ /// happens from time to time with SystemView
+ /// 3 - "tud_network_can_xmit: request blocked" never happens
+ /// >2 - no performance gain
+ #define CFG_TUD_NCM_IN_NTB_N 3
+#endif
+
+#ifndef CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
+ /// this is for the transmission size for allocation of \a ndp16_datagram_t
+ #define CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB 8
+#endif
+
+#ifndef CFG_TUD_NCM_ALIGNMENT
+ #define CFG_TUD_NCM_ALIGNMENT 4
+#endif
+#if (CFG_TUD_NCM_ALIGNMENT != 4)
+ #error "CFG_TUD_NCM_ALIGNMENT must be 4, otherwise the headers and start of datagrams have to be aligned (which they are currently not)"
#endif
+
// Table 4.3 Data Class Interface Protocol Codes
typedef enum
{
@@ -62,8 +99,67 @@ typedef enum
NCM_SET_CRC_MODE = 0x8A,
} ncm_request_code_t;
-#ifdef __cplusplus
- }
-#endif
+
+#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;
+ ndp16_datagram_t ndp_datagram[CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB + 1];
+ };
+ uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
+} xmit_ntb_t;
+
+typedef union TU_ATTR_PACKED {
+ struct {
+ nth16_t nth;
+ // only the header is at a guaranteed position
+ };
+ uint8_t data[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
+} recv_ntb_t;
+
+struct ncm_notify_t {
+ tusb_control_request_t header;
+ uint32_t downlink, uplink;
+};
+
#endif
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index 226c42c4e..f7a236e69 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -1,9 +1,7 @@
/*
* The MIT License (MIT)
*
- * Copyright (c) 2020 Jacob Berg Potter
- * Copyright (c) 2020 Peter Lawrence
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2023 Hardy Griech
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -26,493 +24,1004 @@
* This file is part of the TinyUSB stack.
*/
+/**
+ * Small Glossary (from the spec)
+ * --------------
+ * Datagram - A collection of bytes forming a single item of information, passed as a unit from source to destination.
+ * NCM - Network Control Model
+ * NDP - NCM Datagram Pointer: NTB structure that delineates Datagrams (typically Ethernet frames) within an NTB
+ * NTB - NCM Transfer Block: a data structure for efficient USB encapsulation of one or more datagrams
+ * Each NTB is designed to be a single USB transfer
+ * NTH - NTB Header: a data structure at the front of each NTB, which provides the information needed to validate
+ * the NTB and begin decoding
+ *
+ * Some explanations
+ * -----------------
+ * - rhport is the USB port of the device, in most cases "0"
+ * - itf_data_alt if != 0 -> data xmit/recv are allowed (see spec)
+ * - ep_in IN endpoints take data from the device intended to go in to the host (the device transmits)
+ * - ep_out OUT endpoints send data out of the host to the device (the device receives)
+ */
+
#include "tusb_option.h"
-#if ( CFG_TUD_ENABLED && CFG_TUD_NCM )
+#if ECLIPSE_GUI || ( CFG_TUD_ENABLED && CFG_TUD_NCM )
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
#include "device/usbd.h"
#include "device/usbd_pvt.h"
+
#include "net_device.h"
+#include "ncm.h"
+
+
+#if !defined(tu_static) || ECLIPSE_GUI
+ // TinyUSB <=0.15.0 does not know "tu_static"
+ #define tu_static static
+#endif
-// Level where CFG_TUSB_DEBUG must be at least for this driver is logged
-#ifndef CFG_TUD_NCM_LOG_LEVEL
- #define CFG_TUD_NCM_LOG_LEVEL CFG_TUD_LOG_LEVEL
+#if 0
+ #define DEBUG_OUT(...) printf(__VA_ARGS__)
+ #define DEBUG_OUT_ENABLED
+#else
+ #define DEBUG_OUT(...)
#endif
-#define TU_LOG_DRV(...) TU_LOG(CFG_TUD_NCM_LOG_LEVEL, __VA_ARGS__)
+#if 0
+ #define INFO_OUT(...) printf(__VA_ARGS__)
+#else
+ #define INFO_OUT(...)
+#endif
+
+#if 1
+ #define ERROR_OUT(...) printf(__VA_ARGS__)
+#else
+ #define ERROR_OUT(...)
+#endif
+
+// calculate alignment of xmit datagrams within an NTB
+#define XMIT_ALIGN_OFFSET(x) ((CFG_TUD_NCM_ALIGNMENT - ((x) & (CFG_TUD_NCM_ALIGNMENT - 1))) & (CFG_TUD_NCM_ALIGNMENT - 1))
+
+//-----------------------------------------------------------------------------
+//
+// Module global things
+//
+#define XMIT_NTB_N CFG_TUD_NCM_IN_NTB_N
+#define RECV_NTB_N CFG_TUD_NCM_OUT_NTB_N
+
+typedef struct {
+ // general
+ uint8_t ep_in; //!< endpoint for outgoing datagrams (naming is a little bit confusing)
+ uint8_t ep_out; //!< endpoint for incoming datagrams (naming is a little bit confusing)
+ uint8_t ep_notif; //!< endpoint for notifications
+ uint8_t itf_num; //!< interface number
+ uint8_t itf_data_alt; //!< ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3)
+ uint8_t rhport; //!< storage of \a rhport because some callbacks are done without it
+
+ // recv handling
+ recv_ntb_t recv_ntb[RECV_NTB_N]; //!< actual recv NTBs
+ recv_ntb_t *recv_free_ntb[RECV_NTB_N]; //!< free list of recv NTBs
+ recv_ntb_t *recv_ready_ntb[RECV_NTB_N]; //!< NTBs waiting for transmission to glue logic
+ recv_ntb_t *recv_tinyusb_ntb; //!< buffer for the running transfer TinyUSB -> driver
+ recv_ntb_t *recv_glue_ntb; //!< buffer for the running transfer driver -> glue logic
+ uint16_t recv_glue_ntb_datagram_ndx; //!< index into \a recv_glue_ntb_datagram
+
+ // xmit handling
+ xmit_ntb_t xmit_ntb[XMIT_NTB_N]; //!< actual xmit NTBs
+ xmit_ntb_t *xmit_free_ntb[XMIT_NTB_N]; //!< free list of xmit NTBs
+ xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N]; //!< NTBs waiting for transmission to TinyUSB
+ xmit_ntb_t *xmit_tinyusb_ntb; //!< buffer for the running transfer driver -> TinyUSB
+ xmit_ntb_t *xmit_glue_ntb; //!< buffer for the running transfer glue logic -> driver
+ uint16_t xmit_sequence; //!< NTB sequence counter
+ uint16_t xmit_glue_ntb_datagram_ndx; //!< index into \a xmit_glue_ntb_datagram
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
+ // notification handling
+ enum {
+ NOTIFICATION_SPEED,
+ NOTIFICATION_CONNECTED,
+ NOTIFICATION_DONE
+ } notification_xmit_state; //!< state of notification transmission
+ bool notification_xmit_is_running; //!< notification is currently transmitted
+} ncm_interface_t;
+
+
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static ncm_interface_t ncm_interface;
+
+
+/**
+ * This is the NTB parameter structure
+ *
+ * \attention
+ * We are lucky, that byte order is correct
+ */
+CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = {
+ .wLength = sizeof(ntb_parameters_t),
+ .bmNtbFormatsSupported = 0x01, // 16-bit NTB supported
+ .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 = 6 // 0=no limit
+};
+
+// Some confusing remarks about wNtbOutMaxDatagrams...
+// ==1 -> SystemView packets/s goes up to 2000 and events are lost during startup
+// ==0 -> SystemView runs fine, iperf shows in wireshark a lot of error
+// ==6 -> SystemView runs fine, iperf also
+// >6 -> iperf starts to show errors
+// -> 6 seems to be the best value. Why? Don't know, perhaps only on my system?
+// switch \a INFO_OUT on to see interesting values for this.
+//
+// iperf: for MSS in 100 200 400 800 1200 1450 1500; do iperf -c 192.168.14.1 -e -i 1 -M $MSS -l 8192 -P 1; sleep 2; done
+// sysview: SYSTICKS_PER_SEC=35000, IDLE_US=1000, PRINT_MOD=1000
+//
+
+//-----------------------------------------------------------------------------
+//
+// everything about notifications
+//
+tu_static struct ncm_notify_t 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,
+ },
+};
+
+tu_static struct ncm_notify_t 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 = 1000000,
+ .uplink = 1000000,
+};
-#define NTH16_SIGNATURE 0x484D434E
-#define NDP16_SIGNATURE_NCM0 0x304D434E
-#define NDP16_SIGNATURE_NCM1 0x314D434E
-typedef struct TU_ATTR_PACKED
+
+static void notification_xmit(uint8_t rhport, bool force_next)
+/**
+ * Transmit next notification to the host (if appropriate).
+ * Notifications are transferred to the host once during connection setup.
+ */
{
- 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;
+ DEBUG_OUT("notification_xmit(%d, %d) - %d %d\n", force_next, rhport, ncm_interface.notification_xmit_state, ncm_interface.notification_xmit_is_running);
+
+ if ( !force_next && ncm_interface.notification_xmit_is_running) {
+ return;
+ }
+
+ if (ncm_interface.notification_xmit_state == NOTIFICATION_SPEED) {
+ DEBUG_OUT(" NOTIFICATION_SPEED\n");
+ ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
+ usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change));
+ ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
+ ncm_interface.notification_xmit_is_running = true;
+ }
+ else if (ncm_interface.notification_xmit_state == NOTIFICATION_CONNECTED) {
+ DEBUG_OUT(" NOTIFICATION_CONNECTED\n");
+ ncm_notify_connected.header.wIndex = ncm_interface.itf_num;
+ usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_notify_connected, sizeof(ncm_notify_connected));
+ ncm_interface.notification_xmit_state = NOTIFICATION_DONE;
+ ncm_interface.notification_xmit_is_running = true;
+ }
+ else {
+ DEBUG_OUT(" NOTIFICATION_FINISHED\n");
+ }
+} // notification_xmit
-typedef struct TU_ATTR_PACKED
+
+//-----------------------------------------------------------------------------
+//
+// everything about packet transmission (driver -> TinyUSB)
+//
+
+
+static void xmit_put_ntb_into_free_list(xmit_ntb_t *free_ntb)
+/**
+ * Put NTB into the transmitter free list.
+ */
{
- uint32_t dwSignature;
- uint16_t wHeaderLength;
- uint16_t wSequence;
- uint16_t wBlockLength;
- uint16_t wNdpIndex;
-} nth16_t;
+ DEBUG_OUT("xmit_put_ntb_into_free_list() - %p\n", ncm_interface.xmit_tinyusb_ntb);
+
+ if (free_ntb == NULL) {
+ // can happen due to ZLPs
+ return;
+ }
+
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_free_ntb[i] == NULL) {
+ ncm_interface.xmit_free_ntb[i] = free_ntb;
+ return;
+ }
+ }
+ ERROR_OUT("xmit_put_ntb_into_free_list - no entry in free list\n"); // this should not happen
+} // xmit_put_ntb_into_free_list
+
+
-typedef struct TU_ATTR_PACKED
+static xmit_ntb_t *xmit_get_free_ntb(void)
+/**
+ * Get an NTB from the free list
+ */
{
- uint16_t wDatagramIndex;
- uint16_t wDatagramLength;
-} ndp16_datagram_t;
+ DEBUG_OUT("xmit_get_free_ntb()\n");
+
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_free_ntb[i] != NULL) {
+ xmit_ntb_t *free = ncm_interface.xmit_free_ntb[i];
+ ncm_interface.xmit_free_ntb[i] = NULL;
+ return free;
+ }
+ }
+ return NULL;
+} // xmit_get_free_ntb
-typedef struct TU_ATTR_PACKED
+
+
+static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb)
+/**
+ * Put a filled NTB into the ready list
+ */
{
- uint32_t dwSignature;
- uint16_t wLength;
- uint16_t wNextNdpIndex;
- ndp16_datagram_t datagram[];
-} ndp16_t;
+ INFO_OUT("xmit_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->len);
+
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ if (ncm_interface.xmit_ready_ntb[i] == NULL) {
+ ncm_interface.xmit_ready_ntb[i] = ready_ntb;
+ return;
+ }
+ }
+ ERROR_OUT("xmit_put_ntb_into_ready_list: ready list full\n"); // this should not happen
+} // xmit_put_ntb_into_ready_list
+
-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
+static xmit_ntb_t *xmit_get_next_ready_ntb(void)
+/**
+ * Get the next NTB from the ready list (and remove it from the list).
+ * If the ready list is empty, return NULL.
+ */
{
- tusb_control_request_t header;
- uint32_t downlink, uplink;
-};
+ xmit_ntb_t *r = NULL;
+
+ r = ncm_interface.xmit_ready_ntb[0];
+ memmove(ncm_interface.xmit_ready_ntb + 0, ncm_interface.xmit_ready_ntb + 1, sizeof(ncm_interface.xmit_ready_ntb) - sizeof(ncm_interface.xmit_ready_ntb[0]));
+
+ DEBUG_OUT("recv_get_next_ready_ntb: %p\n", r);
+ return r;
+} // xmit_get_next_ready_ntb
-typedef struct
+
+
+static bool xmit_insert_required_zlp(uint8_t rhport, uint16_t xferred_bytes)
+/**
+ * Transmit a ZLP if required
+ *
+ * \note
+ * Insertion of the ZLPs is a little bit different then described in the spec.
+ * But the below implementation actually works. Don't know if this is a spec
+ * or TinyUSB issue.
+ *
+ * \pre
+ * This must be called from netd_xfer_cb() so that ep_in is ready
+ */
{
- 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
+ DEBUG_OUT("xmit_insert_required_zlp(%d,%d)\n", rhport, xferred_bytes);
- uint8_t ep_notif;
- uint8_t ep_in;
- uint8_t ep_out;
+ if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
+ return false;
+ }
- const ndp16_t *ndp;
- uint8_t num_datagrams, current_datagram_index;
+ TU_ASSERT(ncm_interface.itf_data_alt == 1, false);
+ TU_ASSERT( !usbd_edpt_busy(rhport, ncm_interface.ep_in), false);
- enum {
- REPORT_SPEED,
- REPORT_CONNECTED,
- REPORT_DONE
- } report_state;
- bool report_pending;
+ INFO_OUT("xmit_insert_required_zlp! (%u)\n", (unsigned)xferred_bytes);
- 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
+ // start transmission of the ZLP
+ usbd_edpt_xfer(rhport, ncm_interface.ep_in, NULL, 0);
- uint16_t nth_sequence; // Sequence number counter for transmitted NTBs
+ return true;
+} // xmit_insert_required_zlp
- bool transferring;
-} ncm_interface_t;
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
+static void xmit_start_if_possible(uint8_t rhport)
+/**
+ * Start transmission if it there is a waiting packet and if can be done from interface side.
+ */
+{
+ DEBUG_OUT("xmit_start_if_possible()\n");
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_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
-};
+ if (ncm_interface.xmit_tinyusb_ntb != NULL) {
+ DEBUG_OUT(" !xmit_start_if_possible 1\n");
+ return;
+ }
+ if (ncm_interface.itf_data_alt != 1) {
+ ERROR_OUT(" !xmit_start_if_possible 2\n");
+ return;
+ }
+ if (usbd_edpt_busy(rhport, ncm_interface.ep_in)) {
+ INFO_OUT(" !xmit_start_if_possible 3\n");
+ return;
+ }
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static transmit_ntb_t transmit_ntb[2];
+ ncm_interface.xmit_tinyusb_ntb = xmit_get_next_ready_ntb();
+ if (ncm_interface.xmit_tinyusb_ntb == NULL) {
+ if (ncm_interface.xmit_glue_ntb == NULL || ncm_interface.xmit_glue_ntb_datagram_ndx == 0) {
+ // -> really nothing is waiting
+ return;
+ }
+ ncm_interface.xmit_tinyusb_ntb = ncm_interface.xmit_glue_ntb;
+ ncm_interface.xmit_glue_ntb = NULL;
+ }
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint8_t receive_ntb[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
+#if DEBUG_OUT_ENABLED
+ {
+ uint16_t len = ncm_interface.xmit_tinyusb_ntb->ntb.nth.wBlockLength;
+ DEBUG_OUT(" %d\n", len);
+ for (int i = 0; i < len; ++i) {
+ DEBUG_OUT(" %02x", ncm_interface.xmit_tinyusb_ntb->data[i]);
+ }
+ DEBUG_OUT("\n");
+ }
+#endif
-tu_static ncm_interface_t ncm_interface;
+ if (ncm_interface.xmit_glue_ntb_datagram_ndx != 1) {
+ DEBUG_OUT(">> %d %d\n", ncm_interface.xmit_tinyusb_ntb->len, ncm_interface.xmit_glue_ntb_datagram_ndx);
+ }
-/*
- * Set up the NTB state in ncm_interface to be ready to add datagrams.
+ // Kick off an endpoint transfer
+ usbd_edpt_xfer(0, ncm_interface.ep_in, ncm_interface.xmit_tinyusb_ntb->data, ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength);
+} // xmit_start_if_possible
+
+
+
+static bool xmit_requested_datagram_fits_into_current_ntb(uint16_t datagram_size)
+/**
+ * check if a new datagram fits into the current NTB
*/
-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));
-}
+{
+ DEBUG_OUT("xmit_requested_datagram_fits_into_current_ntb(%d) - %p %p\n", datagram_size, ncm_interface.xmit_tinyusb_ntb, ncm_interface.xmit_glue_ntb);
-/*
- * If not already transmitting, start sending the current NTB to the host and swap buffers
- * to start filling the other one with datagrams.
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ return false;
+ }
+ if (ncm_interface.xmit_glue_ntb_datagram_ndx >= CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB) {
+ return false;
+ }
+ if (ncm_interface.xmit_glue_ntb->nth.wBlockLength + datagram_size + XMIT_ALIGN_OFFSET(datagram_size) > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+ return false;
+ }
+ return true;
+} // xmit_requested_datagram_fits_into_current_ntb
+
+
+
+static bool xmit_setup_next_glue_ntb(void)
+/**
+ * Setup an NTB for the glue logic
*/
-static void ncm_start_tx(void) {
- if (ncm_interface.transferring) {
- return;
- }
+{
+ DEBUG_OUT("xmit_setup_next_glue_ntb - %p\n", ncm_interface.xmit_glue_ntb);
- transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
- size_t ntb_length = ncm_interface.next_datagram_offset;
+ if (ncm_interface.xmit_glue_ntb != NULL) {
+ // put NTB into waiting list (the new datagram did not fit in)
+ xmit_put_ntb_into_ready_list(ncm_interface.xmit_glue_ntb);
+ }
- // 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);
+ ncm_interface.xmit_glue_ntb = xmit_get_free_ntb(); // get next buffer (if any)
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ DEBUG_OUT(" xmit_setup_next_glue_ntb - nothing free\n"); // should happen rarely
+ return false;
+ }
- // 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;
+ ncm_interface.xmit_glue_ntb_datagram_ndx = 0;
- // Kick off an endpoint transfer
- usbd_edpt_xfer(0, ncm_interface.ep_in, ntb->data, ntb_length);
- ncm_interface.transferring = true;
+ xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
- // Swap to the other NTB and clear it out
- ncm_interface.current_ntb = 1 - ncm_interface.current_ntb;
- ncm_prepare_for_tx();
-}
+ // Fill in NTB header
+ ntb->nth.dwSignature = NTH16_SIGNATURE;
+ ntb->nth.wHeaderLength = sizeof(ntb->nth);
+ ntb->nth.wSequence = ncm_interface.xmit_sequence++;
+ ntb->nth.wBlockLength = sizeof(ntb->nth) + sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
+ ntb->nth.wNdpIndex = sizeof(ntb->nth);
+
+ // Fill in NDP16 header and terminator
+ ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
+ ntb->ndp.wLength = sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
+ ntb->ndp.wNextNdpIndex = 0;
+
+ memset(ntb->ndp_datagram, 0, sizeof(ntb->ndp_datagram));
+ return true;
+} // xmit_setup_next_glue_ntb
+
+
+//-----------------------------------------------------------------------------
+//
+// all the recv_*() stuff (TinyUSB -> driver -> glue logic)
+//
-tu_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,
- },
-};
-tu_static struct ecm_notify_struct ncm_notify_speed_change =
+static recv_ntb_t *recv_get_free_ntb(void)
+/**
+ * Return pointer to an available receive buffer or NULL.
+ * Returned buffer (if any) has the size \a CFG_TUD_NCM_OUT_NTB_MAX_SIZE.
+ */
{
- .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,
-};
+ DEBUG_OUT("recv_get_free_ntb()\n");
-void tud_network_recv_renew(void)
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_free_ntb[i] != NULL) {
+ recv_ntb_t *free = ncm_interface.recv_free_ntb[i];
+ ncm_interface.recv_free_ntb[i] = NULL;
+ return free;
+ }
+ }
+ return NULL;
+} // recv_get_free_ntb
+
+
+
+static recv_ntb_t *recv_get_next_ready_ntb(void)
+/**
+ * Get the next NTB from the ready list (and remove it from the list).
+ * If the ready list is empty, return NULL.
+ */
{
- if (!ncm_interface.num_datagrams)
- {
- usbd_edpt_xfer(0, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb));
- return;
- }
+ recv_ntb_t *r = NULL;
- const ndp16_t *ndp = ncm_interface.ndp;
- const int i = ncm_interface.current_datagram_index;
- ncm_interface.current_datagram_index++;
- ncm_interface.num_datagrams--;
+ r = ncm_interface.recv_ready_ntb[0];
+ memmove(ncm_interface.recv_ready_ntb + 0, ncm_interface.recv_ready_ntb + 1, sizeof(ncm_interface.recv_ready_ntb) - sizeof(ncm_interface.recv_ready_ntb[0]));
- tud_network_recv_cb(receive_ntb + ndp->datagram[i].wDatagramIndex, ndp->datagram[i].wDatagramLength);
-}
+ DEBUG_OUT("recv_get_next_ready_ntb: %p\n", r);
+ return r;
+} // recv_get_next_ready_ntb
-//--------------------------------------------------------------------+
-// USBD Driver API
-//--------------------------------------------------------------------+
-void netd_init(void)
+
+static void recv_put_ntb_into_free_list(recv_ntb_t *free_ntb)
+/**
+ *
+ */
{
- 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();
-}
+ DEBUG_OUT("recv_put_ntb_into_free_list(%p)\n", free_ntb);
-void netd_reset(uint8_t rhport)
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_free_ntb[i] == NULL) {
+ ncm_interface.recv_free_ntb[i] = free_ntb;
+ return;
+ }
+ }
+ ERROR_OUT("recv_put_ntb_into_free_list - no entry in free list\n"); // this should not happen
+} // recv_put_ntb_into_free_list
+
+
+
+static void recv_put_ntb_into_ready_list(recv_ntb_t *ready_ntb)
+/**
+ * The \a ncm_interface.recv_tinyusb_ntb is filled,
+ * put this buffer into the waiting list and free the receive logic.
+ */
{
- (void) rhport;
+ DEBUG_OUT("recv_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->len);
+
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ if (ncm_interface.recv_ready_ntb[i] == NULL) {
+ ncm_interface.recv_ready_ntb[i] = ready_ntb;
+ return;
+ }
+ }
+ ERROR_OUT("recv_put_ntb_into_ready_list: ready list full\n"); // this should not happen
+} // recv_put_ntb_into_ready_list
+
- netd_init();
-}
-uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
+static void recv_try_to_start_new_reception(uint8_t rhport)
+/**
+ * If possible, start a new reception TinyUSB -> driver.
+ * Return value is actually not of interest.
+ */
{
- // confirm interface hasn't already been allocated
- TU_ASSERT(0 == ncm_interface.ep_notif, 0);
+ DEBUG_OUT("recv_try_to_start_new_reception(%d)\n", rhport);
- //------------- Management Interface -------------//
- ncm_interface.itf_num = itf_desc->bInterfaceNumber;
+ if (ncm_interface.itf_data_alt != 1) {
+ return;
+ }
+ if (ncm_interface.recv_tinyusb_ntb != NULL) {
+ return;
+ }
+ if (usbd_edpt_busy(ncm_interface.rhport, ncm_interface.ep_out)) {
+ return;
+ }
- uint16_t drv_len = sizeof(tusb_desc_interface_t);
- uint8_t const * p_desc = tu_desc_next( itf_desc );
+ ncm_interface.recv_tinyusb_ntb = recv_get_free_ntb();
+ if (ncm_interface.recv_tinyusb_ntb == NULL) {
+ return;
+ }
- // 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);
- }
+ // initiate transfer
+ DEBUG_OUT(" start reception\n");
+ bool r = usbd_edpt_xfer(0, ncm_interface.ep_out, ncm_interface.recv_tinyusb_ntb->data, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+ if ( !r) {
+ recv_put_ntb_into_free_list(ncm_interface.recv_tinyusb_ntb);
+ ncm_interface.recv_tinyusb_ntb = NULL;
+ }
+} // recv_try_to_start_new_reception
- // 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);
- }
+static bool recv_validate_datagram(const recv_ntb_t *ntb, uint16_t len)
+/**
+ * Validate incoming datagram.
+ * \return true if valid
+ *
+ * \note
+ * \a ndp16->wNextNdpIndex != 0 is not supported
+ */
+{
+ const nth16_t *nth16 = &(ntb->nth);
- //------------- 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);
+ DEBUG_OUT("recv_validate_datagram(%p)\n", ntb);
- 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);
+ //
+ // check header
+ //
+ if (nth16->wHeaderLength != sizeof(nth16_t))
+ {
+ ERROR_OUT(" ill nth16 length: %d\n", nth16->wHeaderLength);
+ return false;
+ }
+ if (nth16->dwSignature != NTH16_SIGNATURE) {
+ ERROR_OUT(" ill signature: 0x%08x\n", (unsigned)nth16->dwSignature);
+ return false;
+ }
+ if (len < sizeof(nth16_t) + sizeof(ndp16_t) + 2*sizeof(ndp16_datagram_t)) {
+ ERROR_OUT(" ill min len: %d\n", len);
+ return false;
+ }
+ if (nth16->wBlockLength > len) {
+ ERROR_OUT(" ill block length: %d > %d\n", nth16->wBlockLength, len);
+ return false;
+ }
+ if (nth16->wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+ ERROR_OUT(" ill block length2: %d > %d\n", nth16->wBlockLength, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+ return false;
+ }
+ if (nth16->wNdpIndex < sizeof(nth16) || nth16->wNdpIndex > len - (sizeof(ndp16_t) + 2*sizeof(ndp16_datagram_t))) {
+ ERROR_OUT(" ill position of first ndp: %d (%d)\n", nth16->wNdpIndex, len);
+ return false;
+ }
- 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));
+ //
+ // check (first) NDP(16)
+ //
+ const ndp16_t *ndp16 = (ndp16_t *)(ntb->data + nth16->wNdpIndex);
- // Pair of endpoints
- TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
+ if (ndp16->wLength < sizeof(ndp16_t) + 2*sizeof(ndp16_datagram_t)) {
+ ERROR_OUT(" ill ndp16 length: %d\n", ndp16->wLength);
+ return false;
+ }
+ if (ndp16->dwSignature != NDP16_SIGNATURE_NCM0 && ndp16->dwSignature != NDP16_SIGNATURE_NCM1) {
+ ERROR_OUT(" ill signature: 0x%08x\n", (unsigned)ndp16->dwSignature);
+ return false;
+ }
+ if (ndp16->wNextNdpIndex != 0) {
+ ERROR_OUT(" cannot handle wNextNdpIndex!=0 (%d)\n", ndp16->wNextNdpIndex);
+ return false;
+ }
- TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in) );
+ const ndp16_datagram_t *ndp16_datagram = (ndp16_datagram_t *)(ntb->data + nth16->wNdpIndex + sizeof(ndp16_t));
+ int ndx = 0;
+ int max_ndx = (ndp16->wLength - sizeof(ndp16_t)) / sizeof(ndp16_datagram_t);
- drv_len += 2*sizeof(tusb_desc_endpoint_t);
+ if (max_ndx > 2) {
+ // number of datagrams in NTB > 1
+ INFO_OUT("<< %d (%d)\n", max_ndx - 1, ntb->len);
+ }
+ if (ndp16_datagram[max_ndx-1].wDatagramIndex != 0 || ndp16_datagram[max_ndx-1].wDatagramLength != 0) {
+ INFO_OUT(" max_ndx != 0\n");
+ return false;
+ }
+ while (ndp16_datagram[ndx].wDatagramIndex != 0 && ndp16_datagram[ndx].wDatagramLength != 0) {
+ DEBUG_OUT(" << %d %d\n", ndp16_datagram[ndx].wDatagramIndex, ndp16_datagram[ndx].wDatagramLength);
+ if (ndp16_datagram[ndx].wDatagramIndex > len) {
+ ERROR_OUT(" ill start of datagram[%d]: %d (%d)\n", ndx, ndp16_datagram[ndx].wDatagramIndex, len);
+ return false;
+ }
+ if (ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength > len) {
+ ERROR_OUT(" ill end of datagram[%d]: %d (%d)\n", ndx, ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength, len);
+ return false;
+ }
+ ++ndx;
+ }
+
+ for (int i = 0; i < len; ++i) {
+ DEBUG_OUT(" %02x", ntb->data[i]);
+ }
+ DEBUG_OUT("\n");
+
+ // -> ntb contains a valid packet structure
+ // ok... I did not check for garbage within the datagram indices...
+ return true;
+} // recv_validate_datagram
- return drv_len;
-}
-static void ncm_report(void)
+
+static void recv_transfer_datagram_to_glue_logic(void)
+/**
+ * Transfer the next (pending) datagram to the glue logic and return receive buffer if empty.
+ */
{
- uint8_t const rhport = 0;
- if (ncm_interface.report_state == REPORT_SPEED) {
- ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
- usbd_edpt_xfer(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(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;
- }
-}
+ DEBUG_OUT("recv_transfer_datagram_to_glue_logic()\n");
+
+ if (ncm_interface.recv_glue_ntb == NULL) {
+ ncm_interface.recv_glue_ntb = recv_get_next_ready_ntb();
+ DEBUG_OUT(" new buffer for glue logic: %p\n", ncm_interface.recv_glue_ntb);
+ ncm_interface.recv_glue_ntb_datagram_ndx = 0;
+ }
+
+ if (ncm_interface.recv_glue_ntb != NULL) {
+ const ndp16_datagram_t *ndp16_datagram = (ndp16_datagram_t *)(ncm_interface.recv_glue_ntb->data
+ + ncm_interface.recv_glue_ntb->nth.wNdpIndex
+ + sizeof(ndp16_t));
+
+ if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex == 0) {
+ ERROR_OUT(" SOMETHING WENT WRONG 1\n");
+ }
+ else if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength == 0) {
+ ERROR_OUT(" SOMETHING WENT WRONG 2\n");
+ }
+ else {
+ uint16_t datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex;
+ uint16_t datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength;
+
+ DEBUG_OUT(" recv[%d] - %d %d\n", ncm_interface.recv_glue_ntb_datagram_ndx, datagramIndex, datagramLength);
+ if (tud_network_recv_cb(ncm_interface.recv_glue_ntb->data + datagramIndex, datagramLength)) {
+ //
+ // send datagram successfully to glue logic
+ //
+ DEBUG_OUT(" OK\n");
+ datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramIndex;
+ datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramLength;
+
+ if (datagramIndex != 0 && datagramLength != 0) {
+ // -> next datagram
+ ++ncm_interface.recv_glue_ntb_datagram_ndx;
+ }
+ else {
+ // end of datagrams reached
+ recv_put_ntb_into_free_list(ncm_interface.recv_glue_ntb);
+ ncm_interface.recv_glue_ntb = NULL;
+ }
+ }
+ }
+ }
+} // recv_transfer_datagram_to_glue_logic
+
+
+//-----------------------------------------------------------------------------
+//
+// all the tud_network_*() stuff (glue logic -> driver)
+//
-TU_ATTR_WEAK void tud_network_link_state_cb(bool state)
+
+bool tud_network_can_xmit(uint16_t size)
+/**
+ * Check if the glue logic is allowed to call tud_network_xmit().
+ * This function also fetches a next buffer if required, so that tud_network_xmit() is ready for copy
+ * and transmission operation.
+ */
{
- (void)state;
-}
+ DEBUG_OUT("tud_network_can_xmit(%d)\n", size);
+
+ TU_ASSERT(size <= CFG_TUD_NCM_OUT_NTB_MAX_SIZE - (sizeof(nth16_t) + sizeof(ndp16_t) + 2*sizeof(ndp16_datagram_t)), false);
+
+ if (xmit_requested_datagram_fits_into_current_ntb(size) || xmit_setup_next_glue_ntb()) {
+ // -> everything is fine
+ return true;
+ }
+ xmit_start_if_possible(ncm_interface.rhport);
+ ERROR_OUT(" tud_network_can_xmit: request blocked\n"); // could happen if all xmit buffers are full (but should happen rarely)
+ return false;
+} // tud_network_can_xmit
-// 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)
+
+
+void tud_network_xmit(void *ref, uint16_t arg)
+/**
+ * Put a datagram into a waiting NTB.
+ * If currently no transmission is started, then initiate transmission.
+ */
{
- if ( stage != CONTROL_STAGE_SETUP ) return true;
+ DEBUG_OUT("tud_network_xmit(%p, %d)\n", ref, arg);
- 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);
+ if (ncm_interface.xmit_glue_ntb == NULL) {
+ ERROR_OUT("tud_network_xmit: no buffer\n"); // must not happen (really)
+ return;
+ }
- tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
- }
- break;
+ xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
- case TUSB_REQ_SET_INTERFACE:
- {
- uint8_t const req_itfnum = (uint8_t) request->wIndex;
- uint8_t const req_alt = (uint8_t) request->wValue;
+ // copy new datagram to the end of the current NTB
+ uint16_t size = tud_network_xmit_cb(ntb->data + ntb->nth.wBlockLength, ref, arg);
- // Only valid for Data Interface with Alternate is either 0 or 1
- TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2);
+ // correct NTB internals
+ ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramIndex = ntb->nth.wBlockLength;
+ ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramLength = size;
+ ncm_interface.xmit_glue_ntb_datagram_ndx += 1;
- if (req_alt != ncm_interface.itf_data_alt) {
- ncm_interface.itf_data_alt = req_alt;
+ ntb->nth.wBlockLength += size + XMIT_ALIGN_OFFSET(size);
- 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();
- }
- }
+ if (ntb->nth.wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+ ERROR_OUT("tud_network_xmit: buffer overflow\n"); // must not happen (really)
+ return;
+ }
- tud_network_link_state_cb(ncm_interface.itf_data_alt);
- }
+ xmit_start_if_possible(ncm_interface.rhport);
+} // tud_network_xmit
- 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);
+void tud_network_recv_renew(void)
+/**
+ * Keep the receive logic busy and transfer pending packets to the glue logic.
+ */
+{
+ DEBUG_OUT("tud_network_recv_renew()\n");
- if (NCM_GET_NTB_PARAMETERS == request->bRequest)
- {
- tud_control_xfer(rhport, request, (void*)(uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
- }
+ recv_transfer_datagram_to_glue_logic();
+ recv_try_to_start_new_reception(ncm_interface.rhport);
+} // tud_network_recv_renew
- break;
- // unsupported request
- default: return false;
- }
- return true;
-}
+void tud_network_recv_renew_r(uint8_t rhport)
+/**
+ * Same as tud_network_recv_renew() but knows \a rhport
+ */
+{
+ DEBUG_OUT("tud_network_recv_renew_r(%d)\n", rhport);
+
+ ncm_interface.rhport = rhport;
+ tud_network_recv_renew();
+} // tud_network_recv_renew
+
-static void handle_incoming_datagram(uint32_t len)
+//-----------------------------------------------------------------------------
+//
+// all the netd_*() stuff (interface TinyUSB -> driver)
+//
+void netd_init(void)
+/**
+ * Initialize the driver data structures.
+ * Might be called several times.
+ */
{
- uint32_t size = len;
+ DEBUG_OUT("netd_init()\n");
- if (len == 0) {
- return;
- }
+ memset( &ncm_interface, 0, sizeof(ncm_interface));
- TU_ASSERT(size >= sizeof(nth16_t), );
+ for (int i = 0; i < XMIT_NTB_N; ++i) {
+ ncm_interface.xmit_free_ntb[i] = ncm_interface.xmit_ntb + i;
+ }
+ for (int i = 0; i < RECV_NTB_N; ++i) {
+ ncm_interface.recv_free_ntb[i] = ncm_interface.recv_ntb + i;
+ }
+} // netd_init
- 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++;
- }
+void netd_reset(uint8_t rhport)
+/**
+ * Resets the port.
+ * In this driver this is the same as netd_init()
+ */
+{
+ DEBUG_OUT("netd_reset(%d)\n", rhport);
- tud_network_recv_renew();
-}
+ netd_init();
+} // netd_reset
-bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+
+
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
+/**
+ * Open the USB interface.
+ * - parse the USB descriptor \a TUD_CDC_NCM_DESCRIPTOR for itfnum and endpoints
+ * - a specific order of elements in the descriptor is tested.
+ *
+ * \note
+ * Actually all of the information could be read directly from \a itf_desc, because the
+ * structure and the values are well known. But we do it this way.
+ *
+ * \post
+ * - \a itf_num set
+ * - \a ep_notif, \a ep_in and \a ep_out are set
+ * - USB interface is open
+ */
{
- (void) rhport;
- (void) result;
+ DEBUG_OUT("netd_open(%d,%p,%d)\n", rhport, itf_desc, max_len);
+
+ TU_ASSERT(ncm_interface.ep_notif == 0, 0); // assure that the interface is only opened once
- /* new datagram receive_ntb */
- if (ep_addr == ncm_interface.ep_out )
- {
- handle_incoming_datagram(xferred_bytes);
- }
+ ncm_interface.itf_num = itf_desc->bInterfaceNumber; // management interface
- /* data transmission finished */
- if (ep_addr == ncm_interface.ep_in )
- {
- if (ncm_interface.transferring) {
- ncm_interface.transferring = false;
+ //
+ // skip the two first entries and the following TUSB_DESC_CS_INTERFACE entries
+ //
+ uint16_t drv_len = sizeof(tusb_desc_interface_t);
+ uint8_t const *p_desc = tu_desc_next(itf_desc);
+ while (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && drv_len <= max_len) {
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- // 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();
+ //
+ // get notification endpoint
+ //
+ TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
+ 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);
+
+ //
+ // skip the following TUSB_DESC_INTERFACE entries (which must be TUSB_CLASS_CDC_DATA)
+ //
+ while (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && drv_len <= max_len) {
+ tusb_desc_interface_t const *data_itf_desc = (tusb_desc_interface_t const*)p_desc;
+ TU_ASSERT(data_itf_desc->bInterfaceClass == TUSB_CLASS_CDC_DATA, 0);
+
+ drv_len += tu_desc_len(p_desc);
+ p_desc = tu_desc_next(p_desc);
}
- }
- if (ep_addr == ncm_interface.ep_notif )
- {
- ncm_interface.report_pending = false;
- ncm_report();
- }
+ //
+ // a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints
+ //
+ TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 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 true;
-}
+ return drv_len;
+} // netd_open
-// poll network driver for its ability to accept another packet to transmit
-bool tud_network_can_xmit(uint16_t size)
+
+
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
+/**
+ * Handle TinyUSB requests to process transfer events.
+ */
{
- TU_VERIFY(ncm_interface.itf_data_alt == 1);
+ DEBUG_OUT("netd_xfer_cb(%d,%d,%d,%u)\n", rhport, ep_addr, result, (unsigned)xferred_bytes);
- if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) {
- TU_LOG_DRV("NTB full [by count]\r\n");
- return false;
- }
+ if (ep_addr == ncm_interface.ep_out) {
+ //
+ // new NTB received
+ // - make the NTB valid
+ // - if ready transfer datagrams to the glue logic for further processing
+ // - if there is a free receive buffer, initiate reception
+ //
+ DEBUG_OUT(" EP_OUT %d %d %d %u\n", rhport, ep_addr, result, (unsigned)xferred_bytes);
+ if ( !recv_validate_datagram( ncm_interface.recv_tinyusb_ntb, xferred_bytes)) {
+ // verification failed: ignore NTB and return it to free
+ ERROR_OUT(" VALIDATION FAILED. WHAT CAN WE DO IN THIS CASE?\n");
+ }
+ else {
+ // packet ok -> put it into ready list
+ recv_put_ntb_into_ready_list(ncm_interface.recv_tinyusb_ntb);
+ }
+ ncm_interface.recv_tinyusb_ntb = NULL;
+ tud_network_recv_renew_r(rhport);
+ }
+ else if (ep_addr == ncm_interface.ep_in) {
+ //
+ // transmission of an NTB finished
+ // - free the transmitted NTB buffer
+ // - insert ZLPs when necessary
+ // - if there is another transmit NTB waiting, try to start transmission
+ //
+ DEBUG_OUT(" EP_IN %d %u\n", ncm_interface.itf_data_alt, (unsigned)xferred_bytes);
+ xmit_put_ntb_into_free_list(ncm_interface.xmit_tinyusb_ntb);
+ ncm_interface.xmit_tinyusb_ntb = NULL;
+ if ( !xmit_insert_required_zlp(rhport, xferred_bytes)) {
+ xmit_start_if_possible(rhport);
+ }
+ }
+ else if (ep_addr == ncm_interface.ep_notif) {
+ //
+ // next transfer on notification channel
+ //
+ DEBUG_OUT(" EP_NOTIF\n");
+ notification_xmit(rhport, true);
+ }
- size_t next_datagram_offset = ncm_interface.next_datagram_offset;
- if (next_datagram_offset + size > ncm_interface.ntb_in_size) {
- TU_LOG_DRV("ntb full [by size]\r\n");
- return false;
- }
+ return true;
+} // netd_xfer_cb
- return true;
-}
-void tud_network_xmit(void *ref, uint16_t arg)
+
+bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
+/**
+ * Respond to TinyUSB control requests.
+ * At startup transmission of notification packets are done here.
+ */
{
- transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
- size_t next_datagram_offset = ncm_interface.next_datagram_offset;
+ DEBUG_OUT("netd_control_xfer_cb(%d, %d, %p)\n", rhport, stage, request);
- uint16_t size = tud_network_xmit_cb(ntb->data + next_datagram_offset, ref, arg);
+ if (stage != CONTROL_STAGE_SETUP) {
+ return true;
+ }
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = ncm_interface.next_datagram_offset;
- ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = size;
+ switch (request->bmRequestType_bit.type) {
+ case TUSB_REQ_TYPE_STANDARD:
+ switch (request->bRequest) {
+ case TUSB_REQ_GET_INTERFACE: {
+ TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex, false);
- ncm_interface.datagram_count++;
- next_datagram_offset += size;
+ DEBUG_OUT(" TUSB_REQ_GET_INTERFACE - %d\n", ncm_interface.itf_data_alt);
+ tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
+ }
+ break;
- // 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);
+ case TUSB_REQ_SET_INTERFACE: {
+ TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex && request->wValue < 2, false);
- ncm_interface.next_datagram_offset = next_datagram_offset;
+ ncm_interface.itf_data_alt = request->wValue;
+ DEBUG_OUT(" TUSB_REQ_SET_INTERFACE - %d %d %d\n", ncm_interface.itf_data_alt, request->wIndex, ncm_interface.itf_num);
- ncm_start_tx();
-}
+ if (ncm_interface.itf_data_alt == 1) {
+ tud_network_recv_renew_r(rhport);
+ notification_xmit(rhport, false);
+ }
+ tud_control_status(rhport, request);
+ }
+ break;
-#endif
+ // unsupported request
+ default:
+ return false;
+ }
+ break;
+
+ case TUSB_REQ_TYPE_CLASS:
+ TU_VERIFY(ncm_interface.itf_num == request->wIndex, false);
+
+ DEBUG_OUT(" TUSB_REQ_TYPE_CLASS: %d\n", request->bRequest);
+
+ if (request->bRequest == NCM_GET_NTB_PARAMETERS) {
+ // transfer NTB parameters to host.
+ // TODO can one assume, that tud_control_xfer() succeeds?
+ DEBUG_OUT(" NCM_GET_NTB_PARAMETERS\n");
+ tud_control_xfer(rhport, request, (void*) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
+ }
+ break;
+
+ // unsupported request
+ default:
+ return false ;
+ }
+
+ return true;
+} // netd_control_xfer_cb
+
+#endif // ECLIPSE_GUI || ( CFG_TUD_ENABLED && CFG_TUD_NCM )
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index 399916355..111c822fb 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -28,14 +28,13 @@
#ifndef _TUSB_NET_DEVICE_H_
#define _TUSB_NET_DEVICE_H_
+#include <stdint.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)
@@ -44,21 +43,6 @@
#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" {
@@ -92,12 +76,6 @@ uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg);
// 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 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);