Add CDC NCM driver
This commit is contained in:
		@@ -69,7 +69,8 @@ typedef enum
 | 
			
		||||
  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_ETHERNET_EMULATION_MODEL          , ///< Ethernet Emulation Model  [USBEEM1.0]
 | 
			
		||||
  CDC_COMM_SUBCLASS_NETWORK_CONTROL_MODEL               ///< Network Control Model  [USBNCM1.0]
 | 
			
		||||
} cdc_comm_sublcass_type_t;
 | 
			
		||||
 | 
			
		||||
/// Communication Interface Protocol Codes
 | 
			
		||||
@@ -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;
 | 
			
		||||
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										493
									
								
								src/class/cdc/cdc_ncm_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										493
									
								
								src/class/cdc/cdc_ncm_device.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,493 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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_pvt.h"
 | 
			
		||||
#include "cdc_device.h"
 | 
			
		||||
#include "cdc_ncm_device.h"
 | 
			
		||||
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
// MACRO CONSTANT TYPEDEF
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
 | 
			
		||||
#define NTH16_SIGNATURE 0x484D434E
 | 
			
		||||
#define NDP16_SIGNATURE_NCM0 0x304D434E
 | 
			
		||||
#define NDP16_SIGNATURE_NCM1 0x314D434E
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
  // Endpoint descriptor use to open/close when receving SetInterface
 | 
			
		||||
  // TODO since configuration descriptor may not be long-lived memory, we should
 | 
			
		||||
  // keep a copy of endpoint attribute instead
 | 
			
		||||
  uint8_t const * ecm_desc_epdata;
 | 
			
		||||
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
// 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() {
 | 
			
		||||
  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() {
 | 
			
		||||
  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 = 0xA1,
 | 
			
		||||
            .bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
 | 
			
		||||
            .wValue = 1 /* Connected */,
 | 
			
		||||
            .wLength = 0,
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
static struct ecm_notify_struct ncm_notify_speed_change =
 | 
			
		||||
    {
 | 
			
		||||
        .header = {
 | 
			
		||||
            .bmRequestType = 0xA1,
 | 
			
		||||
            .bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
 | 
			
		||||
            .wLength = 8,
 | 
			
		||||
        },
 | 
			
		||||
        .downlink = 10000000,
 | 
			
		||||
        .uplink = 10000000,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
void ncm_receive_renew(void)
 | 
			
		||||
{
 | 
			
		||||
  usbd_edpt_xfer(TUD_OPT_RHPORT, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
// USBD Driver API
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
 | 
			
		||||
void ncmd_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 ncmd_reset(uint8_t rhport)
 | 
			
		||||
{
 | 
			
		||||
  (void) rhport;
 | 
			
		||||
 | 
			
		||||
  ncmd_init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint16_t ncmd_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);
 | 
			
		||||
 | 
			
		||||
  // ECM by default is in-active, save the endpoint attribute
 | 
			
		||||
  // to open later when receive_ntb setInterface
 | 
			
		||||
  ncm_interface.ecm_desc_epdata = p_desc;
 | 
			
		||||
 | 
			
		||||
  drv_len += 2*sizeof(tusb_desc_endpoint_t);
 | 
			
		||||
 | 
			
		||||
  return drv_len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Invoked when class request DATA stage is finished.
 | 
			
		||||
bool ncmd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
 | 
			
		||||
{
 | 
			
		||||
  (void) rhport;
 | 
			
		||||
  (void) request;
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ncm_report()
 | 
			
		||||
{
 | 
			
		||||
  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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Handle class control request
 | 
			
		||||
// return false to stall control endpoint (e.g unsupported request)
 | 
			
		||||
bool ncmd_control_request(uint8_t rhport, tusb_control_request_t const * request)
 | 
			
		||||
{
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
          ncm_interface.itf_data_alt = req_alt;
 | 
			
		||||
 | 
			
		||||
          if ( ncm_interface.itf_data_alt )
 | 
			
		||||
          {
 | 
			
		||||
            // TODO since we don't actually close endpoint
 | 
			
		||||
            // hack here to not re-open it
 | 
			
		||||
            if (ncm_interface.ep_in == 0 && ncm_interface.ep_out == 0)
 | 
			
		||||
            {
 | 
			
		||||
              TU_ASSERT(ncm_interface.ecm_desc_epdata);
 | 
			
		||||
              TU_ASSERT( usbd_open_edpt_pair(rhport, ncm_interface.ecm_desc_epdata, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in) );
 | 
			
		||||
 | 
			
		||||
              // TODO set flag indicating tx allowed?
 | 
			
		||||
              ncm_receive_renew(); // prepare for incoming datagrams
 | 
			
		||||
              tud_ncm_link_state_cb(true);
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            // TODO close the endpoint pair
 | 
			
		||||
            // For now pretend that we did, this should have no harm since host won't try to
 | 
			
		||||
            // communicate with the endpoints again
 | 
			
		||||
            // ncm_interface.ep_in = ncm_interface.ep_out = 0
 | 
			
		||||
            // ncm_interface.report_pending = 0;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          tud_control_status(rhport, request);
 | 
			
		||||
 | 
			
		||||
          if (!ncm_interface.report_pending) ncm_report();
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
          // unsupported request
 | 
			
		||||
        default: return false;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
 | 
			
		||||
    case TUSB_REQ_TYPE_CLASS:
 | 
			
		||||
      TU_VERIFY (ncm_interface.itf_num == request->wIndex);
 | 
			
		||||
 | 
			
		||||
      if (0x80 /* 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;
 | 
			
		||||
  for (int i = 0; i < num_datagrams && ndp->datagram[i].wDatagramIndex && ndp->datagram[i].wDatagramLength; i++) {
 | 
			
		||||
    tud_ncm_receive_cb(receive_ntb + ndp->datagram[i].wDatagramIndex, ndp->datagram[i].wDatagramLength);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ncm_receive_renew();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ncmd_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_start_tx();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (ep_addr == ncm_interface.ep_notif )
 | 
			
		||||
  {
 | 
			
		||||
    ncm_interface.report_pending = false;
 | 
			
		||||
    ncm_report();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool tud_ncm_xmit(void *arg, uint16_t size, void (*flatten)(void *, uint8_t *, uint16_t)) {
 | 
			
		||||
  transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
 | 
			
		||||
 | 
			
		||||
  if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  size_t next_datagram_offset = ncm_interface.next_datagram_offset;
 | 
			
		||||
  if (next_datagram_offset + size > ncm_interface.ntb_in_size) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  flatten(arg, ntb->data + next_datagram_offset, size);
 | 
			
		||||
 | 
			
		||||
  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();
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										88
									
								
								src/class/cdc/cdc_ncm_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/class/cdc/cdc_ncm_device.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
/* 
 | 
			
		||||
 * The MIT License (MIT)
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2019 Ha Thach (tinyusb.org)
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
 * in the Software without restriction, including without limitation the rights
 | 
			
		||||
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
 * copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
 * THE SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of the TinyUSB stack.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _TUSB_CDC_NCM_DEVICE_H_
 | 
			
		||||
#define _TUSB_CDC_NCM_DEVICE_H_
 | 
			
		||||
 | 
			
		||||
#include "common/tusb_common.h"
 | 
			
		||||
#include "device/usbd.h"
 | 
			
		||||
#include "cdc.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
 extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifndef CFG_TUD_NCM_EP_BUFSIZE
 | 
			
		||||
#define CFG_TUD_NCM_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Transmit data. Returns true if successful, false if the packet was rejected (transmit buffer full,
 | 
			
		||||
 * interface not up).
 | 
			
		||||
 *
 | 
			
		||||
 * This is structured with a callback to avoid the need for multiple copies; some network stacks,
 | 
			
		||||
 * including LwIP, allow for non-contiguous buffers and provide a function to flatten them. In the
 | 
			
		||||
 * LwIP case, a thin wrapper around pbuf_copy_partial() can be passed here.
 | 
			
		||||
 */
 | 
			
		||||
bool tud_ncm_xmit(void *arg, uint16_t size, void (*flatten)(void *arg, uint8_t *buffer, uint16_t size));
 | 
			
		||||
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
// Application Callback API
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
void tud_ncm_receive_cb(uint8_t *data, size_t length);
 | 
			
		||||
void tud_ncm_link_state_cb(bool state);
 | 
			
		||||
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
// INTERNAL USBD-CLASS DRIVER API
 | 
			
		||||
//--------------------------------------------------------------------+
 | 
			
		||||
void     ncmd_init             (void);
 | 
			
		||||
void     ncmd_reset            (uint8_t rhport);
 | 
			
		||||
uint16_t ncmd_open             (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
 | 
			
		||||
bool     ncmd_control_request  (uint8_t rhport, tusb_control_request_t const * request);
 | 
			
		||||
bool     ncmd_control_complete (uint8_t rhport, tusb_control_request_t const * request);
 | 
			
		||||
bool     ncmd_xfer_cb          (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
 | 
			
		||||
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#endif /* _TUSB_CDC_NCM_DEVICE_H_ */
 | 
			
		||||
		Reference in New Issue
	
	Block a user