summaryrefslogtreecommitdiff
path: root/src/class
diff options
context:
space:
mode:
authorHa Thach <[email protected]>2021-10-11 15:57:46 +0700
committerGitHub <[email protected]>2021-10-11 15:57:46 +0700
commitecec0370cac2a64ef2a0cf2e4e13f1c4e738e81b (patch)
tree1b5be20ee4f09a1518051297a5f39f088ef69fad /src/class
parent9660a5821fd36af10235c400700ae7d2730ec55f (diff)
parenta6723f556d7b302137ca42c7f811d3484f25414a (diff)
Merge pull request #1107 from majbthrd/add_ncm
add NCM driver in a compatible manner : hathach/tinyusb#550
Diffstat (limited to 'src/class')
-rw-r--r--src/class/cdc/cdc.h71
-rw-r--r--src/class/net/ecm_rndis_device.c (renamed from src/class/net/net_device.c)8
-rw-r--r--src/class/net/ncm.h69
-rw-r--r--src/class/net/ncm_device.c510
-rw-r--r--src/class/net/net_device.h53
5 files changed, 663 insertions, 48 deletions
diff --git a/src/class/cdc/cdc.h b/src/class/cdc/cdc.h
index 5df47f70b..e345139ea 100644
--- a/src/class/cdc/cdc.h
+++ b/src/class/cdc/cdc.h
@@ -58,31 +58,32 @@ typedef enum
/// Communication Interface Subclass Codes
typedef enum
{
- CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2]
- CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL , ///< Abstract Control Model [USBPSTN1.2]
- CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL , ///< Telephone Control Model [USBPSTN1.2]
- CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL , ///< Multi-Channel Control Model [USBISDN1.2]
- CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL , ///< CAPI Control Model [USBISDN1.2]
- CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL , ///< Ethernet Networking Control Model [USBECM1.2]
- CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL , ///< ATM Networking Control Model [USBATM1.2]
- CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL , ///< Wireless Handset Control Model [USBWMC1.1]
- CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT , ///< Device Management [USBWMC1.1]
- CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL , ///< Mobile Direct Line Model [USBWMC1.1]
- CDC_COMM_SUBCLASS_OBEX , ///< OBEX [USBWMC1.1]
- CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model [USBEEM1.0]
+ CDC_COMM_SUBCLASS_DIRECT_LINE_CONTROL_MODEL = 0x01 , ///< Direct Line Control Model [USBPSTN1.2]
+ CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL = 0x02 , ///< Abstract Control Model [USBPSTN1.2]
+ CDC_COMM_SUBCLASS_TELEPHONE_CONTROL_MODEL = 0x03 , ///< Telephone Control Model [USBPSTN1.2]
+ CDC_COMM_SUBCLASS_MULTICHANNEL_CONTROL_MODEL = 0x04 , ///< Multi-Channel Control Model [USBISDN1.2]
+ CDC_COMM_SUBCLASS_CAPI_CONTROL_MODEL = 0x05 , ///< CAPI Control Model [USBISDN1.2]
+ CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL = 0x06 , ///< Ethernet Networking Control Model [USBECM1.2]
+ CDC_COMM_SUBCLASS_ATM_NETWORKING_CONTROL_MODEL = 0x07 , ///< ATM Networking Control Model [USBATM1.2]
+ CDC_COMM_SUBCLASS_WIRELESS_HANDSET_CONTROL_MODEL = 0x08 , ///< Wireless Handset Control Model [USBWMC1.1]
+ CDC_COMM_SUBCLASS_DEVICE_MANAGEMENT = 0x09 , ///< Device Management [USBWMC1.1]
+ CDC_COMM_SUBCLASS_MOBILE_DIRECT_LINE_MODEL = 0x0A , ///< Mobile Direct Line Model [USBWMC1.1]
+ CDC_COMM_SUBCLASS_OBEX = 0x0B , ///< OBEX [USBWMC1.1]
+ CDC_COMM_SUBCLASS_ETHERNET_EMULATION_MODEL = 0x0C , ///< Ethernet Emulation Model [USBEEM1.0]
+ CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL = 0x0D ///< Network Control Model [USBNCM1.0]
} cdc_comm_sublcass_type_t;
/// Communication Interface Protocol Codes
typedef enum
{
- CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol
- CDC_COMM_PROTOCOL_ATCOMMAND , ///< AT Commands: V.250 etc
- CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 , ///< AT Commands defined by PCCA-101
- CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO , ///< AT Commands defined by PCCA-101 & Annex O
- CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 , ///< AT Commands defined by GSM 07.07
- CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 , ///< AT Commands defined by 3GPP 27.007
- CDC_COMM_PROTOCOL_ATCOMMAND_CDMA , ///< AT Commands defined by TIA for CDMA
- CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL ///< Ethernet Emulation Model
+ CDC_COMM_PROTOCOL_NONE = 0x00 , ///< No specific protocol
+ CDC_COMM_PROTOCOL_ATCOMMAND = 0x01 , ///< AT Commands: V.250 etc
+ CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101 = 0x02 , ///< AT Commands defined by PCCA-101
+ CDC_COMM_PROTOCOL_ATCOMMAND_PCCA_101_AND_ANNEXO = 0x03 , ///< AT Commands defined by PCCA-101 & Annex O
+ CDC_COMM_PROTOCOL_ATCOMMAND_GSM_707 = 0x04 , ///< AT Commands defined by GSM 07.07
+ CDC_COMM_PROTOCOL_ATCOMMAND_3GPP_27007 = 0x05 , ///< AT Commands defined by 3GPP 27.007
+ CDC_COMM_PROTOCOL_ATCOMMAND_CDMA = 0x06 , ///< AT Commands defined by TIA for CDMA
+ CDC_COMM_PROTOCOL_ETHERNET_EMULATION_MODEL = 0x07 ///< Ethernet Emulation Model
} cdc_comm_protocol_type_t;
//------------- SubType Descriptor in COMM Functional Descriptor -------------//
@@ -114,7 +115,8 @@ typedef enum
CDC_FUNC_DESC_COMMAND_SET = 0x16 , ///< Command Set Functional Descriptor
CDC_FUNC_DESC_COMMAND_SET_DETAIL = 0x17 , ///< Command Set Detail Functional Descriptor
CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL = 0x18 , ///< Telephone Control Model Functional Descriptor
- CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 ///< OBEX Service Identifier Functional Descriptor
+ CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER = 0x19 , ///< OBEX Service Identifier Functional Descriptor
+ CDC_FUNC_DESC_NCM = 0x1A , ///< NCM Functional Descriptor
}cdc_func_desc_type_t;
//--------------------------------------------------------------------+
@@ -122,7 +124,8 @@ typedef enum
//--------------------------------------------------------------------+
// SUBCLASS code of Data Interface is not used and should/must be zero
-/// Data Interface Protocol Codes
+
+// Data Interface Protocol Codes
typedef enum{
CDC_DATA_PROTOCOL_ISDN_BRI = 0x30, ///< Physical interface protocol for ISDN BRI
CDC_DATA_PROTOCOL_HDLC = 0x31, ///< HDLC
@@ -147,7 +150,6 @@ typedef enum
{
CDC_REQUEST_SEND_ENCAPSULATED_COMMAND = 0x00, ///< is used to issue a command in the format of the supported control protocol of the Communications Class interface
CDC_REQUEST_GET_ENCAPSULATED_RESPONSE = 0x01, ///< is used to request a response in the format of the supported control protocol of the Communications Class interface.
-
CDC_REQUEST_SET_COMM_FEATURE = 0x02,
CDC_REQUEST_GET_COMM_FEATURE = 0x03,
CDC_REQUEST_CLEAR_COMM_FEATURE = 0x04,
@@ -194,21 +196,18 @@ typedef enum
// Management Elemenent Notification (Notification Endpoint)
//--------------------------------------------------------------------+
-/// Communication Interface Management Element Notification Codes
+/// 6.3 Notification Codes
typedef enum
{
- NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
- RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
-
- AUX_JACK_HOOK_STATE = 0x08,
- RING_DETECT = 0x09,
-
- SERIAL_STATE = 0x20,
-
- CALL_STATE_CHANGE = 0x28,
- LINE_STATE_CHANGE = 0x29,
- CONNECTION_SPEED_CHANGE = 0x2A, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred
- MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40,
+ CDC_NOTIF_NETWORK_CONNECTION = 0x00, ///< This notification allows the device to notify the host about network connection status.
+ CDC_NOTIF_RESPONSE_AVAILABLE = 0x01, ///< This notification allows the device to notify the hostthat a response is available. This response can be retrieved with a subsequent \ref CDC_REQUEST_GET_ENCAPSULATED_RESPONSE request.
+ CDC_NOTIF_AUX_JACK_HOOK_STATE = 0x08,
+ CDC_NOTIF_RING_DETECT = 0x09,
+ CDC_NOTIF_SERIAL_STATE = 0x20,
+ CDC_NOTIF_CALL_STATE_CHANGE = 0x28,
+ CDC_NOTIF_LINE_STATE_CHANGE = 0x29,
+ CDC_NOTIF_CONNECTION_SPEED_CHANGE = 0x2A, ///< This notification allows the device to inform the host-networking driver that a change in either the upstream or the downstream bit rate of the connection has occurred
+ CDC_NOTIF_MDLM_SEMANTIC_MODEL_NOTIFICATION = 0x40,
}cdc_notification_request_t;
//--------------------------------------------------------------------+
diff --git a/src/class/net/net_device.c b/src/class/net/ecm_rndis_device.c
index 9d9719ce4..c6cd388e3 100644
--- a/src/class/net/net_device.c
+++ b/src/class/net/ecm_rndis_device.c
@@ -27,7 +27,7 @@
#include "tusb_option.h"
-#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_NET )
+#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_ECM_RNDIS )
#include "device/usbd.h"
#include "device/usbd_pvt.h"
@@ -119,6 +119,8 @@ static void do_in_xfer(uint8_t *buf, uint16_t 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);
}
@@ -407,8 +409,10 @@ bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
return true;
}
-bool tud_network_can_xmit(void)
+bool tud_network_can_xmit(uint16_t size)
{
+ (void)size;
+
return can_xmit;
}
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
new file mode 100644
index 000000000..96ba11fbc
--- /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 000000000..3e131a85c
--- /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
index f030f3077..6e294465b 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -30,14 +30,36 @@
#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 Tranmission Unit (in bytes) of the network, including Ethernet header */
+/* 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
@@ -46,8 +68,18 @@
// Application API
//--------------------------------------------------------------------+
-// client must provide this: initialize any network state back to the beginning
-void tud_network_init_cb(void);
+// 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);
@@ -55,18 +87,19 @@ 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];
-// 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(void);
+//------------- NCM -------------//
-// if network_can_xmit() returns true, network_xmit() can be called once
-void tud_network_xmit(void *ref, uint16_t arg);
+// 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