Explorar o código

Merge pull request #345 from hathach/add-alt-itf

Implement setInterface(alt) for usb net driver
Ha Thach %!s(int64=5) %!d(string=hai) anos
pai
achega
bfec3b4479

+ 33 - 22
examples/device/net_lwip_webserver/src/usb_descriptors.c

@@ -24,7 +24,6 @@
  */
 
 #include "tusb.h"
-#include "usb_descriptors.h"
 
 /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
  * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
@@ -47,6 +46,20 @@ enum
   STRID_MAC
 };
 
+enum
+{
+  ITF_NUM_CDC = 0,
+  ITF_NUM_CDC_DATA,
+  ITF_NUM_TOTAL
+};
+
+enum
+{
+  CONFIG_ID_RNDIS = 0,
+  CONFIG_ID_ECM   = 1,
+  CONFIG_ID_COUNT
+};
+
 //--------------------------------------------------------------------+
 // Device Descriptors
 //--------------------------------------------------------------------+
@@ -71,7 +84,7 @@ tusb_desc_device_t const desc_device =
     .iProduct           = STRID_PRODUCT,
     .iSerialNumber      = STRID_SERIAL,
 
-    .bNumConfigurations = 0x02
+    .bNumConfigurations = CONFIG_ID_COUNT // multiple configurations
 };
 
 // Invoked when received GET DEVICE DESCRIPTOR
@@ -84,19 +97,6 @@ uint8_t const * tud_descriptor_device_cb(void)
 //--------------------------------------------------------------------+
 // Configuration Descriptor
 //--------------------------------------------------------------------+
-enum
-{
-  ITF_NUM_CDC = 0,
-  ITF_NUM_CDC_DATA,
-  ITF_NUM_TOTAL
-};
-
-enum
-{
-  CONFIG_NUM_DEFAULT = 1,
-  CONFIG_NUM_ALTERNATE = 2,
-};
-
 #define MAIN_CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN)
 #define ALT_CONFIG_TOTAL_LEN     (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
 
@@ -108,30 +108,41 @@ enum
   #define EPNUM_CDC     2
 #endif
 
-static uint8_t const main_configuration[] =
+static uint8_t const rndis_configuration[] =
 {
-  // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(CONFIG_NUM_DEFAULT, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
+  // Config number (index+1), interface count, string index, total length, attribute, power in mA
+  TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS+1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
 
   // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
   TUD_RNDIS_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, 0x81, 8, EPNUM_CDC, 0x80 | EPNUM_CDC, CFG_TUD_NET_ENDPOINT_SIZE),
 };
 
-static uint8_t const alt_configuration[] =
+static uint8_t const ecm_configuration[] =
 {
-  // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(CONFIG_NUM_ALTERNATE, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
+  // Config number (index+1), interface count, string index, total length, attribute, power in mA
+  TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM+1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
 
   // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
   TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, 0x81, 64, EPNUM_CDC, 0x80 | EPNUM_CDC, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
 };
 
+// Configuration array: RNDIS and CDC-ECM
+// - Windows only works with RNDIS
+// - MacOS only works with CDC-ECM
+// - Linux will work on both
+// Note index is Num-1x
+static uint8_t const * const configuration_arr[2] =
+{
+  [CONFIG_ID_RNDIS] = rndis_configuration,
+  [CONFIG_ID_ECM  ] = ecm_configuration
+};
+
 // Invoked when received GET CONFIGURATION DESCRIPTOR
 // Application return pointer to descriptor
 // Descriptor contents must exist long enough for transfer to complete
 uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
 {
-  return (0 == index) ? main_configuration : alt_configuration;
+  return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL;
 }
 
 //--------------------------------------------------------------------+

+ 0 - 28
examples/device/net_lwip_webserver/src/usb_descriptors.h

@@ -1,28 +0,0 @@
-/* 
- * 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.
- */
-
-#ifndef USB_DESCRIPTORS_H_
-#define USB_DESCRIPTORS_H_
-
-#endif /* USB_DESCRIPTORS_H_ */

+ 135 - 50
src/class/net/net_device.c

@@ -40,11 +40,20 @@ void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networ
 //--------------------------------------------------------------------+
 typedef struct
 {
-  uint8_t itf_num;
+  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;
-  bool ecm_mode;
   uint8_t ep_in;
   uint8_t ep_out;
+
+  bool ecm_mode;
+
+  // Endpoint descriptor use to open/close when receving SetInterface
+  // TODO since configuration descriptor may not be long-lived memory, we should
+  // keep a copy of endpoint attribute instead
+  uint8_t const * ecm_desc_epdata;
+
 } netd_interface_t;
 
 #define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
@@ -145,8 +154,8 @@ bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
   //------------- Management Interface -------------//
   _netd_itf.itf_num = itf_desc->bInterfaceNumber;
 
-  uint8_t const * p_desc = tu_desc_next( itf_desc );
   (*p_length) = 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) )
@@ -167,31 +176,44 @@ bool netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t
   }
 
   //------------- Data Interface -------------//
-  // TODO extract Alt Interface 0 & 1
-  while ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
-         (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
+  // - RNDIS Data followed immediately by a pair of endpoints
+  // - CDC-ECM data interface has 2 alternate settings
+  //   - 0 : zero endpoints for inactive (default)
+  //   - 1 : IN & OUT endpoints for active networking
+  TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc));
+
+  do
   {
-    // next to endpoint descriptor
+    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);
+
     (*p_length) += tu_desc_len(p_desc);
     p_desc = tu_desc_next(p_desc);
-  }
+  }while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) );
+
+  // Pair of endpoints
+  TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc));
 
-  if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc))
+  if ( _netd_itf.ecm_mode )
   {
-    // Open endpoint pair
+    // ECM by default is in-active, save the endpoint attribute
+    // to open later when received setInterface
+    _netd_itf.ecm_desc_epdata = p_desc;
+  }else
+  {
+    // Open endpoint pair for RNDIS
     TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
 
-    (*p_length) += 2*sizeof(tusb_desc_endpoint_t);
-  }
-
-  tud_network_init_cb();
+    tud_network_init_cb();
 
-  // we are ready to transmit a packet
-  can_xmit = true;
+    // we are ready to transmit a packet
+    can_xmit = true;
 
-  // prepare for incoming packets
-  tud_network_recv_renew();
+    // prepare for incoming packets
+    tud_network_recv_renew();
+  }
 
+  (*p_length) += 2*sizeof(tusb_desc_endpoint_t);
 
   return true;
 }
@@ -202,14 +224,15 @@ bool netd_control_complete(uint8_t rhport, tusb_control_request_t const * reques
 {
   (void) rhport;
 
-  // Handle class request only
-  TU_VERIFY (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
-
-  TU_VERIFY (_netd_itf.itf_num == request->wIndex);
-
-  if ( !_netd_itf.ecm_mode && (request->bmRequestType_bit.direction == TUSB_DIR_OUT) )
+  // Handle RNDIS class control OUT only
+  if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
+      request->bmRequestType_bit.direction == TUSB_DIR_OUT   &&
+      _netd_itf.itf_num == request->wIndex)
   {
-    rndis_class_set_handler(notify.rndis_buf, request->wLength);
+    if ( !_netd_itf.ecm_mode )
+    {
+      rndis_class_set_handler(notify.rndis_buf, request->wLength);
+    }
   }
 
   return true;
@@ -226,33 +249,95 @@ static void ecm_report(bool nc)
 // return false to stall control endpoint (e.g unsupported request)
 bool netd_control_request(uint8_t rhport, tusb_control_request_t const * request)
 {
-  // Handle class request only
-  TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
+  switch ( request->bmRequestType_bit.type )
+  {
+    case TUSB_REQ_TYPE_STANDARD:
+      switch ( request->bRequest )
+      {
+        case TUSB_REQ_GET_INTERFACE:
+        {
+          uint8_t const req_itfnum = (uint8_t) request->wIndex;
+          TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum);
+
+          tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1);
+        }
+        break;
 
-  TU_VERIFY (_netd_itf.itf_num == request->wIndex);
+        case TUSB_REQ_SET_INTERFACE:
+        {
+          uint8_t const req_itfnum = (uint8_t) request->wIndex;
+          uint8_t const req_alt    = (uint8_t) request->wValue;
+
+          // Only valid for Data Interface with Alternate is either 0 or 1
+          TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2);
+
+          // ACM-ECM only: qequest to enable/disable network activities
+          TU_VERIFY(_netd_itf.ecm_mode);
+
+          _netd_itf.itf_data_alt = req_alt;
+
+          if ( _netd_itf.itf_data_alt )
+          {
+            // TODO since we don't actually close endpoint
+            // hack here to not re-open it
+            if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 )
+            {
+              TU_ASSERT(_netd_itf.ecm_desc_epdata);
+              TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
+
+              // TODO should be merge with RNDIS's after endpoint opened
+              // Also should have opposite callback for application to disable network !!
+              tud_network_init_cb();
+              can_xmit = true; // we are ready to transmit a packet
+              tud_network_recv_renew(); // prepare for incoming packets
+            }
+          }else
+          {
+            // TODO close the endpoint pair
+            // For now pretend that we did, this should have no harm since host won't try to
+            // communicate with the endpoints again
+            // _netd_itf.ep_in = _netd_itf.ep_out = 0
+          }
+
+          tud_control_status(rhport, request);
+        }
+        break;
 
-  if (_netd_itf.ecm_mode)
-  {
-    /* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
-    if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
-    {
-      tud_control_xfer(rhport, request, NULL, 0);
-      ecm_report(true);
-    }
-  }
-  else
-  {
-    if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
-    {
-      rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *)notify.rndis_buf;
-      uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
-      TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
-      tud_control_xfer(rhport, request, notify.rndis_buf, msglen);
-    }
-    else
-    {
-      tud_control_xfer(rhport, request, notify.rndis_buf, sizeof(notify.rndis_buf));
-    }
+        // unsupported request
+        default: return false;
+      }
+    break;
+
+    case TUSB_REQ_TYPE_CLASS:
+      TU_VERIFY (_netd_itf.itf_num == request->wIndex);
+
+      if (_netd_itf.ecm_mode)
+      {
+        /* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
+        if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
+        {
+          tud_control_xfer(rhport, request, NULL, 0);
+          ecm_report(true);
+        }
+      }
+      else
+      {
+        if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
+        {
+          rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *)notify.rndis_buf;
+          uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
+          TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
+          tud_control_xfer(rhport, request, notify.rndis_buf, msglen);
+        }
+        else
+        {
+          tud_control_xfer(rhport, request, notify.rndis_buf, sizeof(notify.rndis_buf));
+        }
+      }
+    break;
+
+    // unsupported request
+    default: return false;
   }
 
   return true;

+ 9 - 31
src/device/usbd.c

@@ -571,39 +571,17 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
       uint8_t const drvid = _usbd_dev.itf2drv[itf];
       TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT);
 
-      if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
+      // all requests to Interface (STD or Class) is forwarded to class driver.
+      // notable requests are: GET HID REPORT DESCRIPTOR, SET_INTERFACE, GET_INTERFACE
+      if ( !invoke_class_control(rhport, drvid, p_request) )
       {
-        switch ( p_request->bRequest )
-        {
-          case TUSB_REQ_GET_INTERFACE:
-          {
-            // TODO not support alternate interface yet
-            uint8_t alternate = 0;
-            tud_control_xfer(rhport, p_request, &alternate, 1);
-          }
-          break;
-
-          case TUSB_REQ_SET_INTERFACE:
-          {
-            uint8_t const alternate = (uint8_t) p_request->wValue;
-            (void) alternate;
+        // For GET_INTERFACE, it is mandatory to respond even if the class
+        // driver doesn't use alternate settings.
+        TU_VERIFY( TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type &&
+                   TUSB_REQ_GET_INTERFACE == p_request->bRequest);
 
-            // TODO not support alternate interface yet
-//            TU_ASSERT(alternate == 0);
-            tud_control_status(rhport, p_request);
-          }
-          break;
-
-          default:
-            // forward to class driver: "STD request to Interface"
-            // GET HID REPORT DESCRIPTOR falls into this case
-            TU_VERIFY(invoke_class_control(rhport, drvid, p_request));
-          break;
-        }
-      }else
-      {
-        // forward to class driver: "non-STD request to Interface"
-        TU_VERIFY(invoke_class_control(rhport, drvid, p_request));
+        uint8_t alternate = 0;
+        tud_control_xfer(rhport, p_request, &alternate, 1);
       }
     }
     break;