|
|
@@ -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
|