diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index 7cacd0a43329e..a4658e978f457 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -51,6 +51,7 @@ set(MICROPY_SOURCE_EXTMOD ${MICROPY_EXTMOD_DIR}/network_lwip.c ${MICROPY_EXTMOD_DIR}/network_ninaw10.c ${MICROPY_EXTMOD_DIR}/network_ppp_lwip.c + ${MICROPY_EXTMOD_DIR}/network_usbd_ncm.c ${MICROPY_EXTMOD_DIR}/network_wiznet5k.c ${MICROPY_EXTMOD_DIR}/os_dupterm.c ${MICROPY_EXTMOD_DIR}/vfs.c diff --git a/extmod/extmod.mk b/extmod/extmod.mk index 997dd3ba98ead..b8a33c3986a45 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -54,6 +54,7 @@ SRC_EXTMOD_C += \ extmod/network_lwip.c \ extmod/network_ninaw10.c \ extmod/network_ppp_lwip.c \ + extmod/network_usbd_ncm.c \ extmod/network_wiznet5k.c \ extmod/os_dupterm.c \ extmod/vfs.c \ diff --git a/extmod/lwip-include/lwipopts_common.h b/extmod/lwip-include/lwipopts_common.h index 3e4230909499e..ccf74503c5742 100644 --- a/extmod/lwip-include/lwipopts_common.h +++ b/extmod/lwip-include/lwipopts_common.h @@ -54,9 +54,12 @@ #define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up #define LWIP_DNS 1 #define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 -#define LWIP_MDNS_RESPONDER 1 #define LWIP_IGMP 1 +#ifndef LWIP_MDNS_RESPONDER +#define LWIP_MDNS_RESPONDER 1 +#endif + #if MICROPY_PY_LWIP_PPP #define PPP_SUPPORT 1 #define PAP_SUPPORT 1 @@ -108,4 +111,8 @@ typedef uint32_t sys_prot_t; +// #define LWIP_DEBUG +// #define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL +// #define LWIP_DBG_TYPES_ON LWIP_DBG_ON + #endif // MICROPY_INCLUDED_LWIPOPTS_COMMON_H diff --git a/extmod/machine_usb_device.c b/extmod/machine_usb_device.c index 5019cd98779f5..b8bfefb30032b 100644 --- a/extmod/machine_usb_device.c +++ b/extmod/machine_usb_device.c @@ -229,6 +229,58 @@ static mp_obj_t usb_device_config(size_t n_args, const mp_obj_t *pos_args, mp_ma } static MP_DEFINE_CONST_FUN_OBJ_KW(usb_device_config_obj, 1, usb_device_config); +// Per-class control methods +static mp_obj_t usb_device_enable_cdc(size_t n_args, const mp_obj_t *args) { + mp_obj_usb_device_t *self = MP_OBJ_TO_PTR(args[0]); + + if (self->active) { + mp_raise_OSError(MP_EINVAL); + } + + if (n_args == 1) { + return mp_obj_new_bool(mp_usbd_class_state.cdc_enabled); + } else { + bool enable = mp_obj_is_true(args[1]); + mp_usbd_enable_class_cdc(enable); + return mp_const_none; + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_enable_cdc_obj, 1, 2, usb_device_enable_cdc); + +static mp_obj_t usb_device_enable_msc(size_t n_args, const mp_obj_t *args) { + mp_obj_usb_device_t *self = MP_OBJ_TO_PTR(args[0]); + + if (self->active) { + mp_raise_OSError(MP_EINVAL); + } + + if (n_args == 1) { + return mp_obj_new_bool(mp_usbd_class_state.msc_enabled); + } else { + bool enable = mp_obj_is_true(args[1]); + mp_usbd_enable_class_msc(enable); + return mp_const_none; + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_enable_msc_obj, 1, 2, usb_device_enable_msc); + +static mp_obj_t usb_device_enable_ncm(size_t n_args, const mp_obj_t *args) { + mp_obj_usb_device_t *self = MP_OBJ_TO_PTR(args[0]); + + if (self->active) { + mp_raise_OSError(MP_EINVAL); + } + + if (n_args == 1) { + return mp_obj_new_bool(mp_usbd_class_state.ncm_enabled); + } else { + bool enable = mp_obj_is_true(args[1]); + mp_usbd_enable_class_ncm(enable); + return mp_const_none; + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usb_device_enable_ncm_obj, 1, 2, usb_device_enable_ncm); + static const MP_DEFINE_BYTES_OBJ(builtin_default_desc_dev_obj, &mp_usbd_builtin_desc_dev, sizeof(tusb_desc_device_t)); @@ -278,6 +330,11 @@ static const mp_rom_map_elem_t usb_device_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&usb_device_active_obj) }, { MP_ROM_QSTR(MP_QSTR_stall), MP_ROM_PTR(&usb_device_stall_obj) }, { MP_ROM_QSTR(MP_QSTR_remote_wakeup), MP_ROM_PTR(&usb_remote_wakeup_obj) }, + + // Per-class control methods + { MP_ROM_QSTR(MP_QSTR_enable_cdc), MP_ROM_PTR(&usb_device_enable_cdc_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_msc), MP_ROM_PTR(&usb_device_enable_msc_obj) }, + { MP_ROM_QSTR(MP_QSTR_enable_ncm), MP_ROM_PTR(&usb_device_enable_ncm_obj) }, // Built-in driver constants { MP_ROM_QSTR(MP_QSTR_BUILTIN_NONE), MP_ROM_PTR(&mp_type_usb_device_builtin_none) }, diff --git a/extmod/modlwip.c b/extmod/modlwip.c index 65a3412ecb801..5a516cfbeb2d9 100644 --- a/extmod/modlwip.c +++ b/extmod/modlwip.c @@ -280,6 +280,68 @@ static const int error_lookup_table[] = { }; #endif + +/*******************************************************************************/ +// atoi() used in netif_find() +// Based on (MIT) implementation from https://github.com/littlekernel/lk + +int isspace(int c) { + return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; +} +int isdigit(int c) { + return (c >= '0') && (c <= '9'); +} +int isxdigit(int c) { + return isdigit(c) || ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')); +} +static int hexval(char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + + return 0; +} + +int atoi(const char *num) { + long long value = 0; + int neg = 0; + + // skip leading whitespace + while (isspace(num[0])) { + num++; + } + + if (num[0] == '0' && num[1] == 'x') { + // hex + num += 2; + while (*num && isxdigit(*num)) { + value = value * 16 + hexval(*num++); + } + } else { + // decimal + if (num[0] == '-') { + neg = 1; + num++; + } else if (num[0] == '+') { + num++; + } + + while (*num && isdigit(*num)) { + value = value * 10 + *num++ - '0'; + } + } + + if (neg) { + value = -value; + } + + return (int)value; +} + /*******************************************************************************/ // The socket object provided by lwip.socket. diff --git a/extmod/network_usbd_ncm.c b/extmod/network_usbd_ncm.c new file mode 100644 index 0000000000000..da1115e6b2b18 --- /dev/null +++ b/extmod/network_usbd_ncm.c @@ -0,0 +1,372 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 Andrew Leech + * + * 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. + */ + +#include "py/runtime.h" +#include "py/mphal.h" + +#if MICROPY_HW_NETWORK_USBNET + +#ifndef NO_QSTR +#include "tusb.h" +#endif + +#include "lwip/ethip6.h" +#include "lwip/init.h" +#include "lwip/timeouts.h" +#include "lwip/etharp.h" +#include "lwip/dns.h" +#include "lwip/dhcp.h" +#include "netif/ethernet.h" +#include "lwip/apps/mdns.h" + +#include "extmod/modnetwork.h" +#include "shared/netutils/netutils.h" +#include "shared/netutils/dhcpserver.h" +#include "lib/uzlib/uzlib.h" + +#define error_printf(...) mp_printf(&mp_plat_print, "network_usbd_ncm: " __VA_ARGS__) + +#if LWIP_IPV6 +#define IP(x) ((x).u_addr.ip4) +#else +#define IP(x) (x) +#endif + +// Maximum retries for TinyUSB packet transmission +#define USBNET_TX_MAX_RETRIES (100) + +const mp_obj_type_t mod_network_nic_type_usbnet; + +typedef struct _usbnet_obj_t { + mp_obj_base_t base; + + struct netif netif; + bool init; + bool active; + + ip_addr_t ipaddr; + ip_addr_t netmask; + ip_addr_t gateway; + + #if MICROPY_HW_NETWORK_USBNET_DHCP_SERVER + dhcp_server_t dhcp_server; + #endif +} usbnet_obj_t; + +// Global object holding the usb net state +static usbnet_obj_t usbnet_obj; + +static mp_sched_node_t network_usbd_ncm_sched_node; + +#define IS_ACTIVE(self) (self->netif.flags & NETIF_FLAG_UP) + +/* This default mac will be updated during init from hardware unique ID (if available) */ +/* it is suggested that the first byte is 0x02 to indicate a link-local address */ +uint8_t tud_network_mac_address[6] = {0x02, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/** + * Generate a link-local IPv4 address from MAC address using CRC32 + * Result will be 169.254.X.1 where X is derived from MAC address CRC + * + * @param mac_addr: 6-byte MAC address + * @param ip_addr: 4-byte buffer to store generated IP address + */ +static void generate_linklocal_ip_from_mac(const uint8_t mac_addr[6], uint8_t ip_addr[4]) { + // Set fixed parts of link-local address + ip_addr[0] = 169; + ip_addr[1] = 254; + ip_addr[3] = 1; // Always .1 as requested + + // Use uzlib's CRC32 function + uint32_t crc = uzlib_crc32(mac_addr, 6, 0); + + // Extract one byte from CRC for third octet + uint8_t third_octet = (crc & 0xFF); + + // Ensure valid range (1-254) - avoid .0 and .255 subnets + if (third_octet == 0) { + third_octet = 1; + } + if (third_octet == 255) { + third_octet = 254; + } + + ip_addr[2] = third_octet; +} + + +static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) { + (void)netif; + + for (int i = USBNET_TX_MAX_RETRIES; i; i--) { + // if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do + if (!tud_ready()) { + return ERR_USE; + } + + // if the network driver can accept another packet, we make it happen + if (tud_network_can_xmit(p->tot_len)) { + tud_network_xmit(p, 0); + return ERR_OK; + } + + // transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet + tud_task(); + + mp_event_handle_nowait(); + } + return ERR_USE; +} + +static err_t netif_init_cb(struct netif *netif) { + LWIP_ASSERT("netif != NULL", (netif != NULL)); + netif->mtu = CFG_TUD_NET_MTU; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP; + netif->state = NULL; + netif->linkoutput = linkoutput_fn; + netif->output = etharp_output; + #if LWIP_IPV6 + netif->output_ip6 = ethip6_output; + netif->flags |= NETIF_FLAG_MLD6; + #endif + return ERR_OK; +} + +#if MICROPY_HW_NETWORK_USBNET_DHCP_SERVER +void dhcp_client_connected(uint8_t ciaddr[4], uint8_t chaddr[16]) { + // When a host pc connects and is assigned an ip address, use that host + // as the default gateway. This means the host could optionally be configured + // to forward internet to the usb network adaptor and this device will be able to use it. + ip4_addr_t gw4; + IP4_ADDR(&gw4, ciaddr[0], ciaddr[1], ciaddr[2], ciaddr[3]); + netif_set_gw(&(usbnet_obj.netif), &gw4); + + #if LWIP_MDNS_RESPONDER + mdns_resp_netif_settings_changed(&(usbnet_obj.netif)); + #endif +} +#endif + +void usbnet_init(void) { + // Init the usbnet object + usbnet_obj.base.type = (mp_obj_type_t *)&mod_network_nic_type_usbnet; + usbnet_obj.active = false; + + struct netif *netif = &(usbnet_obj.netif); + + MICROPY_PY_LWIP_ENTER + + mp_hal_get_mac(MP_HAL_MAC_ETH0, tud_network_mac_address); + tud_network_mac_address[0] = 0x02; + + netif->hwaddr_len = sizeof(tud_network_mac_address); + memcpy(netif->hwaddr, tud_network_mac_address, sizeof(tud_network_mac_address)); + + // This USB Network essentially creates a link with two interfaces, one for the + // host and another for the micropython/lwip end of the connection. + // The MAC configured above is use for one end of the link, flip a bit for the + // MAC on the other end of the link. + netif->hwaddr[5] ^= 0x01; + + // Generate unique link-local IP address from MAC address + uint8_t ip_bytes[4]; + generate_linklocal_ip_from_mac(tud_network_mac_address, ip_bytes); + + // Convert to network byte order and set IP configuration + IP(usbnet_obj.ipaddr).addr = PP_HTONL((ip_bytes[0] << 24) | (ip_bytes[1] << 16) | (ip_bytes[2] << 8) | ip_bytes[3]); + IP(usbnet_obj.netmask).addr = PP_HTONL(0xFFFF0000); // 255.255.0.0 for link-local + IP(usbnet_obj.gateway).addr = 0; // No gateway for link-local + + netif = netif_add( + netif, + ip_2_ip4(&usbnet_obj.ipaddr), + ip_2_ip4(&usbnet_obj.netmask), + ip_2_ip4(&usbnet_obj.gateway), + NULL, + netif_init_cb, + ethernet_input + ); + #if LWIP_IPV6 + netif_create_ip6_linklocal_address(netif, 1); + netif_set_ip6_autoconfig_enabled(netif, 1); + #endif + #if LWIP_NETIF_HOSTNAME + netif_set_hostname(netif, mod_network_hostname_data); + #endif + netif_set_default(netif); + + #if LWIP_MDNS_RESPONDER + mdns_resp_add_netif(netif, mod_network_hostname_data); + #endif + + #if MICROPY_HW_NETWORK_USBNET_DHCP_SERVER + dhcp_server_init(&usbnet_obj.dhcp_server, &usbnet_obj.ipaddr, &usbnet_obj.netmask); + dhcp_server_register_connect_cb(&usbnet_obj.dhcp_server, &dhcp_client_connected); + #endif + + // register with network module + mod_network_register_nic(&usbnet_obj); + usbnet_obj.init = true; + + MICROPY_PY_LWIP_EXIT +} + +void usbnet_deinit(void) { + struct netif *netif = &(usbnet_obj.netif); + + #if LWIP_MDNS_RESPONDER + mdns_resp_remove_netif(netif); + #endif + + #if MICROPY_HW_NETWORK_USBNET_DHCP_SERVER + dhcp_server_deinit(&usbnet_obj.dhcp_server); + #endif + + // remove from lwip + netif_remove(netif); + usbnet_obj.init = false; +} + +static void _scheduled_tud_network_recv_renew(mp_sched_node_t *node) { + // Indicate to usb network driver that client has finished with + // the packet provided to network_recv_cb() + tud_network_recv_renew(); +} + +bool tud_network_recv_cb(const uint8_t *src, uint16_t size) { + if (!usbnet_obj.init) { + return false; + } + struct netif *netif = &(usbnet_obj.netif); + bool ret = true; + if (size) { + struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); + if (p == NULL) { + error_printf("tud_network_recv_cb() failed to alloc pbuf %d\n", size); + return false; + } + + // Copy buf to pbuf + pbuf_take(p, src, size); + + if (netif->input(p, netif) != ERR_OK) { + error_printf("tud_network_recv_cb() netif input failed\n"); + pbuf_free(p); + ret = false; + } + // We need to signal to TinyUSB to continue processing after this current context is complete + mp_sched_schedule_node(&network_usbd_ncm_sched_node, _scheduled_tud_network_recv_renew); + } + return ret; +} + +uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { + // copy from network stack packet pointer to dst. + (void)arg; + struct pbuf *p = (struct pbuf *)ref; + return pbuf_copy_partial(p, dst, p->tot_len, 0); +} + +void tud_network_init_cb(void) { + // The usb network is (re-)initializing. + if (usbnet_obj.init) { + usbnet_deinit(); + } + usbnet_init(); +} + + +/*******************************************************************************/ +// MicroPython bindings + +// Create and return a USB_NET object. +static mp_obj_t usbnet_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + if (!usbnet_obj.init) { + usbnet_init(); + } + // Return usbnet object + return MP_OBJ_FROM_PTR(&usbnet_obj); +} + +static mp_obj_t usbnet_isconnected(mp_obj_t self_in) { + usbnet_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(IS_ACTIVE(self)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(usbnet_isconnected_obj, usbnet_isconnected); + +static mp_obj_t usbnet_active(size_t n_args, const mp_obj_t *args) { + usbnet_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args == 1) { + return mp_obj_new_bool(IS_ACTIVE(self)); + } else { + if (mp_obj_is_true(args[1])) { + if (!IS_ACTIVE(self)) { + netif_set_up(&self->netif); + netif_set_link_up(&self->netif); + } + } else { + if (IS_ACTIVE(self)) { + netif_set_link_down(&self->netif); + netif_set_down(&self->netif); + // Note: We don't call usbnet_deinit() here because that would + // completely remove the network interface. We just want to + // deactivate it so it can be reactivated later. + } + } + return mp_const_none; + } +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usbnet_active_obj, 1, 2, usbnet_active); + +static mp_obj_t usbnet_ifconfig(size_t n_args, const mp_obj_t *args) { + usbnet_obj_t *self = MP_OBJ_TO_PTR(args[0]); + return mod_network_nic_ifconfig(&self->netif, n_args - 1, args + 1); +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(usbnet_ifconfig_obj, 1, 2, usbnet_ifconfig); + +static mp_obj_t network_usbnet_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + usbnet_obj_t *self = MP_OBJ_TO_PTR(args[0]); + return mod_network_nic_ipconfig(&self->netif, n_args - 1, args + 1, kwargs); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(usbnet_ipconfig_obj, 1, network_usbnet_ipconfig); + +static const mp_rom_map_elem_t usbnet_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&usbnet_isconnected_obj) }, + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&usbnet_active_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&usbnet_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&usbnet_ipconfig_obj) }, +}; +static MP_DEFINE_CONST_DICT(usbnet_locals_dict, usbnet_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mod_network_nic_type_usbnet, + MP_QSTR_USB_NET, + MP_TYPE_FLAG_NONE, + make_new, usbnet_make_new, + locals_dict, &usbnet_locals_dict + ); + +#endif diff --git a/extmod/network_usbd_ncm.h b/extmod/network_usbd_ncm.h new file mode 100644 index 0000000000000..b6fb6b8e3b58e --- /dev/null +++ b/extmod/network_usbd_ncm.h @@ -0,0 +1,59 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2022 Blake W. Felt & Angus Gratton + * + * 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 MICROPY_INCLUDED_SHARED_TINYUSB_NETWORK_USBD_NCM_H +#define MICROPY_INCLUDED_SHARED_TINYUSB_NETWORK_USBD_NCM_H + +#include "lwipopts.h" + +// TinyUSB NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING + +// Start dhcp server on this interface by default as this allows the host computer +// to be automatically / quickly allocated a suitable ip address to be able to +// communicate over the usb network link. +#ifndef MICROPY_HW_NETWORK_USBNET_DHCP_SERVER +#define MICROPY_HW_NETWORK_USBNET_DHCP_SERVER (1) +#endif + +// Must be >> MTU +// Can be set to 2048 without impact +#define CFG_TUD_NCM_IN_NTB_MAX_SIZE ((2 + LWIP_MDNS_RESPONDER) * TCP_MSS + 100) + +// Must be >> MTU +// Can be set to smaller values if wNtbOutMaxDatagrams==1 +#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE ((2 + LWIP_MDNS_RESPONDER) * TCP_MSS + 100) + +// Number of NCM transfer blocks for reception side +#ifndef CFG_TUD_NCM_OUT_NTB_N + #define CFG_TUD_NCM_OUT_NTB_N 1 +#endif + +// Number of NCM transfer blocks for transmission side +#ifndef CFG_TUD_NCM_IN_NTB_N + #define CFG_TUD_NCM_IN_NTB_N (2 + LWIP_MDNS_RESPONDER) +#endif + +#endif // MICROPY_INCLUDED_SHARED_TINYUSB_NETWORK_USBD_NCM_H diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index bb07a3aa2025c..090a6006c5b5e 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -177,14 +177,9 @@ DRIVERS_SRC_C += $(addprefix drivers/,\ dht/dht.c \ ) -TINYUSB_SRC_C += \ - lib/tinyusb/src/tusb.c \ - lib/tinyusb/src/class/cdc/cdc_device.c \ - lib/tinyusb/src/class/msc/msc_device.c \ - lib/tinyusb/src/common/tusb_fifo.c \ - lib/tinyusb/src/device/usbd.c \ - lib/tinyusb/src/device/usbd_control.c \ - tinyusb_port/tusb_alif_dcd.c \ +-include $(TOP)/lib/tinyusb/src/tinyusb.mk +TINYUSB_SRC_C := $(addprefix lib/tinyusb/, $(TINYUSB_SRC_C) ) +TINYUSB_SRC_C += tinyusb_port/tusb_alif_dcd.c ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\ Device/common/source/clk.c \ diff --git a/ports/mimxrt/Makefile b/ports/mimxrt/Makefile index 7788b54ca57ff..1cb800e526611 100644 --- a/ports/mimxrt/Makefile +++ b/ports/mimxrt/Makefile @@ -94,19 +94,11 @@ endif # ============================================================================= # TinyUSB Stack source -SRC_TINYUSB_C += \ - lib/tinyusb/src/class/cdc/cdc_device.c \ - lib/tinyusb/src/class/dfu/dfu_rt_device.c \ - lib/tinyusb/src/class/hid/hid_device.c \ - lib/tinyusb/src/class/midi/midi_device.c \ - lib/tinyusb/src/class/msc/msc_device.c \ - lib/tinyusb/src/class/usbtmc/usbtmc_device.c \ - lib/tinyusb/src/class/vendor/vendor_device.c \ - lib/tinyusb/src/common/tusb_fifo.c \ - lib/tinyusb/src/device/usbd.c \ - lib/tinyusb/src/device/usbd_control.c \ - lib/tinyusb/src/portable/chipidea/ci_hs/dcd_ci_hs.c \ - lib/tinyusb/src/tusb.c +-include $(TOP)/lib/tinyusb/src/tinyusb.mk +TINYUSB_SRC_C := $(addprefix lib/tinyusb/, \ + $(TINYUSB_SRC_C) \ + src/portable/chipidea/ci_hs/dcd_ci_hs.c \ + ) # All settings for Ethernet support are controller by the value of MICROPY_PY_LWIP ifeq ($(MICROPY_PY_LWIP),1) @@ -479,7 +471,7 @@ OBJ += $(addprefix $(BUILD)/, $(SHARED_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_SS:.S=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_TINYUSB_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(TINYUSB_SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_HAL_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_HAL_IMX_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_ETH_C:.c=.o)) diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index e1c605f452a32..4a7c36314588c 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -138,6 +138,9 @@ uint32_t trng_random_u32(void); #define MICROPY_PY_WEBSOCKET (MICROPY_PY_LWIP) #define MICROPY_PY_WEBREPL (MICROPY_PY_LWIP) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) +#ifndef MICROPY_HW_NETWORK_USBNET +#define MICROPY_HW_NETWORK_USBNET (MICROPY_PY_LWIP) +#endif #define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) #define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) #define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) @@ -178,12 +181,27 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_NIC_ETH #endif +#if MICROPY_PY_NETWORK_CYW43 +extern const struct _mp_obj_type_t mp_network_cyw43_type; +#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, +#else +#define MICROPY_HW_NIC_CYW43 +#endif + +#if MICROPY_HW_NETWORK_USBNET +extern const struct _mp_obj_type_t mod_network_nic_type_usbnet; +#define MICROPY_HW_NIC_USBNET { MP_ROM_QSTR(MP_QSTR_USB_NET), MP_ROM_PTR(&mod_network_nic_type_usbnet) }, +#else +#define MICROPY_HW_NIC_USBNET +#endif #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_ETH \ + MICROPY_HW_NIC_CYW43 \ + MICROPY_HW_NIC_USBNET \ MICROPY_BOARD_NETWORK_INTERFACES \ #ifndef MICROPY_BOARD_ROOT_POINTERS diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile index fd74c60a8564e..c5bafb6596585 100644 --- a/ports/renesas-ra/Makefile +++ b/ports/renesas-ra/Makefile @@ -179,22 +179,12 @@ SHARED_SRC_C += $(addprefix shared/,\ tinyusb/mp_usbd_descriptor.c \ ) -# TinyUSB Stack source -TINYUSB_SRC_C += $(addprefix lib/tinyusb/,\ - src/class/cdc/cdc_device.c \ - src/class/dfu/dfu_rt_device.c \ - src/class/hid/hid_device.c \ - src/class/midi/midi_device.c \ - src/class/msc/msc_device.c \ - src/class/usbtmc/usbtmc_device.c \ - src/class/vendor/vendor_device.c \ - src/common/tusb_fifo.c \ - src/device/usbd.c \ - src/device/usbd_control.c \ +-include $(TOP)/lib/tinyusb/src/tinyusb.mk +TINYUSB_SRC_C := $(addprefix lib/tinyusb/, \ + $(TINYUSB_SRC_C) \ src/portable/renesas/rusb2/dcd_rusb2.c \ src/portable/renesas/rusb2/hcd_rusb2.c \ src/portable/renesas/rusb2/rusb2_common.c \ - src/tusb.c \ ) ifeq ($(MICROPY_FLOAT_IMPL),double) diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h index 8a116269bb49e..bf46e4d6be0ee 100644 --- a/ports/renesas-ra/mpconfigport.h +++ b/ports/renesas-ra/mpconfigport.h @@ -127,6 +127,9 @@ #define MICROPY_PY_TIME_TIME_TIME_NS (1) #define MICROPY_PY_TIME_INCLUDEFILE "ports/renesas-ra/modtime.c" #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) +#ifndef MICROPY_HW_NETWORK_USBNET +#define MICROPY_HW_NETWORK_USBNET (MICROPY_PY_LWIP) +#endif #ifndef MICROPY_PY_MACHINE #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/renesas-ra/modmachine.c" diff --git a/ports/rp2/boards/RPI_PICO/board.json b/ports/rp2/boards/RPI_PICO/board.json index 07938978f3655..ecd4bdd5db255 100644 --- a/ports/rp2/boards/RPI_PICO/board.json +++ b/ports/rp2/boards/RPI_PICO/board.json @@ -15,5 +15,8 @@ "product": "Pico", "thumbnail": "", "url": "https://www.raspberrypi.com/products/raspberry-pi-pico/", + "variants": { + "USB_NET": "USB Network Adapter" + }, "vendor": "Raspberry Pi" } diff --git a/ports/rp2/boards/RPI_PICO/mpconfigboard.h b/ports/rp2/boards/RPI_PICO/mpconfigboard.h index c39bc4d489bb6..a147108dfc9c0 100644 --- a/ports/rp2/boards/RPI_PICO/mpconfigboard.h +++ b/ports/rp2/boards/RPI_PICO/mpconfigboard.h @@ -1,3 +1,9 @@ // Board and hardware specific configuration #define MICROPY_HW_BOARD_NAME "Raspberry Pi Pico" #define MICROPY_HW_FLASH_STORAGE_BYTES (1408 * 1024) + +#if MICROPY_PY_LWIP +// Enable networking. +#define MICROPY_PY_NETWORK 1 +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "Pico" +#endif diff --git a/ports/rp2/boards/RPI_PICO/mpconfigvariant.cmake b/ports/rp2/boards/RPI_PICO/mpconfigvariant.cmake new file mode 100644 index 0000000000000..da6414fe08ade --- /dev/null +++ b/ports/rp2/boards/RPI_PICO/mpconfigvariant.cmake @@ -0,0 +1 @@ +set(MICROPY_PY_LWIP OFF) diff --git a/ports/rp2/boards/RPI_PICO/mpconfigvariant_USB_NET.cmake b/ports/rp2/boards/RPI_PICO/mpconfigvariant_USB_NET.cmake new file mode 100644 index 0000000000000..bad78b830dbcb --- /dev/null +++ b/ports/rp2/boards/RPI_PICO/mpconfigvariant_USB_NET.cmake @@ -0,0 +1,7 @@ +set(MICROPY_PY_LWIP ON) + +list(APPEND MICROPY_DEF_BOARD + MICROPY_HW_NETWORK_USBNET=1 +) +# Board specific version of the frozen manifest +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/../RPI_PICO_W/manifest.py) \ No newline at end of file diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 35afea4fac5f4..69401cd3f13ff 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -196,6 +196,9 @@ #define MICROPY_SSL_MBEDTLS (1) #define MICROPY_PY_LWIP_PPP (MICROPY_PY_NETWORK_PPP_LWIP) #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) +#ifndef MICROPY_HW_NETWORK_USBNET +#define MICROPY_HW_NETWORK_USBNET (MICROPY_PY_LWIP) +#endif // Hardware timer alarm index. Available range 0-3. // Number 3 is currently used by pico-sdk alarm pool (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM) @@ -242,11 +245,53 @@ #endif #endif +#if MICROPY_PY_NETWORK_CYW43 +extern const struct _mp_obj_type_t mp_network_cyw43_type; +#define MICROPY_HW_NIC_CYW43 \ + { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, \ + { MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(CYW43_LINK_DOWN) }, \ + { MP_ROM_QSTR(MP_QSTR_STAT_CONNECTING), MP_ROM_INT(CYW43_LINK_JOIN) }, \ + { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(CYW43_LINK_BADAUTH) }, \ + { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(CYW43_LINK_NONET) }, \ + { MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(CYW43_LINK_FAIL) }, \ + { MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(CYW43_LINK_UP) }, +#else +#define MICROPY_HW_NIC_CYW43 +#endif + +#if MICROPY_PY_NETWORK_NINAW10 +// This Network interface requires the extended socket state. +#ifndef MICROPY_PY_SOCKET_EXTENDED_STATE +#define MICROPY_PY_SOCKET_EXTENDED_STATE (1) +#endif +extern const struct _mp_obj_type_t mod_network_nic_type_nina; +#define MICROPY_HW_NIC_NINAW10 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_nic_type_nina) }, +#else +#define MICROPY_HW_NIC_NINAW10 +#endif + +#if MICROPY_PY_NETWORK_WIZNET5K +extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; +#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, +#else +#define MICROPY_HW_NIC_WIZNET5K +#endif + +#if MICROPY_HW_NETWORK_USBNET +extern const struct _mp_obj_type_t mod_network_nic_type_usbnet; +#define MICROPY_HW_NIC_USBNET { MP_ROM_QSTR(MP_QSTR_USB_NET), MP_ROM_PTR(&mod_network_nic_type_usbnet) }, +#else +#define MICROPY_HW_NIC_USBNET +#endif #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ + MICROPY_HW_NIC_CYW43 \ + MICROPY_HW_NIC_NINAW10 \ + MICROPY_HW_NIC_WIZNET5K \ + MICROPY_HW_NIC_USBNET \ MICROPY_BOARD_NETWORK_INTERFACES \ // Additional entries for use with pendsv_schedule_dispatch. diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index fed34be380a50..bea8db96044e1 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -30,6 +30,7 @@ #if MICROPY_PY_LWIP +#include "extmod/network_usbd_ncm.h" #include "shared/runtime/softtimer.h" #include "lwip/netif.h" #include "lwip/timeouts.h" diff --git a/ports/samd/Makefile b/ports/samd/Makefile index bec530d803f7e..517bd44bbb509 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -163,13 +163,11 @@ ASF4_SRC_C += $(addprefix lib/asf4/$(MCU_SERIES_LOWER)/,\ LIBM_SRC_C += $(SRC_LIB_LIBM_C) LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C) -TINYUSB_SRC_C += $(addprefix lib/tinyusb/src/,\ - class/cdc/cdc_device.c \ - common/tusb_fifo.c \ - device/usbd.c \ - device/usbd_control.c \ - portable/microchip/samd/dcd_samd.c \ - tusb.c \ +# TinyUSB Stack source +-include $(TOP)/lib/tinyusb/src/tinyusb.mk +TINYUSB_SRC_C := $(addprefix lib/tinyusb/, \ + $(TINYUSB_SRC_C) \ + src/portable/microchip/samd/dcd_samd.c \ ) DRIVERS_SRC_C += \ diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h index 9e2402c8dc5dc..ba303de0ef776 100644 --- a/ports/stm32/lwip_inc/lwipopts.h +++ b/ports/stm32/lwip_inc/lwipopts.h @@ -6,10 +6,12 @@ #define LWIP_LOOPIF_MULTICAST 1 #define LWIP_LOOPBACK_MAX_PBUFS 8 -#define LWIP_IPV6 0 - #define LWIP_RAND() rng_get() +#ifndef LWIP_IPV6 +#define LWIP_IPV6 1 +#endif + // Include common lwIP configuration. #include "extmod/lwip-include/lwipopts_common.h" diff --git a/ports/stm32/main.c b/ports/stm32/main.c index e8395013b9188..2682dadfc731c 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -539,6 +539,10 @@ void stm32_main(uint32_t reset_mode) { extint_init0(); timer_init0(); + #if MICROPY_PY_NETWORK + mod_network_init(); + #endif + #if MICROPY_HW_ENABLE_CAN pyb_can_init0(); #endif @@ -626,10 +630,6 @@ void stm32_main(uint32_t reset_mode) { servo_init(); #endif - #if MICROPY_PY_NETWORK - mod_network_init(); - #endif - // At this point everything is fully configured and initialised. // Run main.py (or whatever else a board configures at this stage). diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index bfaa3fb0ba084..9baf79b4c8507 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -150,6 +150,9 @@ #define MICROPY_HW_SOFTSPI_MAX_BAUDRATE (HAL_RCC_GetSysClockFreq() / 48) #define MICROPY_PY_WEBSOCKET (MICROPY_PY_LWIP) #define MICROPY_PY_WEBREPL (MICROPY_PY_LWIP) +#ifndef MICROPY_HW_NETWORK_USBNET +#define MICROPY_HW_NETWORK_USBNET (MICROPY_PY_LWIP) +#endif #ifndef MICROPY_PY_NETWORK #define MICROPY_PY_NETWORK (1) #endif @@ -205,6 +208,26 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_NIC_ETH #endif +#if MICROPY_PY_NETWORK_CYW43 +extern const struct _mp_obj_type_t mp_network_cyw43_type; +#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, +#else +#define MICROPY_HW_NIC_CYW43 +#endif + +#if MICROPY_PY_NETWORK_WIZNET5K +extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; +#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, +#else +#define MICROPY_HW_NIC_WIZNET5K +#endif + +#if MICROPY_HW_NETWORK_USBNET +extern const struct _mp_obj_type_t mod_network_nic_type_usbnet; +#define MICROPY_HW_NIC_USBNET { MP_ROM_QSTR(MP_QSTR_USB_NET), MP_ROM_PTR(&mod_network_nic_type_usbnet) }, +#else +#define MICROPY_HW_NIC_USBNET +#endif // extra constants #define MICROPY_PORT_CONSTANTS \ MACHINE_BUILTIN_MODULE_CONSTANTS \ @@ -217,6 +240,9 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_ETH \ + MICROPY_HW_NIC_CYW43 \ + MICROPY_HW_NIC_WIZNET5K \ + MICROPY_HW_NIC_USBNET \ MICROPY_BOARD_NETWORK_INTERFACES \ #define MP_STATE_PORT MP_STATE_VM diff --git a/shared/netutils/dhcpserver.c b/shared/netutils/dhcpserver.c index 9a4d461a93f55..3d6a109be2b47 100644 --- a/shared/netutils/dhcpserver.c +++ b/shared/netutils/dhcpserver.c @@ -65,11 +65,9 @@ #define PORT_DHCP_SERVER (67) #define PORT_DHCP_CLIENT (68) -#define DEFAULT_DNS MAKE_IP4(192, 168, 4, 1) #define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds #define MAC_LEN (6) -#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) typedef struct { uint8_t op; // message opcode @@ -273,6 +271,9 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, printf("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n", dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5], dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3]); + if (d->on_client_connect != NULL) { + d->on_client_connect(dhcp_msg.yiaddr, dhcp_msg.chaddr); + } break; } @@ -283,7 +284,7 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &ip_2_ip4(&d->ip)->addr); opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &ip_2_ip4(&d->nm)->addr); opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &ip_2_ip4(&d->ip)->addr); // aka gateway; can have multiple addresses - opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have multiple addresses + opt_write_n(&opt, DHCP_OPT_DNS, 4, &ip_2_ip4(&d->ip)->addr); // can have multiple addresses opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S); *opt++ = DHCP_OPT_END; struct netif *netif = ip_current_input_netif(); @@ -297,6 +298,7 @@ void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { ip_addr_copy(d->ip, *ip); ip_addr_copy(d->nm, *nm); memset(d->lease, 0, sizeof(d->lease)); + d->on_client_connect = NULL; if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) { return; } @@ -307,4 +309,8 @@ void dhcp_server_deinit(dhcp_server_t *d) { dhcp_socket_free(&d->udp); } +void dhcp_server_register_connect_cb(dhcp_server_t *d, dhcp_client_callback_t on_client_connect) { + d->on_client_connect = on_client_connect; +} + #endif // MICROPY_PY_LWIP diff --git a/shared/netutils/dhcpserver.h b/shared/netutils/dhcpserver.h index 2349d2ea427f4..f76779523f244 100644 --- a/shared/netutils/dhcpserver.h +++ b/shared/netutils/dhcpserver.h @@ -36,14 +36,18 @@ typedef struct _dhcp_server_lease_t { uint16_t expiry; } dhcp_server_lease_t; +typedef void (*dhcp_client_callback_t)(uint8_t[4], uint8_t[16]); + typedef struct _dhcp_server_t { ip_addr_t ip; ip_addr_t nm; dhcp_server_lease_t lease[DHCPS_MAX_IP]; struct udp_pcb *udp; + dhcp_client_callback_t on_client_connect; } dhcp_server_t; void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm); void dhcp_server_deinit(dhcp_server_t *d); +void dhcp_server_register_connect_cb(dhcp_server_t *d, dhcp_client_callback_t on_client_connect); #endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H diff --git a/shared/tinyusb/mp_usbd.h b/shared/tinyusb/mp_usbd.h index 5c8f2a6095f30..6ebbc0943339c 100644 --- a/shared/tinyusb/mp_usbd.h +++ b/shared/tinyusb/mp_usbd.h @@ -35,6 +35,10 @@ #include "py/objarray.h" #include "py/runtime.h" +#if MICROPY_HW_NETWORK_USBNET +#include "extmod/network_usbd_ncm.h" +#endif + #ifndef NO_QSTR #include "tusb.h" #include "device/dcd.h" @@ -42,6 +46,8 @@ // Initialise TinyUSB device. static inline void mp_usbd_init_tud(void) { + // Initialize class state before TinyUSB init + mp_usbd_init_class_state(); tusb_init(); tud_cdc_configure_fifo_t cfg = { .rx_persistent = 0, .tx_persistent = 1 }; tud_cdc_configure_fifo(&cfg); @@ -62,11 +68,31 @@ extern void mp_usbd_port_get_serial_number(char *buf); // length (2 * bytes_len + 1) (including NUL terminator). void mp_usbd_hex_str(char *out_str, const uint8_t *bytes, size_t bytes_len); +// Per-class runtime enable/disable state +typedef struct { + bool cdc_enabled; + bool msc_enabled; + bool ncm_enabled; +} mp_usbd_class_state_t; + +// Global class enable state +extern mp_usbd_class_state_t mp_usbd_class_state; + +// Functions to control individual USB classes at runtime +void mp_usbd_enable_class_cdc(bool enable); +void mp_usbd_enable_class_msc(bool enable); +void mp_usbd_enable_class_ncm(bool enable); +void mp_usbd_init_class_state(void); + +// Get dynamic descriptor length based on enabled classes +size_t mp_usbd_get_descriptor_cfg_len(void); + // Length of built-in configuration descriptor -#define MP_USBD_BUILTIN_DESC_CFG_LEN (TUD_CONFIG_DESC_LEN + \ - (CFG_TUD_CDC ? (TUD_CDC_DESC_LEN) : 0) + \ - (CFG_TUD_MSC ? (TUD_MSC_DESC_LEN) : 0) \ - ) +#define MP_USBD_BUILTIN_DESC_CFG_LEN ( \ + (CFG_TUD_CDC ? (TUD_CDC_DESC_LEN) : 0) + \ + (CFG_TUD_MSC ? (TUD_MSC_DESC_LEN) : 0) + \ + (CFG_TUD_NCM ? (TUD_CDC_NCM_DESC_LEN) : 0) + \ + TUD_CONFIG_DESC_LEN) // Built-in USB device and configuration descriptor values extern const tusb_desc_device_t mp_usbd_builtin_desc_dev; diff --git a/shared/tinyusb/mp_usbd_descriptor.c b/shared/tinyusb/mp_usbd_descriptor.c index d0c8845b68087..0a118640634f9 100644 --- a/shared/tinyusb/mp_usbd_descriptor.c +++ b/shared/tinyusb/mp_usbd_descriptor.c @@ -32,9 +32,82 @@ #include "tusb.h" #include "mp_usbd.h" +// Global class enable state +mp_usbd_class_state_t mp_usbd_class_state; + +// Initialize class state based on compile-time configuration and runtime mode +void mp_usbd_init_class_state(void) { + #if MICROPY_HW_ENABLE_USB_RUNTIME_DEVICE + // In runtime mode, only CDC enabled by default + mp_usbd_class_state.cdc_enabled = (CFG_TUD_CDC == 1); + mp_usbd_class_state.msc_enabled = false; + mp_usbd_class_state.ncm_enabled = false; + #else + // In static mode, enable all compiled classes + mp_usbd_class_state.cdc_enabled = (CFG_TUD_CDC == 1); + mp_usbd_class_state.msc_enabled = (CFG_TUD_MSC == 1); + mp_usbd_class_state.ncm_enabled = (CFG_TUD_NCM == 1); + #endif +} + +// Functions to control individual USB classes at runtime +void mp_usbd_enable_class_cdc(bool enable) { + mp_usbd_class_state.cdc_enabled = enable; +} + +void mp_usbd_enable_class_msc(bool enable) { + mp_usbd_class_state.msc_enabled = enable; +} + +void mp_usbd_enable_class_ncm(bool enable) { + mp_usbd_class_state.ncm_enabled = enable; + // If NCM is being disabled and it was active, clean it up + #if MICROPY_HW_NETWORK_USBNET + if (!enable) { + extern void usbnet_deinit(void); + usbnet_deinit(); + } + #endif +} + +// Get dynamic descriptor length based on enabled classes +size_t mp_usbd_get_descriptor_cfg_len(void) { + size_t len = TUD_CONFIG_DESC_LEN; + + if (mp_usbd_class_state.cdc_enabled && CFG_TUD_CDC) { + len += TUD_CDC_DESC_LEN; + } + if (mp_usbd_class_state.msc_enabled && CFG_TUD_MSC) { + len += TUD_MSC_DESC_LEN; + } + if (mp_usbd_class_state.ncm_enabled && CFG_TUD_NCM) { + len += TUD_CDC_NCM_DESC_LEN; + } + + return len; +} + +// Get number of enabled interfaces +static uint8_t mp_usbd_get_interface_count(void) { + uint8_t count = 0; + + if (mp_usbd_class_state.cdc_enabled && CFG_TUD_CDC) { + count += 2; // CDC uses 2 interfaces + } + if (mp_usbd_class_state.msc_enabled && CFG_TUD_MSC) { + count += 1; + } + if (mp_usbd_class_state.ncm_enabled && CFG_TUD_NCM) { + count += 1; + } + + return count; +} + #define USBD_CDC_CMD_MAX_SIZE (8) #define USBD_CDC_IN_OUT_MAX_SIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 64) #define USBD_MSC_IN_OUT_MAX_SIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 64) +#define USBD_NET_IN_OUT_MAX_SIZE ((CFG_TUD_MAX_SPEED == OPT_MODE_HIGH_SPEED) ? 512 : 64) const tusb_desc_device_t mp_usbd_builtin_desc_dev = { .bLength = sizeof(tusb_desc_device_t), @@ -53,6 +126,80 @@ const tusb_desc_device_t mp_usbd_builtin_desc_dev = { .bNumConfigurations = 1, }; +// Static descriptor for maximum possible configuration +static const uint8_t mp_usbd_builtin_desc_cfg_max[MP_USBD_BUILTIN_DESC_CFG_LEN] = { + TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_BUILTIN_MAX, USBD_STR_0, MP_USBD_BUILTIN_DESC_CFG_LEN, + 0, USBD_MAX_POWER_MA), + + #if CFG_TUD_CDC + TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, + USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), + #endif + #if CFG_TUD_MSC + TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, USBD_STR_MSC, USBD_MSC_EP_OUT, USBD_MSC_EP_IN, USBD_MSC_IN_OUT_MAX_SIZE), + #endif + #if CFG_TUD_NCM + // 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_NCM_DESCRIPTOR(USBD_ITF_NET, USBD_STR_NET, USBD_STR_NET_MAC, USBD_NET_EP_CMD, 64, USBD_NET_EP_OUT, USBD_NET_EP_IN, USBD_NET_IN_OUT_MAX_SIZE, CFG_TUD_NET_MTU), + #endif +}; + +// Dynamic descriptor buffer for runtime configuration +static uint8_t mp_usbd_dynamic_desc_cfg[MP_USBD_BUILTIN_DESC_CFG_LEN]; + +// Generate dynamic configuration descriptor based on enabled classes +static const uint8_t *mp_usbd_generate_desc_cfg(void) { + uint8_t *desc = mp_usbd_dynamic_desc_cfg; + uint8_t interface_count = mp_usbd_get_interface_count(); + size_t total_len = mp_usbd_get_descriptor_cfg_len(); + + // Configuration descriptor header + *desc++ = 9; // bLength + *desc++ = TUSB_DESC_CONFIGURATION; // bDescriptorType + *desc++ = (total_len) & 0xFF; // wTotalLength low + *desc++ = (total_len >> 8) & 0xFF; // wTotalLength high + *desc++ = interface_count; // bNumInterfaces + *desc++ = 1; // bConfigurationValue + *desc++ = USBD_STR_0; // iConfiguration + *desc++ = 0; // bmAttributes + *desc++ = USBD_MAX_POWER_MA / 2; // bMaxPower (in 2mA units) + + // Add enabled class descriptors + #if CFG_TUD_CDC + if (mp_usbd_class_state.cdc_enabled) { + const uint8_t cdc_desc[] = { + TUD_CDC_DESCRIPTOR(USBD_ITF_CDC, USBD_STR_CDC, USBD_CDC_EP_CMD, + USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE) + }; + memcpy(desc, cdc_desc, sizeof(cdc_desc)); + desc += sizeof(cdc_desc); + } + #endif + + #if CFG_TUD_MSC + if (mp_usbd_class_state.msc_enabled) { + const uint8_t msc_desc[] = { + TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, USBD_STR_MSC, USBD_MSC_EP_OUT, USBD_MSC_EP_IN, USBD_MSC_IN_OUT_MAX_SIZE) + }; + memcpy(desc, msc_desc, sizeof(msc_desc)); + desc += sizeof(msc_desc); + } + #endif + + #if CFG_TUD_NCM + if (mp_usbd_class_state.ncm_enabled) { + const uint8_t ncm_desc[] = { + TUD_CDC_NCM_DESCRIPTOR(USBD_ITF_NET, USBD_STR_NET, USBD_STR_NET_MAC, USBD_NET_EP_CMD, 64, USBD_NET_EP_OUT, USBD_NET_EP_IN, USBD_NET_IN_OUT_MAX_SIZE, CFG_TUD_NET_MTU) + }; + memcpy(desc, ncm_desc, sizeof(ncm_desc)); + desc += sizeof(ncm_desc); + } + #endif + + return mp_usbd_dynamic_desc_cfg; +} + +// For backward compatibility - return the maximum static descriptor const uint8_t mp_usbd_builtin_desc_cfg[MP_USBD_BUILTIN_DESC_CFG_LEN] = { TUD_CONFIG_DESCRIPTOR(1, USBD_ITF_BUILTIN_MAX, USBD_STR_0, MP_USBD_BUILTIN_DESC_CFG_LEN, 0, USBD_MAX_POWER_MA), @@ -62,7 +209,11 @@ const uint8_t mp_usbd_builtin_desc_cfg[MP_USBD_BUILTIN_DESC_CFG_LEN] = { USBD_CDC_CMD_MAX_SIZE, USBD_CDC_EP_OUT, USBD_CDC_EP_IN, USBD_CDC_IN_OUT_MAX_SIZE), #endif #if CFG_TUD_MSC - TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, USBD_STR_MSC, EPNUM_MSC_OUT, EPNUM_MSC_IN, USBD_MSC_IN_OUT_MAX_SIZE), + TUD_MSC_DESCRIPTOR(USBD_ITF_MSC, USBD_STR_MSC, USBD_MSC_EP_OUT, USBD_MSC_EP_IN, USBD_MSC_IN_OUT_MAX_SIZE), + #endif + #if CFG_TUD_NCM + // 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_NCM_DESCRIPTOR(USBD_ITF_NET, USBD_STR_NET, USBD_STR_NET_MAC, USBD_NET_EP_CMD, 64, USBD_NET_EP_OUT, USBD_NET_EP_IN, USBD_NET_IN_OUT_MAX_SIZE, CFG_TUD_NET_MTU), #endif }; @@ -115,6 +266,15 @@ const uint16_t *tud_descriptor_string_cb(uint8_t index, uint16_t langid) { desc_str = MICROPY_HW_USB_MSC_INTERFACE_STRING; break; #endif + #if CFG_TUD_NCM + case USBD_STR_NET: + desc_str = MICROPY_HW_NETWORK_USBNET_INTERFACE_STRING; + break; + case USBD_STR_NET_MAC: + mp_usbd_hex_str(serial_buf, (uint8_t *)tud_network_mac_address, sizeof(tud_network_mac_address)); + desc_str = serial_buf; + break; + #endif default: break; } @@ -146,7 +306,8 @@ const uint8_t *tud_descriptor_device_cb(void) { const uint8_t *tud_descriptor_configuration_cb(uint8_t index) { (void)index; - return mp_usbd_builtin_desc_cfg; + // Use dynamic generation to include only enabled classes + return mp_usbd_generate_desc_cfg(); } #else diff --git a/shared/tinyusb/tusb_config.h b/shared/tinyusb/tusb_config.h index 0cc5ef03985ef..86ad322042fbf 100644 --- a/shared/tinyusb/tusb_config.h +++ b/shared/tinyusb/tusb_config.h @@ -63,6 +63,8 @@ #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) #endif +// Always enable USB classes for compilation to allow runtime control +// Runtime enabling/disabling is controlled by mp_usbd_class_state #if MICROPY_HW_USB_CDC #define CFG_TUD_CDC (1) #else @@ -94,16 +96,33 @@ #define CFG_TUD_MSC_BUFSIZE (MICROPY_FATFS_MAX_SS) #endif +#if MICROPY_HW_NETWORK_USBNET +// TinyUSB supports both RNDIS and NCM protocols. +// NCM is now recommended by Microsoft on Win 11 and above. +#define CFG_TUD_NCM (1) +#include "extmod/network_usbd_ncm.h" +#ifndef MICROPY_HW_NETWORK_USBNET_INTERFACE_STRING +#define MICROPY_HW_NETWORK_USBNET_INTERFACE_STRING "Board NET" +#endif +#else +#define CFG_TUD_NCM (0) +#endif // MICROPY_HW_NETWORK_USBNET + #define USBD_RHPORT (0) // Currently only one port is supported // Define built-in interface, string and endpoint numbering based on the above config -#define USBD_STR_0 (0x00) -#define USBD_STR_MANUF (0x01) -#define USBD_STR_PRODUCT (0x02) -#define USBD_STR_SERIAL (0x03) -#define USBD_STR_CDC (0x04) -#define USBD_STR_MSC (0x05) +enum _USBD_STR { + USBD_STR_0 = (0x00), + USBD_STR_MANUF = (0x01), + USBD_STR_PRODUCT = (0x02), + USBD_STR_SERIAL = (0x03), + USBD_STR_CDC, + USBD_STR_MSC, + USBD_STR_NET, + USBD_STR_NET_MAC, + USBD_STR_BUILTIN_MAX, +}; #define USBD_MAX_POWER_MA (250) @@ -111,40 +130,55 @@ #define MICROPY_HW_USB_DESC_STR_MAX (40) #endif -#if CFG_TUD_CDC -#define USBD_ITF_CDC (0) // needs 2 interfaces -#define USBD_CDC_EP_CMD (0x81) -#define USBD_CDC_EP_OUT (0x02) -#define USBD_CDC_EP_IN (0x82) -#endif // CFG_TUD_CDC +enum _USBD_ITF { + #if CFG_TUD_CDC + USBD_ITF_CDC, + USBD_ITF_CDC_I2, + #endif // CFG_TUD_CDC + #if CFG_TUD_MSC + USBD_ITF_MSC, + #endif // CFG_TUD_MSC + #if CFG_TUD_NCM + USBD_ITF_NET, + #endif +}; + +enum _USBD_EP { + __USBD_EP = 0x80, + #if CFG_TUD_CDC + USBD_CDC_EP_CMD, + USBD_CDC_EP_IN, + #endif // CFG_TUD_CDC + #if CFG_TUD_MSC + USBD_MSC_EP_IN, + #endif // CFG_TUD_MSC + #if CFG_TUD_NCM + USBD_NET_EP_CMD, + USBD_NET_EP_IN, + #endif // CFG_TUD_NET + USBD_EP_BUILTIN_MAX +}; + +// define the matching in endpoints to each EP_OUT +#define USBD_CDC_EP_OUT ((USBD_CDC_EP_IN)&~TUSB_DIR_IN_MASK) +#define USBD_MSC_EP_OUT ((USBD_MSC_EP_IN)&~TUSB_DIR_IN_MASK) +#define USBD_NET_EP_OUT ((USBD_NET_EP_IN)&~TUSB_DIR_IN_MASK) -#if CFG_TUD_MSC -// Interface & Endpoint numbers for MSC come after CDC, if it is enabled -#if CFG_TUD_CDC -#define USBD_ITF_MSC (2) -#define EPNUM_MSC_OUT (0x03) -#define EPNUM_MSC_IN (0x83) -#else -#define USBD_ITF_MSC (0) -#define EPNUM_MSC_OUT (0x01) -#define EPNUM_MSC_IN (0x81) -#endif // CFG_TUD_CDC -#endif // CFG_TUD_MSC /* Limits of builtin USB interfaces, endpoints, strings */ -#if CFG_TUD_MSC -#define USBD_ITF_BUILTIN_MAX (USBD_ITF_MSC + 1) -#define USBD_STR_BUILTIN_MAX (USBD_STR_MSC + 1) -#define USBD_EP_BUILTIN_MAX (EPNUM_MSC_OUT + 1) -#elif CFG_TUD_CDC -#define USBD_ITF_BUILTIN_MAX (USBD_ITF_CDC + 2) -#define USBD_STR_BUILTIN_MAX (USBD_STR_CDC + 1) -#define USBD_EP_BUILTIN_MAX (((USBD_CDC_EP_IN)&~TUSB_DIR_IN_MASK) + 1) -#else // !CFG_TUD_MSC && !CFG_TUD_CDC -#define USBD_ITF_BUILTIN_MAX (0) -#define USBD_STR_BUILTIN_MAX (0) -#define USBD_EP_BUILTIN_MAX (0) -#endif +// 1 plus the number of interfaces used by all enabled classes +#define USBD_ITF_BUILTIN_MAX ( \ + (CFG_TUD_CDC ? 2 : 0) + \ + (CFG_TUD_MSC ? 1 : 0) + \ + (CFG_TUD_NCM ? 1 : 0) + \ + 1) + +// 1 plus the number of interfaces used by all enabled classes +#define USBD_EP_BUILTIN_MAX ( \ + (CFG_TUD_CDC ? 2 : 0) + \ + (CFG_TUD_MSC ? 1 : 0) + \ + (CFG_TUD_NCM ? 2 : 0) + \ + 1) #endif // MICROPY_HW_ENABLE_USBDEV
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: