diff --git a/CMakeLists.txt b/CMakeLists.txt index f26c217e4..eae8b133d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,8 @@ option(OPT_PROBE_DEBUG_OUT "Enable CDC for probe debug output" ${DEFAUL option(OPT_SIGROK "Enable sigrok" 0) option(OPT_MSC "Enable Mass Storage Device" 1) option(OPT_MSC_RAM_UF2 "Enable file 'RAM.UF2' on Mass Storage" 1) -set(OPT_NET "NCM" CACHE STRING "Enable lwIP on the Pico via ECM/NCM/RNDIS") +set(OPT_MCU_OVERCLOCK_MHZ "240" CACHE STRING "Set processor frequency. Should be a multiple of 24MHz, disable with empty string (=120MHz)") +set(OPT_NET "NCM" CACHE STRING "Enable lwIP on the Pico via NCM/ECM/RNDIS, disable NET with empty string") set(OPT_NET_192_168 14 CACHE STRING "Set the subnet of 192.168.x") option(OPT_NET_ECHO_SERVER "Enable echo server for testing" 1) option(OPT_NET_IPERF_SERVER "Enable iperf server for tuning" 1) @@ -95,6 +96,9 @@ add_definitions( -DTARGET_BOARD_${PICO_BOARD_UPPER} -DOPT_SPECIAL_CLK_FOR_PIO=${OPT_SPECIAL_CLK_FOR_PIO} ) +if(NOT OPT_MCU_OVERCLOCK_MHZ STREQUAL "") + add_compile_definitions(OPT_MCU_OVERCLOCK_MHZ=${OPT_MCU_OVERCLOCK_MHZ}) +endif() # set version string to "x.yy" # there are perhaps smarter ways... @@ -257,7 +261,18 @@ if(NOT OPT_NET STREQUAL "") ${PICO_TINYUSB_PATH}/lib/networking/dhserver.c ) - if(NOT OPT_NET STREQUAL "NCM") + if(OPT_NET STREQUAL "NCM") + # NCM: use own copy of ncm_device, original one is removed from target_sources() with a trick + message("--------- " ${PICO_TINYUSB_PATH}) + target_sources(${PROJECT} PRIVATE + src/net/tinyusb/ncm_device_simple.c + ) + set_source_files_properties( + ${PICO_TINYUSB_PATH}/src/class/net/ncm_device.c + PROPERTIES HEADER_FILE_ONLY ON + ) + else() + # !NCM target_sources(${PROJECT} PRIVATE ${PICO_TINYUSB_PATH}/lib/networking/rndis_reports.c ) diff --git a/README.adoc b/README.adoc index 74fb79911..920b35564 100644 --- a/README.adoc +++ b/README.adoc @@ -99,7 +99,7 @@ containing the steps from installation until debugging in VSCode. |yes |`openocd -f interface/cmsis-dap.cfg -f target/rp2040.cfg -c "adapter speed 25000" -c "program {firmware.elf} verify reset; shutdown;"` -|pyOCD 0.34.x +|pyOCD 0.34 & 0.35 |yes |yes |`pyocd flash -f 400000 -t nrf52840 firmware.elf` @@ -371,6 +371,7 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="2e8a", ATTR{idProduct}=="000c", MODE:="0666" ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ATTRS{interface}=="YAPicoprobe CDC-UART", MODE:="0666", SYMLINK+="ttyPicoTarget" ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ATTRS{interface}=="YAPicoprobe CDC-DEBUG", MODE:="0666", SYMLINK+="ttyPicoProbe" ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ATTRS{interface}=="YAPicoprobe CDC-SIGROK", MODE:="0666", SYMLINK+="ttyPicoSigRok +ACTION=="add", SUBSYSTEMS=="usb", KERNEL=="ttyACM[0-9]*", ATTRS{interface}=="YAPicoprobe CDC-SysView", MODE:="0666", SYMLINK+="ttyPicoSysView" # mount Picoprobe to /media/picoprobe ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", RUN+="/usr/bin/logger --tag picoprobe-mount Mounting what seems to be a Raspberry Pi Picoprobe", RUN+="/usr/bin/systemd-mount --no-block --collect --fsck=0 -o uid=hardy,gid=hardy,flush $devnode /media/picoprobe" @@ -383,8 +384,11 @@ ACTION=="remove", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="file ### Network Configuration -SystemView connectivity over TCP/IP is on most systems not configuration free. On Debian the configuration -is as follows: +SystemView connectivity over TCP/IP is on most systems not configuration free. + +#### Debian + +On Debian the configuration is as follows: * after connecting the probe with the host, `ip a` shows its network interface, e.g. `enxfe`. If the network interface already shows an IP address everything is fine and you are ready @@ -402,6 +406,27 @@ On my system this unfortunately leads to error messages (which are harmless) if The default address of the probe is 192.168.14.1. If there is a collision with the current setup of the host, the probe firmware has to be rebuilt with `-DOPT_NET_192_168=`. +#### Win10 + +A certain version of Win10 is required for USB-NCM connectivity. Exact version is unknown. + +The driver needs to be installed manually. Procedure is roughly as follows: + +* in "Device Manager" search for `YaPicoprobe NCM` with an exclamation mark +* "Update Driver" +* "Browse my computer for drivers" +* "Let me pick..." +* "Network adapters" +* "Microsoft" +* "UsbNcm Host Device" - if this is not available, then the version of Win10 is not ready for USB-NCM +* confirm any dialog boxes +* if `ipconfig` on a command line shows `192.168.14.2` for an Ethernet adapter, the procedure has most likely + succeeded and SystemView is ready for operation + +#### Win11 + +It is said, that USB-NCM is working out of the box. This is not tested. + ### PlatformIO [[platformio]] https://platformio.org/[PlatformIO] configuration in `platformio.ini` is pretty straight forward: diff --git a/doc/lwIP-notes.adoc b/doc/lwIP-notes.adoc index 078747b36..e02114ccd 100755 --- a/doc/lwIP-notes.adoc +++ b/doc/lwIP-notes.adoc @@ -9,8 +9,9 @@ ## Some lwIP Notes lwIP is used for creation of a network interface of YAPicoprobe to avoid -an uncountable number of additional CDC com ports. + -Currently just a Segger SysView server is listening on port 19111. +an uncountable number of additional CDC COM ports. + +Currently a Segger SysView server is listening on port 19111. But I guess there is more to come. @@ -64,19 +65,7 @@ messages even when the device has `allow-hotplug`. Unfortunately RNDIS seems to manipulate routing in a way that the default route on my Linux wants to go through the probe. Not really what I want... - -* *NCM*: is said to be the best choice. I did not manage to get an - acceptable throughput. Transfer seems to get stuck from time to time - and SystemView aborts. So also not the favorite. + - `iperf` is running fast, it seems that `ncm_device.c` output is triggered - by incoming packets. -* *ECM*: works good, packets are transferred continuously, throughput - also seems to be ok. So this is the way to go. + - Unfortunately there is no driver integrated into Win10, so possible - extra trouble appears. Yes... extra trouble: cannot find a driver - for Win10. - [NOTE] ==== RNDIS on Win10 works only, if RNDIS on the probe is the only USB class selected. @@ -85,6 +74,20 @@ like SystemView, but you cannot have a probe which does SystemView and CMSIS-DAP This is not a fault of lwIP, it is a bug in the Win10 driver(?). ==== +* *ECM*: works good, packets are transferred continuously, throughput + also seems to be ok. So this is the way to go. + + Unfortunately there is no driver integrated into Win10, so possible + extra trouble appears. Yes... extra trouble: cannot find a driver + for Win10. + +* *NCM*: is said to be the best choice. And in fact it is. + At least after creation of a `ncm_device_simple.c` driver which is a + stripped down version of `ncm_simple.c` which revealed as very buggy. + + Now thoughput under Linux and Windows is ok. Operation with SystemView + works without glitches, `iperf` tests sometimes crashes the probe. + So consider this driver as beta and work in progress. + + ## Performance @@ -93,9 +96,72 @@ must be set on built). Good command line for measurement: iperf -c 192.168.14.1 -e -i 1 -l 1024 +## Testing + +Good test cases are the following command lines: + + for MSS in 90 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1459 1460 1500; do iperf -c 192.168.14.1 -e -i 1 -l 1024 -M $MSS; sleep 10; done + + for LEN in 40000 16000 1400 1024 800 200 80 22 8 2 1; do for MSS in 90 93 100 150 200 255 256 300 400 500 511 512 600 700 800 900 1000 1100 1200 1300 1400 1450 1459 1460 1500; do iperf -c 192.168.14.1 -e -i 1 -l $LEN -M $MSS; sleep 2; done; done + +Monitor performance/errors with Wireshark. + + +## Some words about... + +### ... `net_glue.c` + +I'm really trying hard to switch context betwenn lwIP and TinyUSB correctly. This leads +to some kind of delayed call chains and also does not make the code neither nice nor +very much maintainable. + + +### ... `ncm_device_simple.c` + +`ncm_device_simple.c` is actually a mixture of `ecm_rndis_device.c` and `ncm_device.c`. +From `ecm_rndis_device.c` the structure has been inherited and from `ncm_device.c` the +functionality. + +The driver can be considered work in progress, because in conjunction with `iperf` +crashes sometimes happen. But for operation with SystemView quality seems to be good enough. + + +### ... possible Bugs in `ncm_device.c` + +This is more or less obsoleted by `ncm_device_simple.c`. But as a short summary: the original +driver is very buggy. Perhaps it is working in certain scenarios, but for sure not together with +SystemView. + +* not sure, but perhaps it is best to call all functions within ncm_device in the FreeRTOS + context of TinyUSB +* `wNtbOutMaxDatagrams` must be set to 1 [2023-06-27] +** iperf runs then +** Systemview still has problems +** `wNtbOutMaxDatagrams == 0` generates a lot of retries with iperf +* I guess that the *major problem* lies within handle_incoming_datagram() because it changes values + on an incoming packet although tud_network_recv_renew() is still handling the old one +* is there multicore a problem!? (14.7.2023: no!) I have seen retries with multicore even with + `wNtbOutMaxDatagrams = 1` +* I think it is assumed, that TinyUSB and lwIP are running in the same task (but in my scenario they don't) +* if removing debug messages, then the receive path seems to work better, which + indicates a race condition somewhere + +There is an open issue in the TinyUSB repo for this issue: https://github.com/hathach/tinyusb/issues/2068 + ## Log +### 2023-07-14 + +* did some performance tuning with lwIP and TinyUSB +* stripped sources +* BUG: `ncm_device_simple` sometimes crashes with `iperf` + +### 2023-06-30 + +* for debugging purposes reimplemented `ncm_device_simple.c` which can hold only + one ethernet frame per NTB (NCM Transfer Block). This unfortunately requires + that the original `ncm_device.c` must be outcommented via `#if` on top. + ### 2023-06-26 * after some changes to `rtt_console.c`, `net_sysview.c` and `net_glue.c` diff --git a/include/FreeRTOSConfig.h b/include/FreeRTOSConfig.h index fad0cf741..0656d4549 100644 --- a/include/FreeRTOSConfig.h +++ b/include/FreeRTOSConfig.h @@ -78,7 +78,7 @@ /* Memory allocation related definitions. */ #define configSUPPORT_STATIC_ALLOCATION 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 -#define configTOTAL_HEAP_SIZE (150*1024) +#define configTOTAL_HEAP_SIZE (90*1024) #define configAPPLICATION_ALLOCATED_HEAP 0 /* Hook function related definitions. */ @@ -97,6 +97,9 @@ #warning "configGENERATE_RUN_TIME_STATS is set" #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() do {} while( 0 ) #define portALT_GET_RUN_TIME_COUNTER_VALUE( dest ) ( dest = *((uint32_t *)(TF_TIMER_BASE + TF_TIMER_TIMERAWL_OFFSET)) ) + + #undef configUSE_TRACE_FACILITY + #define configUSE_TRACE_FACILITY 1 #endif /* Co-routine related definitions. */ diff --git a/include/lwipopts.h b/include/lwipopts.h index f971b424b..9e306efc0 100755 --- a/include/lwipopts.h +++ b/include/lwipopts.h @@ -65,41 +65,49 @@ //-------------------------------------- -// performance tuning (do not change without extensive testing, optimized for ECM) -#define TCP_MSS (1500 - 20 - 20) // MTU minus header sizes (best value til now) -#define TCP_SND_BUF (4 * TCP_MSS) // good tuning +// performance tuning (do not change without extensive testing, optimized for ECM/NCM) +#define TCP_MSS (1500 - 20 - 20) // MTU minus header sizes (best value til now) +#define TCP_SND_BUF (8 * TCP_MSS) // good tuning -//#define TCP_WND TCP_SND_BUF -//#define TCP_OVERSIZE (TCP_MSS / 4) // til now no good value found +//#define TCP_WND TCP_SND_BUF // til now no good value found +#define TCP_SND_QUEUELEN 16 +#define TCP_SNDQUEUELOWAT (TCP_SND_QUEUELEN / 2) +#define MEMP_NUM_TCP_SEG 32 //-------------------------------------- // memory -#define MEM_SIZE 20000 -//#define MEM_LIBC_MALLOC 1 -//#define MEMP_MEM_MALLOC 1 +#define MEM_SIZE 20000 +//#define MEMP_OVERFLOW_CHECK 1 //#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 1 //-------------------------------------- // for freertos mode -#define TCPIP_MBOX_SIZE 64 -#define TCPIP_THREAD_STACKSIZE 8192 -#define TCPIP_THREAD_PRIO 11 +#define TCPIP_MBOX_SIZE 64 +#define TCPIP_THREAD_STACKSIZE 8192 +#define TCPIP_THREAD_PRIO 27 //-------------------------------------- // trying... #define LWIP_PROVIDE_ERRNO 1 #if LWIP_SOCKET - #define LWIP_TIMEVAL_PRIVATE 0 // required for LWIP_SOCKET + #define LWIP_TIMEVAL_PRIVATE 0 // required for LWIP_SOCKET #endif //-------------------------------------- // statistics -//#define LWIP_STATS 1 -//#define LWIP_STATS_DISPLAY 1 -//#define LINK_STATS 1 +// use stats_display() for display +#define LWIP_STATS 0 +#define LWIP_STATS_DISPLAY 0 +#define ETHARP_STATS 0 // do not display the topics below +#define ICMP_STATS 0 +#define IPFRAG_STATS 0 +#define LINK_STATS 0 +#define MEMP_STATS 0 +#define SYS_STATS 0 +#define UDP_STATS 0 //-------------------------------------- @@ -108,7 +116,7 @@ #define API_LIB_DEBUG LWIP_DBG_OFF #define API_MSG_DEBUG LWIP_DBG_OFF #define AUTOIP_DEBUG LWIP_DBG_OFF -#define DHCP_DEBUG LWIP_DBG_ON +#define DHCP_DEBUG LWIP_DBG_OFF #define DNS_DEBUG LWIP_DBG_OFF #define ETHARP_DEBUG LWIP_DBG_OFF #define ICMP_DEBUG LWIP_DBG_OFF @@ -125,7 +133,7 @@ #define TCP_INPUT_DEBUG LWIP_DBG_OFF #define TCP_FR_DEBUG LWIP_DBG_OFF #define TCP_RTO_DEBUG LWIP_DBG_OFF -#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_ON #define TCP_WND_DEBUG LWIP_DBG_OFF #define TCP_RST_DEBUG LWIP_DBG_OFF #define TCP_OUTPUT_DEBUG LWIP_DBG_OFF diff --git a/include/picoprobe_config.h b/include/picoprobe_config.h index abfee7402..b1a249089 100755 --- a/include/picoprobe_config.h +++ b/include/picoprobe_config.h @@ -63,10 +63,10 @@ // Base value of sys_clk in khz. Must be <=125Mhz per RP2040 spec and a multiple of 24Mhz // to support integer divisors of the PIO clock and ADC clock (for sigrok) -#if 0 -#define PROBE_CPU_CLOCK_KHZ ((120 + 2*24) * 1000) // overclocked, even 264MHz seems to be no problem +#ifdef OPT_MCU_OVERCLOCK_MHZ + #define PROBE_CPU_CLOCK_KHZ ((OPT_MCU_OVERCLOCK_MHZ) * 1000) // overclocked, even 264MHz seems to be no problem #else -#define PROBE_CPU_CLOCK_KHZ ((120 + 0*24) * 1000) // overclocked, even 264MHz seems to be no problem + #define PROBE_CPU_CLOCK_KHZ ((120 + 0*24) * 1000) #endif diff --git a/src/cdc/cdc_debug.c b/src/cdc/cdc_debug.c index 981b8c59a..786938d51 100644 --- a/src/cdc/cdc_debug.c +++ b/src/cdc/cdc_debug.c @@ -79,7 +79,7 @@ void cdc_debug_thread(void *ptr) if (xStreamBufferIsEmpty(stream_printf)) { // -> end of transmission: flush and sleep for a long time (or until new data is available) tud_cdc_n_write_flush(CDC_DEBUG_N); - xEventGroupWaitBits(events, EV_TX_COMPLETE | EV_STREAM, pdTRUE, pdFALSE, pdMS_TO_TICKS(10000)); + xEventGroupWaitBits(events, EV_TX_COMPLETE | EV_STREAM, pdTRUE, pdFALSE, pdMS_TO_TICKS(1000)); } else { size_t cnt; @@ -255,7 +255,7 @@ void cdc_debug_init(uint32_t task_prio) panic("cdc_debug_init: cannot create sema_printf\n"); } - xTaskCreate(cdc_debug_thread, "CDC_DEBUG", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_printf); + xTaskCreate(cdc_debug_thread, "CDC-ProbeUart", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_printf); cdc_debug_line_state_cb(false, false); stdio_set_driver_enabled(&stdio_cdc, true); diff --git a/src/cdc/cdc_sysview.c b/src/cdc/cdc_sysview.c index 1ba7049e1..661ee9156 100755 --- a/src/cdc/cdc_sysview.c +++ b/src/cdc/cdc_sysview.c @@ -144,7 +144,7 @@ void cdc_sysview_line_state_cb(bool dtr, bool rts) * This seems to be necessary to survive e.g. a restart of the host (Linux) */ { - printf("cdc_sysview_line_state_cb(%d,%d)\n", dtr, rts); + //printf("cdc_sysview_line_state_cb(%d,%d)\n", dtr, rts); tud_cdc_n_write_clear(CDC_SYSVIEW_N); tud_cdc_n_read_flush(CDC_SYSVIEW_N); @@ -171,6 +171,13 @@ void cdc_sysview_rx_cb() +bool net_sysview_is_connected(void) +{ + return m_connected; +} // net_sysview_is_connected + + + uint32_t net_sysview_send(const uint8_t *buf, uint32_t cnt) /** * Send characters from SysView RTT channel into stream. @@ -188,7 +195,7 @@ uint32_t net_sysview_send(const uint8_t *buf, uint32_t cnt) r = xStreamBufferSpacesAvailable(stream_sysview); } else { - if ( !m_connected) { + if ( !net_sysview_is_connected()) { xStreamBufferReset(stream_sysview); } else { @@ -212,6 +219,6 @@ void cdc_sysview_init(uint32_t task_prio) picoprobe_error("cdc_sysview_init: cannot create stream_sysview\n"); } - xTaskCreate(cdc_thread, "CDC_SysView", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_sysview); + xTaskCreate(cdc_thread, "CDC-SysViewUart", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_sysview); cdc_sysview_line_state_cb(false, false); } // cdc_sysview_init diff --git a/src/cdc/cdc_sysview.h b/src/cdc/cdc_sysview.h index eee62eed4..eb1c6525e 100755 --- a/src/cdc/cdc_sysview.h +++ b/src/cdc/cdc_sysview.h @@ -30,13 +30,13 @@ #include #include "tusb.h" -#if OPT_CDC_SYSVIEW - uint32_t net_sysview_send(const uint8_t *buf, uint32_t cnt); - void cdc_sysview_init(uint32_t task_prio); +void cdc_sysview_init(uint32_t task_prio); - void cdc_sysview_line_state_cb(bool dtr, bool rts); - void cdc_sysview_tx_complete_cb(void); - void cdc_sysview_rx_cb(void); -#endif +uint32_t net_sysview_send(const uint8_t *buf, uint32_t cnt); +bool net_sysview_is_connected(void); + +void cdc_sysview_line_state_cb(bool dtr, bool rts); +void cdc_sysview_tx_complete_cb(void); +void cdc_sysview_rx_cb(void); #endif diff --git a/src/cdc/cdc_uart.c b/src/cdc/cdc_uart.c index ad6b35a9a..447b562c8 100644 --- a/src/cdc/cdc_uart.c +++ b/src/cdc/cdc_uart.c @@ -332,6 +332,6 @@ void cdc_uart_init(uint32_t task_prio) uart_set_irq_enables(PICOPROBE_UART_INTERFACE, true, false); /* UART needs to preempt USB as if we don't, characters get lost */ - xTaskCreate(cdc_thread, "CDC_UART", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_uart); + xTaskCreate(cdc_thread, "CDC-TargetUart", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_uart); cdc_uart_line_state_cb(false, false); } // cdc_uart_init diff --git a/src/get_config.h b/src/get_config.h index 6a7d94b22..4aa8763f6 100644 --- a/src/get_config.h +++ b/src/get_config.h @@ -80,6 +80,7 @@ #endif #else #define __OPT_NET + #define __OPT_NET_CONF "" #endif #if OPT_NET_SYSVIEW_SERVER #define __OPT_NET_SYSVIEW_SERVER " [Net: SysView]" @@ -101,21 +102,27 @@ /** * CONFIG_FEATURES */ -#define CONFIG_FEATURES() __OPT_CMSIS_DAPV1 __OPT_CMSIS_DAPV2 __OPT_MSC __OPT_TARGET_UART __OPT_TARGET_UART \ +#define CONFIG_FEATURES() __OPT_CMSIS_DAPV1 __OPT_CMSIS_DAPV2 __OPT_MSC __OPT_TARGET_UART __OPT_SIGROK \ __OPT_PROBE_DEBUG_OUT __OPT_CDC_SYSVIEW \ __OPT_NET_CONF __OPT_NET_SYSVIEW_SERVER __OPT_NET_ECHO_SERVER __OPT_NET_IPERF_SERVER /** * CONFIG_BOARD */ +#if defined(OPT_MCU_OVERCLOCK_MHZ) +#define __OPT_MCU_MHZ " @ " xxCoNfSTR(OPT_MCU_OVERCLOCK_MHZ) "MHz" +#else +#define __OPT_MCU_MHZ +#endif + #if defined(TARGET_BOARD_PICO) - #define CONFIG_BOARD() "Pico" + #define CONFIG_BOARD() "Pico" __OPT_MCU_MHZ #elif defined(TARGET_BOARD_PICO_W) - #define CONFIG_BOARD() "Pico_W" + #define CONFIG_BOARD() "Pico_W" __OPT_MCU_MHZ #elif defined(TARGET_BOARD_PICO_DEBUG_PROBE) - #define CONFIG_BOARD() "Pico Debug Probe" + #define CONFIG_BOARD() "Pico Debug Probe" __OPT_MCU_MHZ #else - #define CONFIG_BOARD() "UNKNOWN board" + #define CONFIG_BOARD() "UNKNOWN board" __OPT_MCU_MHZ #endif diff --git a/src/main.c b/src/main.c index 4575e6802..6cd27a4ec 100644 --- a/src/main.c +++ b/src/main.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "hardware/vreg.h" #include "bsp/board.h" @@ -117,20 +118,21 @@ static uint8_t RxDataBuffer[_DAP_PACKET_COUNT_OPENOCD * CFG_TUD_VENDOR_RX_BUFSIZ // prios are critical and determine throughput -// there is one more task prio in lwipopts.h -#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 20) // uses one core continuously (no longer valid with FreeRTOS usage) -#define LED_TASK_PRIO (tskIDLE_PRIORITY + 12) // simple task which may interrupt everything else for periodic blinking +#define LED_TASK_PRIO (tskIDLE_PRIORITY + 30) // simple task which may interrupt everything else for periodic blinking +#define TUD_TASK_PRIO (tskIDLE_PRIORITY + 28) // high prio for TinyUSB +//#define TCPIP_THREAD_PRIO (27) // defined in lwipopts.h +#define CDC_DEBUG_TASK_PRIO (tskIDLE_PRIORITY + 26) // probe debugging output (CDC) +#define PRINT_STATUS_TASK_PRIO (tskIDLE_PRIORITY + 24) // high prio to get status output transferred in (almost) any case #define SIGROK_TASK_PRIO (tskIDLE_PRIORITY + 9) // Sigrok digital/analog signals (does nothing at the moment) #define MSC_WRITER_THREAD_PRIO (tskIDLE_PRIORITY + 8) // this is only running on writing UF2 files -#define SYSVIEW_TASK_PRIO (tskIDLE_PRIORITY + 6) // target -> host via SysView -#define UART_TASK_PRIO (tskIDLE_PRIORITY + 5) // target -> host via UART -#define RTT_CONSOLE_TASK_PRIO (tskIDLE_PRIORITY + 4) // target -> host via RTT -#define CDC_DEBUG_TASK_PRIO (tskIDLE_PRIORITY + 4) // probe debugging output -#define DAP_TASK_PRIO (tskIDLE_PRIORITY + 2) // DAP execution, during connection this takes the other core +#define SYSVIEW_TASK_PRIO (tskIDLE_PRIORITY + 6) // target -> host via SysView (CDC) +#define UART_TASK_PRIO (tskIDLE_PRIORITY + 5) // target -> host via UART (CDC) +#define DAPV2_TASK_PRIO (tskIDLE_PRIORITY + 3) // DAPv2 execution +#define RTT_CONSOLE_TASK_PRIO (tskIDLE_PRIORITY + 1) // target -> host via RTT, ATTENTION: this task can fully load the CPU depending on target RTT output static TaskHandle_t tud_taskhandle; static TaskHandle_t dap_taskhandle; -static EventGroupHandle_t events; +static EventGroupHandle_t dap_events; @@ -224,7 +226,7 @@ void tud_cdc_tx_complete_cb(uint8_t itf) void tud_vendor_rx_cb(uint8_t itf) { if (itf == 0) { - xEventGroupSetBits(events, 0x01); + xEventGroupSetBits(dap_events, 0x01); } } // tud_vendor_rx_cb #endif @@ -272,7 +274,7 @@ void dap_task(void *ptr) tool = DAP_FingerprintTool(NULL, 0); } - xEventGroupWaitBits(events, 0x01, pdTRUE, pdFALSE, pdMS_TO_TICKS(100)); // TODO "pyocd reset -f 500000" does otherwise not disconnect + xEventGroupWaitBits(dap_events, 0x01, pdTRUE, pdFALSE, pdMS_TO_TICKS(100)); // TODO "pyocd reset -f 500000" does otherwise not disconnect if (tud_vendor_available()) { @@ -306,7 +308,7 @@ void dap_task(void *ptr) // // initiate SWD connect / disconnect // - if ( !swd_connected && RxDataBuffer[0] == ID_DAP_Connect) { + if ( !swd_connected && RxDataBuffer[0] != ID_DAP_Info) { if (sw_lock("DAPv2", true)) { swd_connected = true; picoprobe_info("=================================== DAPv2 connect target, host %s\n", @@ -386,7 +388,7 @@ void print_task_stat(void *ptr) TaskStatus_t task_status[TASK_MAX_CNT]; uint32_t total_run_time; - vTaskDelay(pdMS_TO_TICKS(100)); + vTaskDelay(pdMS_TO_TICKS(5000)); timer_task_stat = xTimerCreate("task stat", pdMS_TO_TICKS(10000), pdTRUE, NULL, trigger_task_stat); // just for fun: exact period of 10s events_task_stat = xEventGroupCreate(); @@ -396,6 +398,14 @@ void print_task_stat(void *ptr) for (;;) { printf("---------------------------------------\n"); +#if LWIP_STATS + { + extern void stats_display(void); + stats_display(); + printf("---------------------------------------\n"); + } +#endif + printf("TinyUSB counter : %lu\n", tusb_count - prev_tusb_count); prev_tusb_count = tusb_count; vPortGetHeapStats( &heap_status); @@ -413,8 +423,8 @@ void print_task_stat(void *ptr) static uint32_t total_sum_tick_ms; uint32_t cnt; uint32_t all_delta_tick_sum_us; - uint32_t percent_sum; - uint32_t percent_total_sum; + uint32_t permille_sum; + uint32_t permille_total_sum; cnt = uxTaskGetSystemState(task_status, TASK_MAX_CNT, &total_run_time); all_delta_tick_sum_us = 0; @@ -427,6 +437,7 @@ void print_task_stat(void *ptr) all_delta_tick_sum_us += ticks_us; sum_tick_ms[task_ndx] += (ticks_us + 500) / 1000; } + printf("uptime [s] : %lu\n", clock() / CLOCKS_PER_SEC); printf("delta tick sum : %lu\n", all_delta_tick_sum_us); printf("NUM PRI S/AM CPU TOT STACK NAME\n"); @@ -435,11 +446,11 @@ void print_task_stat(void *ptr) all_delta_tick_sum_us /= configNUM_CORES; total_sum_tick_ms += (all_delta_tick_sum_us + 500) / 1000; - percent_sum = 0; - percent_total_sum = 0; + permille_sum = 0; + permille_total_sum = 0; for (uint32_t n = 0; n < cnt; ++n) { - uint32_t percent; - uint32_t percent_total; + uint32_t permille; + uint32_t permille_total; uint32_t curr_tick; uint32_t delta_tick; uint32_t task_ndx = task_status[n].xTaskNumber; @@ -447,17 +458,17 @@ void print_task_stat(void *ptr) curr_tick = task_status[n].ulRunTimeCounter; delta_tick = curr_tick - prev_tick_us[task_ndx]; - percent = (delta_tick + all_delta_tick_sum_us / 2000) / (all_delta_tick_sum_us / 1000); - percent_total = (1000 * sum_tick_ms[task_ndx] + total_sum_tick_ms / 2) / total_sum_tick_ms; - percent_sum += percent; - percent_total_sum += percent_total; + permille = (delta_tick + all_delta_tick_sum_us / 2000) / (all_delta_tick_sum_us / 1000); + permille_total = (sum_tick_ms[task_ndx] + total_sum_tick_ms / 2000) / (total_sum_tick_ms / 1000); + permille_sum += permille; + permille_total_sum += permille_total; #if defined(configUSE_CORE_AFFINITY) && configUSE_CORE_AFFINITY != 0 printf("%3lu %2lu %c/%2d %4lu %4lu %5lu %s\n", task_status[n].xTaskNumber, task_status[n].uxCurrentPriority, task_state(task_status[n].eCurrentState), (int)task_status[n].uxCoreAffinityMask, - percent, percent_total, + permille, permille_total, task_status[n].usStackHighWaterMark, task_status[n].pcTaskName); #else @@ -465,7 +476,7 @@ void print_task_stat(void *ptr) task_status[n].xTaskNumber, task_status[n].uxCurrentPriority, task_state(task_status[n].eCurrentState), 1, - percent, percent_total, + permille, permille_total, task_status[n].usStackHighWaterMark, task_status[n].pcTaskName); #endif @@ -473,7 +484,7 @@ void print_task_stat(void *ptr) prev_tick_us[task_ndx] = curr_tick; } printf("---------------------------------------\n"); - printf(" %4lu %4lu\n", percent_sum, percent_total_sum); + printf(" %4lu %4lu\n", permille_sum, permille_total_sum); } printf("---------------------------------------\n"); @@ -539,13 +550,13 @@ void usb_thread(void *ptr) #endif #if OPT_CMSIS_DAPV2 - xTaskCreate(dap_task, "CMSIS-DAP", configMINIMAL_STACK_SIZE, NULL, DAP_TASK_PRIO, &dap_taskhandle); + xTaskCreate(dap_task, "CMSIS-DAPv2", configMINIMAL_STACK_SIZE, NULL, DAPV2_TASK_PRIO, &dap_taskhandle); #endif #if configGENERATE_RUN_TIME_STATS { TaskHandle_t task_stat_handle; - xTaskCreate(print_task_stat, "Print Task Stat", configMINIMAL_STACK_SIZE, NULL, 30, &task_stat_handle); + xTaskCreate(print_task_stat, "Print Task Stat", configMINIMAL_STACK_SIZE, NULL, PRINT_STATUS_TASK_PRIO, &task_stat_handle); } #endif @@ -553,7 +564,7 @@ void usb_thread(void *ptr) // // This is the only place to set task affinity. // TODO ATTENTION core affinity - // Currently only RTT_FROM is running on an extra core (and RTT_FROM is a real hack). This is because + // Currently only "RTT-From" is running on an extra core (and "RTT-From" is a real hack). This is because // if RTT is running on a different thread than tinyusb/lwip (not sure), the probe is crashing very // fast on SystemView events in net_sysview_send() // @@ -569,8 +580,8 @@ void usb_thread(void *ptr) for (uint32_t n = 0; n < cnt; ++n) { if ( strcmp(task_status[n].pcTaskName, "IDLE1") == 0 - || strcmp(task_status[n].pcTaskName, "RTT_FROM") == 0 - || strcmp(task_status[n].pcTaskName, "RTTxxx") == 0 + || strcmp(task_status[n].pcTaskName, "RTT-From") == 0 + || strcmp(task_status[n].pcTaskName, "RTT-IO-Dont-Do-That") == 0 ) { // set it to core 1 vTaskCoreAffinitySet(task_status[n].xHandle, 1 << 1); @@ -623,7 +634,7 @@ int main(void) picoprobe_info(" %s\n", CONFIG_BOARD()); picoprobe_info("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); - events = xEventGroupCreate(); + dap_events = xEventGroupCreate(); // it seems that TinyUSB does not like affinity setting in its thread, so the affinity of the USB thread is corrected in the task itself xTaskCreate(usb_thread, "TinyUSB Main", 4096, NULL, TUD_TASK_PRIO, &tud_taskhandle); @@ -690,7 +701,7 @@ void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t rep // // initiate SWD connect / disconnect // - if ( !hid_swd_connected && RxDataBuffer[0] == ID_DAP_Connect) { + if ( !hid_swd_connected && RxDataBuffer[0] != ID_DAP_Info) { if (sw_lock("DAPv1", true)) { hid_swd_connected = true; picoprobe_info("=================================== DAPv1 connect target\n"); diff --git a/src/net/net_glue.c b/src/net/net_glue.c index 116ff122d..999f3af6b 100755 --- a/src/net/net_glue.c +++ b/src/net/net_glue.c @@ -26,12 +26,11 @@ //---------------------------------------------------------------------------------------------------------------------- // -// TCP server for SystemView -// - using RNDIS / ECM because it is driver free for Windows / Linux / iOS -// - we leave the IPv6 stuff outside +// Stuff to glue lwIP and TinyUSB together. +// Code might be confusing because it is trying hard to call everything in the right context. +// It is assumed that net_glue is running in a FreeRTOS environment. // - #include #include #include @@ -42,19 +41,23 @@ #include "dhserver.h" #include "tusb.h" +#include "device/usbd_pvt.h" // for usbd_defer_func +#include "tinyusb/net_device.h" -#define EV_RCVFRAME_READY 1 - - -/* lwip context */ +/// lwIP context static struct netif netif_data; -/* shared between tud_network_recv_cb() and service_traffic() */ -static struct pbuf *received_frame; +/// Buffer for lwIP <- TinyUSB transmission +static uint8_t rcv_buff[CFG_TUD_NET_MTU + 10]; // MTU plus some margin +static uint16_t rcv_buff_len = 0; + +/// Buffer for lwIP -> TinyUSB transmission +static uint8_t xmt_buff[CFG_TUD_NET_MTU + 10]; // MTU plus some margin +static uint16_t xmt_buff_len = 0; #ifndef OPT_NET_192_168 - #define OPT_NET_192_168 10 + #define OPT_NET_192_168 14 #endif /* network parameters of this MCU */ @@ -66,70 +69,91 @@ static const ip4_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0); static dhcp_entry_t entries[] = { /* mac ip address lease time */ - { {0}, IPADDR4_INIT_BYTES(192, 168, OPT_NET_192_168, 2), 24 * 60 * 60 }, + { + .mac = {0}, + .addr = IPADDR4_INIT_BYTES(192, 168, OPT_NET_192_168, 2), + .lease = 24 * 60 * 60 + }, }; static const dhcp_config_t dhcp_config = { - .router = IPADDR4_INIT_BYTES(0, 0, 0, 0), // router address (if any) - .port = 67, // listen port - .dns = IPADDR4_INIT_BYTES(0, 0, 0, 0), // dns server - NULL, // dns suffix: specify NULL, otherwise /etc/resolv.conf will be changed - TU_ARRAY_SIZE(entries), // num entry - entries // entries + .router = IPADDR4_INIT_BYTES(0, 0, 0, 0), // router address (if any) + .port = 67, // listen port + .dns = IPADDR4_INIT_BYTES(0, 0, 0, 0), // dns server + .domain = NULL, // dns suffix: specify NULL, otherwise /etc/resolv.conf will be changed + .num_entry = TU_ARRAY_SIZE(entries), // num entry + .entries = entries // entries }; void tud_network_init_cb(void) +/** + * initialize any network state back to the beginning + */ { - /* if the network is re-initializing and we have a leftover packet, we must do a cleanup */ - if (received_frame != NULL) - { - pbuf_free(received_frame); - received_frame = NULL; - } + rcv_buff_len = 0; + xmt_buff_len = 0; } // tud_network_init_cb +static void context_tinyusb_tud_network_recv_renew(void *param) +/** + * Reenable reception logic in TinyUSB. + * + * Context: TinyUSB + */ +{ + tud_network_recv_renew(); +} // context_tinyusb_tud_network_recv_renew + + + static void net_glue_usb_to_lwip(void *ptr) /** - * handle any packet received by tud_network_recv_cb() in context of lwIP + * Handle any packet received by tud_network_recv_cb() + * + * Context: lwIP */ { //printf("net_glue_usb_to_lwip\n"); - if (received_frame != NULL) { - ethernet_input(received_frame, &netif_data); - pbuf_free(received_frame); - received_frame = NULL; - tud_network_recv_renew(); + if (rcv_buff_len != 0) { + struct pbuf *p = pbuf_alloc(PBUF_RAW, rcv_buff_len, PBUF_POOL); + + if (p) { + memcpy(p->payload, rcv_buff, rcv_buff_len); + ethernet_input(p, &netif_data); + pbuf_free(p); + rcv_buff_len = 0; + usbd_defer_func(context_tinyusb_tud_network_recv_renew, NULL, false); + } } } // net_glue_usb_to_lwip bool tud_network_recv_cb(const uint8_t *src, uint16_t size) +/** + * Copy buffer (host ->) TinyUSB -> lwIP (-> application) + * + * Context: TinyUSB + * + * \return false if the packet buffer was not accepted + */ { - //printf("!!!!!!!!!!!!!!tud_network_recv_cb(%p,%u)\n", src, size); + //printf("tud_network_recv_cb(%p,%u)\n", src, size); - /* this shouldn't happen, but if we get another packet before - parsing the previous, we must signal our inability to accept it */ - if (received_frame) + if (rcv_buff_len != 0) return false; - if (size) { - struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); - - if (p) { - /* pbuf_alloc() has already initialized struct; all we need to do is copy the data */ - memcpy(p->payload, src, size); - - /* store away the pointer for service_traffic() to later handle */ - received_frame = p; - tcpip_callback_with_block(net_glue_usb_to_lwip, NULL, 0); - } + if (size != 0) { + assert(size < sizeof(rcv_buff)); + memcpy(rcv_buff, src, size); + rcv_buff_len = size; + tcpip_callback_with_block(net_glue_usb_to_lwip, NULL, 0); } return true; } // tud_network_recv_cb @@ -137,53 +161,68 @@ bool tud_network_recv_cb(const uint8_t *src, uint16_t size) uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) +/** + * This does the actual copy operation into a TinyUSB buffer. + * Called by tud_network_xmit(). + * + * (application ->) lwIP -> TinyUSB (-> host) + * + * Context: TinyUSB + * + * \return number of bytes copied + */ { //printf("!!!!!!!!!!!!!!tud_network_xmit_cb(%p,%p,%u)\n", dst, ref, arg); -#if 0 - struct pbuf *p = (struct pbuf *)ref; - struct pbuf *q; - uint16_t len = 0; - - (void)arg; /* unused for this example */ - - /* traverse the "pbuf chain"; see ./lwip/src/core/pbuf.c for more info */ - for (q = p; q != NULL; q = q->next) - { - memcpy(dst, (char *)q->payload, q->len); - dst += q->len; - len += q->len; - if (q->len == q->tot_len) - break; - } + uint16_t r = xmt_buff_len; + memcpy(dst, xmt_buff, xmt_buff_len); + xmt_buff_len = 0; + return r; +} // tud_network_xmit_cb - return len; -#else - struct pbuf *p = (struct pbuf *)ref; - (void)arg; /* unused for this example */ - return pbuf_copy_partial(p, dst, p->tot_len, 0); -#endif -} // tud_network_xmit_cb +static void context_tinyusb_linkoutput(void *param) +/** + * Put \a xmt_buff into TinyUSB (if possible). + * + * Context: TinyUSB + */ +{ + if ( !tud_network_can_xmit(xmt_buff_len)) { + //printf("context_tinyusb_linkoutput: sleep\n"); + vTaskDelay(pdMS_TO_TICKS(5)); + usbd_defer_func(context_tinyusb_linkoutput, NULL, false); + } + else { + tud_network_xmit(xmt_buff, 0); + } +} // context_tinyusb_linkoutput static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) +/** + * called by lwIP to transmit data to TinyUSB + * + * Context: lwIP + */ { - (void)netif; - - for (;;) { - /* 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 ( !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 /* unused for this example */); - return ERR_OK; - } + if (xmt_buff_len != 0) { + return ERR_USE; } + + // copy data into temp buffer + xmt_buff_len = pbuf_copy_partial(p, xmt_buff, p->tot_len, 0); + assert(xmt_buff_len < sizeof(xmt_buff)); + + usbd_defer_func(context_tinyusb_linkoutput, NULL, false); + + return ERR_OK; } // linkoutput_fn diff --git a/src/net/net_sysview.c b/src/net/net_sysview.c index 76e4af68a..4414359d8 100755 --- a/src/net/net_sysview.c +++ b/src/net/net_sysview.c @@ -23,6 +23,12 @@ * */ +//---------------------------------------------------------------------------------------------------------------------- +// +// TCP server for SystemView +// - using NCM because it is driver free for Windows / Linux / iOS +// - we leave the IPv6 stuff outside +// #include "FreeRTOS.h" #include "stream_buffer.h" @@ -131,12 +137,14 @@ static void sysview_try_send(void *ctx) if (cnt != 0) { // the write has either 512 bytes (so send it) or the stream is empty (so send it as well) err = tcp_write(m_pcb_client, tx_buf, cnt, TCP_WRITE_FLAG_COPY); + //printf("sysview_try_send: %d %d\n", cnt, block_call_to_tcp_output); if (err != ERR_OK) { picoprobe_error("sysview_try_send/a: %d\n", err); sysview_close(m_pcb_client); } - if ( !block_call_to_tcp_output && tcp_sndbuf(m_pcb_client) < TCP_SND_BUF / 2) { + if ( !block_call_to_tcp_output && tcp_sndbuf(m_pcb_client) < 2 * TCP_SND_BUF / 4) { + //printf("sysview_try_send: flush %d %d\n", tcp_sndbuf(m_pcb_client), 3 * TCP_SND_BUF / 4); block_call_to_tcp_output = true; tcp_output(m_pcb_client); } @@ -146,6 +154,14 @@ static void sysview_try_send(void *ctx) } } } + else { +// printf("sysview_try_send: no tcp_sndbuf!!!!\n"); + if ( !block_call_to_tcp_output) { + //printf("sysview_try_send: flush\n"); + block_call_to_tcp_output = true; + tcp_output(m_pcb_client); + } + } } } // sysview_try_send @@ -250,7 +266,8 @@ err_t sysview_poll(void *arg, struct tcp_pcb *tpcb) { //printf("sysview_poll(%p,%p) %d\n", arg, tpcb, m_state); - sysview_try_send(NULL); + //sysview_try_send(NULL); + tcpip_callback_with_block(sysview_try_send, NULL, 0); return ERR_OK; } // sysview_poll @@ -273,6 +290,14 @@ static err_t sysview_accept(void *arg, struct tcp_pcb *newpcb, err_t err) return ERR_OK; } // sysview_accept +// TODO recheck here + + +bool net_sysview_is_connected(void) +{ + return m_state == SVS_READY; +} // net_sysview_is_connected + uint32_t net_sysview_send(const uint8_t *buf, uint32_t cnt) @@ -281,7 +306,7 @@ uint32_t net_sysview_send(const uint8_t *buf, uint32_t cnt) * * \param buf pointer to the buffer to be sent, if NULL then remaining space in stream is returned * \param cnt number of bytes to be sent - * \return if \buf is NULL the remaining space in stream is returned, otherwise the number of bytes + * \return if \buf is NULL the remaining space in stream is returned, otherwise the number of bytes sent */ { uint32_t r = 0; @@ -295,7 +320,7 @@ uint32_t net_sysview_send(const uint8_t *buf, uint32_t cnt) r = xStreamBufferSpacesAvailable(stream_sysview_to_host); } else { - if (m_state != SVS_READY) + if ( !net_sysview_is_connected()) { xStreamBufferReset(stream_sysview_to_host); } diff --git a/src/net/net_sysview.h b/src/net/net_sysview.h index 1e53b4cd5..9b797bd00 100755 --- a/src/net/net_sysview.h +++ b/src/net/net_sysview.h @@ -34,7 +34,7 @@ void net_sysview_init(void); uint32_t net_sysview_send(const uint8_t *buf, uint32_t cnt); - +bool net_sysview_is_connected(void); #ifdef __cplusplus } diff --git a/src/net/tinyusb/ncm.h b/src/net/tinyusb/ncm.h new file mode 100755 index 000000000..6f9f064ed --- /dev/null +++ b/src/net/tinyusb/ncm.h @@ -0,0 +1,135 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2021, Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + + +#ifndef _TUSB_NCM_H_ +#define _TUSB_NCM_H_ + +#include "common/tusb_common.h" + + +#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE + /// must be >> MTU + #define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200 +#endif + +#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE + /// must be >> MTU + #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 + + +// Table 4.3 Data Class Interface Protocol Codes +typedef enum +{ + NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01 +} ncm_data_interface_protocol_code_t; + + +// Table 6.2 Class-Specific Request Codes for Network Control Model subclass +typedef enum +{ + NCM_SET_ETHERNET_MULTICAST_FILTERS = 0x40, + NCM_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x41, + NCM_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER = 0x42, + NCM_SET_ETHERNET_PACKET_FILTER = 0x43, + NCM_GET_ETHERNET_STATISTIC = 0x44, + NCM_GET_NTB_PARAMETERS = 0x80, + NCM_GET_NET_ADDRESS = 0x81, + NCM_SET_NET_ADDRESS = 0x82, + NCM_GET_NTB_FORMAT = 0x83, + NCM_SET_NTB_FORMAT = 0x84, + NCM_GET_NTB_INPUT_SIZE = 0x85, + NCM_SET_NTB_INPUT_SIZE = 0x86, + NCM_GET_MAX_DATAGRAM_SIZE = 0x87, + NCM_SET_MAX_DATAGRAM_SIZE = 0x88, + NCM_GET_CRC_MODE = 0x89, + NCM_SET_CRC_MODE = 0x8A, +} ncm_request_code_t; + + +#define NTH16_SIGNATURE 0x484D434E +#define NDP16_SIGNATURE_NCM0 0x304D434E +#define NDP16_SIGNATURE_NCM1 0x314D434E + +typedef struct TU_ATTR_PACKED { + uint16_t wLength; + uint16_t bmNtbFormatsSupported; + uint32_t dwNtbInMaxSize; + uint16_t wNdbInDivisor; + uint16_t wNdbInPayloadRemainder; + uint16_t wNdbInAlignment; + uint16_t wReserved; + uint32_t dwNtbOutMaxSize; + uint16_t wNdbOutDivisor; + uint16_t wNdbOutPayloadRemainder; + uint16_t wNdbOutAlignment; + uint16_t wNtbOutMaxDatagrams; +} ntb_parameters_t; + +typedef struct TU_ATTR_PACKED { + uint32_t dwSignature; + uint16_t wHeaderLength; + uint16_t wSequence; + uint16_t wBlockLength; + uint16_t wNdpIndex; +} nth16_t; + +typedef struct TU_ATTR_PACKED { + uint16_t wDatagramIndex; + uint16_t wDatagramLength; +} ndp16_datagram_t; + +typedef struct TU_ATTR_PACKED { + uint32_t dwSignature; + uint16_t wLength; + uint16_t wNextNdpIndex; + ndp16_datagram_t datagram[]; +} ndp16_t; + +typedef union TU_ATTR_PACKED { + struct { + nth16_t nth; + ndp16_t ndp; + }; + uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE]; +} transmit_ntb_t; + +struct ncm_notify_struct { + tusb_control_request_t header; + uint32_t downlink, uplink; +}; + + +#endif diff --git a/src/net/tinyusb/ncm_device.c b/src/net/tinyusb/ncm_device.c new file mode 100755 index 000000000..ef2ddb8c3 --- /dev/null +++ b/src/net/tinyusb/ncm_device.c @@ -0,0 +1,618 @@ +/* + * 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" + +#warning "Don't use this driver because it is (very) buggy. It is for reference only." + +#if ECLIPSE_GUI || ( CFG_TUD_ENABLED && CFG_TUD_NCM ) + +#if ECLIPSE_GUI + #define tu_static static +#endif + +#include +#include "device/usbd.h" +#include "device/usbd_pvt.h" +#include "net_device.h" +#include "ncm.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +typedef struct { + uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface + uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active + + uint8_t ep_notif; + uint8_t ep_in; + uint8_t ep_out; + + const ndp16_t *rcv_ndp; + uint8_t rcv_datagram_num; + uint8_t rcv_datagram_index; + CFG_TUSB_MEM_ALIGN uint8_t rcv_datagram[CFG_TUD_NCM_OUT_NTB_MAX_SIZE]; + uint16_t rcv_usb_datagram_size; + + 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 xmt_running; + +} ncm_interface_t; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = { + .wLength = sizeof(ntb_parameters_t), + .bmNtbFormatsSupported = 0x01, // 16-bit NTB supported + .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 // 0=no limit TODO set to 0 +}; + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static transmit_ntb_t transmit_ntb[2]; + +tu_static ncm_interface_t ncm_interface; + +static void ncm_prepare_for_tx(void) +/** + * Set up the NTB state in ncm_interface to be ready to add datagrams. + * + * \pre + * \a ncm_interface.current_ntb must be set correctly + */ +{ + //printf("ncm_prepare_for_tx()\n"); + 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)); + memset(transmit_ntb + ncm_interface.current_ntb, 0, sizeof(transmit_ntb_t)); +} + + + +/* + * If not already transmitting, start sending the current NTB to the host and swap buffers + * to start filling the other one with datagrams. + */ +static void ncm_start_tx(void) +{ + //printf("ncm_start_tx() - %d %d\n", ncm_interface.xmt_running, ncm_interface.datagram_count); + + if (ncm_interface.xmt_running) { + 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(0, ncm_interface.ep_in, ntb->data, ntb_length); + ncm_interface.xmt_running = true; + + // Swap to the other NTB and clear it out + ncm_interface.current_ntb = 1 - ncm_interface.current_ntb; + ncm_prepare_for_tx(); +} + + + +tu_static struct ncm_notify_struct ncm_notify_connected = { + .header = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN + }, + .bRequest = CDC_NOTIF_NETWORK_CONNECTION, + .wValue = 1 /* Connected */, + .wLength = 0, + }, +}; + +tu_static struct ncm_notify_struct ncm_notify_speed_change = { + .header = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN + }, + .bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE, + .wLength = 8, + }, + .downlink = 1000000, + .uplink = 1000000, +}; + + + +void tud_network_recv_renew(void) +/** + * context: lwIP & TinyUSB + */ +{ + //printf("tud_network_recv_renew() - %d [%p]\n", ncm_interface.rcv_datagram_num, xTaskGetCurrentTaskHandle()); + if (ncm_interface.rcv_datagram_index >= ncm_interface.rcv_datagram_num) { + + //printf("--0\n"); + + if (ncm_interface.rcv_usb_datagram_size == 0) { + if (usbd_edpt_busy(0, ncm_interface.ep_out)) { + printf("--0.1\n"); + return; + } + else { + bool r = usbd_edpt_xfer(0, ncm_interface.ep_out, ncm_interface.rcv_datagram, CFG_TUD_NCM_OUT_NTB_MAX_SIZE); + if ( !r) { + printf("--0.2\n"); + return; + } + } + } + + //printf("--1\n"); + + const nth16_t *hdr = (const nth16_t*)ncm_interface.rcv_datagram; + if (ncm_interface.rcv_usb_datagram_size < sizeof(nth16_t) + sizeof(ndp16_t) + 2*sizeof(ndp16_datagram_t)) { + //printf("--1.1.1 %d\n", ncm_interface.rcv_usb_datagram_size); + return; + } + if (hdr->dwSignature != NTH16_SIGNATURE) { + printf("--1.1.2 0x%lx %d\n", hdr->dwSignature, ncm_interface.rcv_usb_datagram_size); + return; + } + if (hdr->wNdpIndex < sizeof(nth16_t)) { + printf("--1.1.3 %d\n", hdr->wNdpIndex); + return; + } + if (hdr->wNdpIndex + sizeof(ndp16_t) > ncm_interface.rcv_usb_datagram_size) { + printf("--1.1.4 %d %d\n", hdr->wNdpIndex + sizeof(ndp16_t), ncm_interface.rcv_usb_datagram_size); + return; + } + + const ndp16_t *ndp = (const ndp16_t*) (ncm_interface.rcv_datagram + hdr->wNdpIndex); + if (hdr->wNdpIndex + ndp->wLength > ncm_interface.rcv_usb_datagram_size) { + printf("--1.2.1 %d %d\n", hdr->wNdpIndex + ndp->wLength, ncm_interface.rcv_usb_datagram_size); + return; + } + if (ndp->dwSignature != NDP16_SIGNATURE_NCM0 && ndp->dwSignature != NDP16_SIGNATURE_NCM1) { + printf("--1.2.2 0x%lx %d\n", ndp->dwSignature, ncm_interface.rcv_usb_datagram_size); + return; + } + + //printf("--2\n"); + + int max_rcv_datagrams = (ndp->wLength - 8) / 4; + ncm_interface.rcv_datagram_index = 0; + ncm_interface.rcv_datagram_num = 0; + ncm_interface.rcv_ndp = ndp; + while (ncm_interface.rcv_datagram_num < max_rcv_datagrams) + { +#if 0 + printf(" %d %d %d\n", ncm_interface.rcv_datagram_num, + ndp->datagram[ncm_interface.rcv_datagram_num].wDatagramIndex, + ndp->datagram[ncm_interface.rcv_datagram_num].wDatagramLength); +#endif + if ( ndp->datagram[ncm_interface.rcv_datagram_num].wDatagramIndex == 0 + && ndp->datagram[ncm_interface.rcv_datagram_num].wDatagramLength == 0) { + break; + } + ++ncm_interface.rcv_datagram_num; + } + +#if 0 + printf("tud_network_recv_renew: %d 0x%08lx %d %d\n", ncm_interface.rcv_datagram_num, ndp->dwSignature, ndp->wLength, + ndp->wNextNdpIndex); +#endif + + ncm_interface.rcv_usb_datagram_size = 0; + } + + if (ncm_interface.rcv_datagram_num == 0) { + return; + } + + const ndp16_t *ndp = ncm_interface.rcv_ndp; + +#if 0 + printf("tud_network_recv_renew->: %d %p %d %d\n", ncm_interface.rcv_datagram_index, + ndp, ndp->datagram[ncm_interface.rcv_datagram_index].wDatagramIndex, + ndp->datagram[ncm_interface.rcv_datagram_index].wDatagramLength); +#endif + + if (tud_network_recv_cb(ncm_interface.rcv_datagram + ndp->datagram[ncm_interface.rcv_datagram_index].wDatagramIndex, + ndp->datagram[ncm_interface.rcv_datagram_index].wDatagramLength)) { + //printf("!!!!!!!!!!!!!!!!!!!!\n"); + ++ncm_interface.rcv_datagram_index; + } +} + + + +static void handle_incoming_datagram(uint32_t len) +{ + //printf("!!!!!!!!!!!!!handle_incoming_datagram(%lu) %d\n", len, ncm_interface.rcv_usb_datagram_size); + + ncm_interface.rcv_usb_datagram_size = len; + + tud_network_recv_renew(); +} + + + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ + +void netd_init(void) +/** + * called on start + * + * context: TinyUSB + */ +{ + printf("netd_init() [%p]\n", xTaskGetCurrentTaskHandle()); + + tu_memclr(&ncm_interface, sizeof(ncm_interface)); + ncm_interface.ntb_in_size = CFG_TUD_NCM_IN_NTB_MAX_SIZE; + ncm_interface.max_datagrams_per_ntb = CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB; + ncm_prepare_for_tx(); +} + + + +void netd_reset(uint8_t rhport) +/** + * called with rhport=0 + * + * context: TinyUSB + */ +{ + (void) rhport; + + printf("netd_reset(%d) [%p]\n", rhport, xTaskGetCurrentTaskHandle()); + + netd_init(); +} + + + +uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +/** + * called with max_len=143 + * + * context: TinyUSB + */ +{ + // confirm interface hasn't already been allocated + TU_ASSERT(0 == ncm_interface.ep_notif, 0); + + printf("netd_open(%d,%p,%d) [%p]\n", rhport, itf_desc, max_len, xTaskGetCurrentTaskHandle()); + + //------------- Management Interface -------------// + ncm_interface.itf_num = itf_desc->bInterfaceNumber; + + uint16_t drv_len = sizeof(tusb_desc_interface_t); + uint8_t const *p_desc = tu_desc_next(itf_desc); + + // Communication Functional Descriptors + while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) { + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + // notification endpoint (if any) + if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) { + TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const* ) p_desc), 0); + + ncm_interface.ep_notif = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface -------------// + // - CDC-NCM data interface has 2 alternate settings + // - 0 : zero endpoints for inactive (default) + // - 1 : IN & OUT endpoints for transfer of NTBs + TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0); + + do { + tusb_desc_interface_t const *data_itf_desc = (tusb_desc_interface_t const*) p_desc; + TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0); + + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } while ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len)); + + // Pair of endpoints + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0); + + TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in)); + + drv_len += 2 * sizeof(tusb_desc_endpoint_t); + + return drv_len; +} + + + +static void ncm_report(void) +/** + * called on init + */ +{ + printf("ncm_report - %d\n", ncm_interface.report_state); + uint8_t const rhport = 0; + if (ncm_interface.report_state == REPORT_SPEED) { + ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num; + usbd_edpt_xfer(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(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_notify_connected, sizeof(ncm_notify_connected)); + ncm_interface.report_state = REPORT_DONE; + ncm_interface.report_pending = true; + } +} + + + +TU_ATTR_WEAK void tud_network_link_state_cb(bool state) +/** + * called on init three times with 1/0/1 + * + * context: TinyUSB + */ +{ + (void) state; + printf("tud_network_link_state_cb(%d) [%p]\n", state, xTaskGetCurrentTaskHandle()); +} + + + +// Handle class control request +// return false to stall control endpoint (e.g unsupported request) +bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) +/** + * Called on init of connection + * + * context: TinyUSB + */ +{ + printf("netd_control_xfer_cb(%d, %d, %p) [%p]\n", rhport, stage, request, xTaskGetCurrentTaskHandle()); + + if (stage != CONTROL_STAGE_SETUP) + return true ; + + switch (request->bmRequestType_bit.type) { + case TUSB_REQ_TYPE_STANDARD: + switch (request->bRequest) { + case TUSB_REQ_GET_INTERFACE: { + uint8_t const req_itfnum = (uint8_t) request->wIndex; + TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum); + + tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1); + } + break; + + case TUSB_REQ_SET_INTERFACE: { + uint8_t const req_itfnum = (uint8_t) request->wIndex; + uint8_t const req_alt = (uint8_t) request->wValue; + + // Only valid for Data Interface with Alternate is either 0 or 1 + TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2); + + if (req_alt != ncm_interface.itf_data_alt) { + ncm_interface.itf_data_alt = req_alt; + + if (ncm_interface.itf_data_alt) { + if (!usbd_edpt_busy(rhport, ncm_interface.ep_out)) { + tud_network_recv_renew(); // prepare for incoming datagrams + } + if (!ncm_interface.report_pending) { + ncm_report(); + } + } + + tud_network_link_state_cb(ncm_interface.itf_data_alt); + } + + tud_control_status(rhport, request); + } + break; + + // unsupported request + default: + return false ; + } + break; + + case TUSB_REQ_TYPE_CLASS: + TU_VERIFY(ncm_interface.itf_num == request->wIndex); + + //printf("netd_control_xfer_cb/TUSB_REQ_TYPE_CLASS: %d\n", request->bRequest); + + if (NCM_GET_NTB_PARAMETERS == request->bRequest) { + tud_control_xfer(rhport, request, (void*) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters)); + } + + break; + + // unsupported request + default: + return false ; + } + + return true ; +} + + + +bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +/** + * context: TinyUSB + */ +{ + (void) rhport; + (void) result; + + //printf("netd_xfer_cb(%d,%d,%d,%lu) [%p]\n", rhport, ep_addr, result, xferred_bytes, xTaskGetCurrentTaskHandle()); + + /* new datagram rcv_datagram */ + if (ep_addr == ncm_interface.ep_out) { + //printf(" EP_OUT %d %d %d %lu\n", rhport, ep_addr, result, xferred_bytes); + handle_incoming_datagram(xferred_bytes); + } + + /* data transmission finished */ + if (ep_addr == ncm_interface.ep_in) { + //printf(" EP_IN %d %d %d\n", ncm_interface.xmt_running, ncm_interface.datagram_count, ncm_interface.itf_data_alt); + ncm_interface.xmt_running = false; + + if (xferred_bytes != 0 && xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE == 0) + { + // TODO check when ZLP is really needed + ncm_interface.xmt_running = true; + usbd_edpt_xfer(0, ncm_interface.ep_in, NULL, 0); + } + else { + // If there are datagrams queued up that we tried to send while this NTB was being emitted, send them now + if (ncm_interface.datagram_count && ncm_interface.itf_data_alt == 1) { + ncm_start_tx(); + } + } + } + + if (ep_addr == ncm_interface.ep_notif) { + printf(" EP_NOTIF\n"); + ncm_interface.report_pending = false; + ncm_report(); + } + + return true ; +} + + + +bool tud_network_can_xmit(uint16_t size) +/** + * poll network driver for its ability to accept another packet to transmit + * + * context: lwIP + * + */ +{ + TU_VERIFY(ncm_interface.itf_data_alt == 1); + +#if 0 + printf("tud_network_can_xmit(%d) %d %d - %d %d [%p]\n", size, ncm_interface.datagram_count, ncm_interface.max_datagrams_per_ntb, + ncm_interface.next_datagram_offset, ncm_interface.ntb_in_size, xTaskGetCurrentTaskHandle()); +#endif + + if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) { + // this happens if max... is set to 1 + printf("NTB full [by count]\r\n"); + return false ; + } + + if (ncm_interface.next_datagram_offset + size > ncm_interface.ntb_in_size) { + // this happens + printf("ntb full [by size]\r\n"); + return false ; + } + + return true ; +} + + + +void tud_network_xmit(void *ref, uint16_t arg) +/** + * context: lwIP. + */ +{ + transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb]; + + //printf("tud_network_xmit(%p,%d) [%p]\n", ref, arg, xTaskGetCurrentTaskHandle()); + + uint16_t size = tud_network_xmit_cb(ntb->data + ncm_interface.next_datagram_offset, ref, arg); + + ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = ncm_interface.next_datagram_offset; + ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = size; + + ncm_interface.datagram_count++; + ncm_interface.next_datagram_offset += size; + + // round up so the next datagram is aligned correctly + ncm_interface.next_datagram_offset += (CFG_TUD_NCM_ALIGNMENT - 1); + ncm_interface.next_datagram_offset -= (ncm_interface.next_datagram_offset % CFG_TUD_NCM_ALIGNMENT); + + ncm_start_tx(); +} + +#endif diff --git a/src/net/tinyusb/ncm_device_simple.c b/src/net/tinyusb/ncm_device_simple.c new file mode 100755 index 000000000..041fa4a7a --- /dev/null +++ b/src/net/tinyusb/ncm_device_simple.c @@ -0,0 +1,553 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2023 Hardy Griech + * 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 ECLIPSE_GUI || ( CFG_TUD_ENABLED && CFG_TUD_NCM ) + +#if ECLIPSE_GUI + #define tu_static static +#endif + +#include +#include "device/usbd.h" +#include "device/usbd_pvt.h" + +#include "net_device.h" + +#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE + #define CFG_TUD_NCM_IN_NTB_MAX_SIZE (CFG_TUD_NET_MTU+200) +#endif + +#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE + #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (CFG_TUD_NET_MTU+200) +#endif + +#include "ncm.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +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; + + CFG_TUSB_MEM_ALIGN uint8_t rcv_ntb[CFG_TUD_NCM_OUT_NTB_MAX_SIZE]; + + enum { + REPORT_SPEED, + REPORT_CONNECTED, + REPORT_DONE + } report_state; + bool report_pending; + + uint16_t nth_sequence; // Sequence number counter for transmitted NTBs + + bool can_xmit; +} ncm_interface_t; + +//--------------------------------------------------------------------+ +// INTERNAL OBJECT & FUNCTION DECLARATION +//--------------------------------------------------------------------+ + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = { + .wLength = sizeof(ntb_parameters_t), + .bmNtbFormatsSupported = 0x01, // 16-bit NTB supported + .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 = 1 // 0=no limit TODO set to 0 +}; + +CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static transmit_ntb_t transmit_ntb; + +tu_static ncm_interface_t ncm_interface; + + + +static void ncm_prepare_for_tx(void) +/** + * Set up the NTB state in ncm_interface to be ready to add datagrams. + */ +{ + //printf("ncm_prepare_for_tx()\n"); + memset(&transmit_ntb, 0, sizeof(transmit_ntb_t)); + ncm_interface.can_xmit = true; +} // ncm_prepare_for_tx + + + +tu_static struct ncm_notify_struct ncm_notify_connected = { + .header = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN + }, + .bRequest = CDC_NOTIF_NETWORK_CONNECTION, + .wValue = 1 /* Connected */, + .wLength = 0, + }, +}; + +tu_static struct ncm_notify_struct ncm_notify_speed_change = { + .header = { + .bmRequestType_bit = { + .recipient = TUSB_REQ_RCPT_INTERFACE, + .type = TUSB_REQ_TYPE_CLASS, + .direction = TUSB_DIR_IN + }, + .bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE, + .wLength = 8, + }, + .downlink = 10000000, + .uplink = 10000000, +}; + + + +void tud_network_recv_renew(void) +/** + * context: TinyUSB + */ +{ + // printf("tud_network_recv_renew() - [%p]\n", xTaskGetCurrentTaskHandle()); + + if (usbd_edpt_busy(0, ncm_interface.ep_out)) { + printf("--0.1\n"); + return; + } + else { + bool r = usbd_edpt_xfer(0, ncm_interface.ep_out, ncm_interface.rcv_ntb, CFG_TUD_NCM_OUT_NTB_MAX_SIZE); + if ( !r) { + printf("--0.2\n"); + return; + } + } +} + + + +static void do_in_xfer(uint8_t *buf, uint16_t len) +{ + ncm_interface.can_xmit = false; + usbd_edpt_xfer(0, ncm_interface.ep_in, buf, len); +} // do_in_xfer + + + +static void handle_incoming_datagram(uint32_t len) +/** + * Handle an incoming NTP. + * Most is checking validity of the frame. If the frame is not valid, it is rejected. + * Input NTP is in \a ncm_interface.rcv_ntb. + */ +{ + bool ok = true; + + //printf("!!!!!!!!!!!!!handle_incoming_datagram(%lu)\n", len); + + const nth16_t *hdr = (const nth16_t*)ncm_interface.rcv_ntb; + const ndp16_t *ndp = NULL; + + if (len < sizeof(nth16_t) + sizeof(ndp16_t) + 2*sizeof(ndp16_datagram_t)) { + printf("--1.1.1 %ld\n", len); + ok = false; + } + else if (hdr->dwSignature != NTH16_SIGNATURE) { + printf("--1.1.2 0x%lx %ld\n", hdr->dwSignature, len); + ok = false; + } + else if (hdr->wNdpIndex < sizeof(nth16_t)) { + printf("--1.1.3 %d\n", hdr->wNdpIndex); + ok = false; + } + else if (hdr->wNdpIndex + sizeof(ndp16_t) > len) { + printf("--1.1.4 %d %ld\n", hdr->wNdpIndex + sizeof(ndp16_t), len); + ok = false; + } + + if (ok) { + ndp = (const ndp16_t*) (ncm_interface.rcv_ntb + hdr->wNdpIndex); + + if (hdr->wNdpIndex + ndp->wLength > len) { + printf("--1.2.1 %d %ld\n", hdr->wNdpIndex + ndp->wLength, len); + ok = false; + } + else if (ndp->dwSignature != NDP16_SIGNATURE_NCM0 && ndp->dwSignature != NDP16_SIGNATURE_NCM1) { + printf("--1.2.2 0x%lx %ld\n", ndp->dwSignature, len); + ok = false; + } + } + + if (ok) { + if (ndp->datagram[1].wDatagramIndex != 0 || ndp->datagram[1].wDatagramLength != 0) { + printf("--1.3.1 %d %d\n", ndp->datagram[1].wDatagramIndex, ndp->datagram[1].wDatagramLength); + ok = false; + } + else if (ndp->wNextNdpIndex != 0) { + printf("--1.3.2 %d\n", ndp->wNextNdpIndex); + ok = false; + } + else if (ndp->datagram[0].wDatagramIndex + ndp->datagram[0].wDatagramLength > len) { + printf("--1.3.3 %d %d %ld\n", ndp->datagram[0].wDatagramIndex, ndp->datagram[0].wDatagramLength, len); + ok = false; + } + } + + if (ok) { + if ( !tud_network_recv_cb(ncm_interface.rcv_ntb + ndp->datagram[0].wDatagramIndex, + ndp->datagram[0].wDatagramLength)) { + ok = false; + } + } + + if ( !ok) { + // receiver must be reenabled to get a chance to recover + printf("!!!!!!!!!!!!!!!!!!!!\n"); + tud_network_recv_renew(); + } +} // handle_incoming_datagram + + + +//--------------------------------------------------------------------+ +// USBD Driver API +//--------------------------------------------------------------------+ + +void netd_init(void) +/** + * called on start + * + * context: TinyUSB + */ +{ +// printf("netd_init() [%p]\n", xTaskGetCurrentTaskHandle()); + + tu_memclr(&ncm_interface, sizeof(ncm_interface)); + ncm_prepare_for_tx(); +} + + + +void netd_reset(uint8_t rhport) +/** + * called with rhport=0 + * + * context: TinyUSB + */ +{ + (void) rhport; + +// printf("netd_reset(%d) [%p]\n", rhport, xTaskGetCurrentTaskHandle()); + + netd_init(); +} + + + +uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) +/** + * called with max_len=143 + * + * context: TinyUSB + */ +{ + // confirm interface hasn't already been allocated + TU_ASSERT(0 == ncm_interface.ep_notif, 0); + +// printf("netd_open(%d,%p,%d) [%p]\n", rhport, itf_desc, max_len, xTaskGetCurrentTaskHandle()); + + //------------- Management Interface -------------// + ncm_interface.itf_num = itf_desc->bInterfaceNumber; + + uint16_t drv_len = sizeof(tusb_desc_interface_t); + uint8_t const *p_desc = tu_desc_next(itf_desc); + + // Communication Functional Descriptors + while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) { + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + // notification endpoint (if any) + if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) { + TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const* ) p_desc), 0); + + ncm_interface.ep_notif = ((tusb_desc_endpoint_t const*) p_desc)->bEndpointAddress; + + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } + + //------------- Data Interface -------------// + // - CDC-NCM data interface has 2 alternate settings + // - 0 : zero endpoints for inactive (default) + // - 1 : IN & OUT endpoints for transfer of NTBs + TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0); + + do { + tusb_desc_interface_t const *data_itf_desc = (tusb_desc_interface_t const*) p_desc; + TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0); + + drv_len += tu_desc_len(p_desc); + p_desc = tu_desc_next(p_desc); + } while ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len)); + + // Pair of endpoints + TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0); + + TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in)); + + drv_len += 2 * sizeof(tusb_desc_endpoint_t); + + return drv_len; +} + + + +static void ncm_report(void) +/** + * called on init + */ +{ +// printf("ncm_report - %d\n", ncm_interface.report_state); + uint8_t const rhport = 0; + if (ncm_interface.report_state == REPORT_SPEED) { + ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num; + usbd_edpt_xfer(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(rhport, ncm_interface.ep_notif, (uint8_t*) &ncm_notify_connected, sizeof(ncm_notify_connected)); + ncm_interface.report_state = REPORT_DONE; + ncm_interface.report_pending = true; + } +} + + + +TU_ATTR_WEAK void tud_network_link_state_cb(bool state) +/** + * called on init three times with 1/0/1 + * + * context: TinyUSB + */ +{ +// printf("tud_network_link_state_cb(%d) [%p]\n", state, xTaskGetCurrentTaskHandle()); +} + + + +// Handle class control request +// return false to stall control endpoint (e.g unsupported request) +bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) +/** + * Called on init of connection + * + * context: TinyUSB + */ +{ +// printf("netd_control_xfer_cb(%d, %d, %p) [%p]\n", rhport, stage, request, xTaskGetCurrentTaskHandle()); + + if (stage != CONTROL_STAGE_SETUP) + return true ; + + switch (request->bmRequestType_bit.type) { + case TUSB_REQ_TYPE_STANDARD: + switch (request->bRequest) { + case TUSB_REQ_GET_INTERFACE: { + uint8_t const req_itfnum = (uint8_t) request->wIndex; + TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum); + + tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1); + } + break; + + case TUSB_REQ_SET_INTERFACE: { + uint8_t const req_itfnum = (uint8_t) request->wIndex; + uint8_t const req_alt = (uint8_t) request->wValue; + + // Only valid for Data Interface with Alternate is either 0 or 1 + TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2); + + if (req_alt != ncm_interface.itf_data_alt) { + ncm_interface.itf_data_alt = req_alt; + + if (ncm_interface.itf_data_alt) { + if (!usbd_edpt_busy(rhport, ncm_interface.ep_out)) { + tud_network_recv_renew(); // prepare for incoming datagrams + } + if ( !ncm_interface.report_pending) { + ncm_report(); + } + } + + tud_network_link_state_cb(ncm_interface.itf_data_alt); + } + + tud_control_status(rhport, request); + } + break; + + // unsupported request + default: + return false ; + } + break; + + case TUSB_REQ_TYPE_CLASS: + TU_VERIFY(ncm_interface.itf_num == request->wIndex); + + //printf("netd_control_xfer_cb/TUSB_REQ_TYPE_CLASS: %d\n", request->bRequest); + + if (request->bRequest == NCM_GET_NTB_PARAMETERS) { + tud_control_xfer(rhport, request, (void*) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters)); + } + break; + + // unsupported request + default: + return false ; + } + + return true ; +} + + + +bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) +/** + * context: TinyUSB + */ +{ + (void) rhport; + (void) result; + + //printf("netd_xfer_cb(%d,%d,%d,%lu) [%p]\n", rhport, ep_addr, result, xferred_bytes, xTaskGetCurrentTaskHandle()); + + /* new datagram rcv_ntb */ + if (ep_addr == ncm_interface.ep_out) { + //printf(" EP_OUT %d %d %d %lu\n", rhport, ep_addr, result, xferred_bytes); + handle_incoming_datagram(xferred_bytes); + } + + /* data transmission finished */ + if (ep_addr == ncm_interface.ep_in) { + //printf(" EP_IN %d %d\n", ncm_interface.can_xmit, ncm_interface.itf_data_alt); + if (xferred_bytes != 0 && xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE == 0) + { + // TODO check when ZLP is really needed + do_in_xfer(NULL, 0); /* a ZLP is needed */ + } + else + { + /* we're finally finished */ + ncm_prepare_for_tx(); + } + } + + if (ep_addr == ncm_interface.ep_notif) { +// printf(" EP_NOTIF\n"); + ncm_interface.report_pending = false; + ncm_report(); + } + + return true ; +} // netd_xfer_cb + + + +bool tud_network_can_xmit(uint16_t size) +/** + * poll network driver for its ability to accept another packet to transmit + * + * context: TinyUSB + */ +{ + //printf("tud_network_can_xmit() %d [%p]\n", ncm_interface.can_xmit, xTaskGetCurrentTaskHandle()); + return ncm_interface.can_xmit; +} // tud_network_can_xmit + + + +void tud_network_xmit(void *ref, uint16_t arg) +/** + * context: TinyUSB. + */ +{ + transmit_ntb_t *ntb = &transmit_ntb; + uint16_t next_datagram_offset = sizeof(nth16_t) + sizeof(ndp16_t) + + ((1 + 1) * sizeof(ndp16_datagram_t)); + + //printf("tud_network_xmit(%p,%d) %d [%p]\n", ref, arg, ncm_interface.can_xmit, xTaskGetCurrentTaskHandle()); + + if ( !ncm_interface.can_xmit) { + return; + } + + uint16_t size = tud_network_xmit_cb(ntb->data + next_datagram_offset, ref, arg); + + ntb->ndp.datagram[0].wDatagramIndex = next_datagram_offset; + ntb->ndp.datagram[0].wDatagramLength = size; + + next_datagram_offset += size; + + // 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 = next_datagram_offset; + ntb->nth.wNdpIndex = sizeof(nth16_t); + + // Fill in NDP16 header and terminator + ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0; + ntb->ndp.wLength = sizeof(ndp16_t) + (2) * sizeof(ndp16_datagram_t); + ntb->ndp.wNextNdpIndex = 0; + ntb->ndp.datagram[1].wDatagramIndex = 0; + ntb->ndp.datagram[1].wDatagramLength = 0; + + // Kick off an endpoint transfer + do_in_xfer(ntb->data, next_datagram_offset); +} // tud_network_xmit + +#endif diff --git a/src/net/tinyusb/net_device.h b/src/net/tinyusb/net_device.h new file mode 100755 index 000000000..cf40651b4 --- /dev/null +++ b/src/net/tinyusb/net_device.h @@ -0,0 +1,102 @@ +/* + * The MIT License (MIT) + * + * 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. + */ + +#ifndef _TUSB_NET_DEVICE_H_ +#define _TUSB_NET_DEVICE_H_ + +#include +#include "class/cdc/cdc.h" + +#if CFG_TUD_ECM_RNDIS && CFG_TUD_NCM +#error "Cannot enable both ECM_RNDIS and NCM network drivers" +#endif + +/* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */ +#define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64) + +/* Maximum Transmission Unit (in bytes) of the network, including Ethernet header */ +#ifndef CFG_TUD_NET_MTU +#define CFG_TUD_NET_MTU 1514 +#endif + + +#ifdef __cplusplus + extern "C" { +#endif + +//--------------------------------------------------------------------+ +// Application API +//--------------------------------------------------------------------+ + +// indicate to network driver that client has finished with the packet provided to network_recv_cb() +void tud_network_recv_renew(void); + +// poll network driver for its ability to accept another packet to transmit +bool tud_network_can_xmit(uint16_t size); + +// if network_can_xmit() returns true, network_xmit() can be called once +void tud_network_xmit(void *ref, uint16_t arg); + +//--------------------------------------------------------------------+ +// Application Callbacks (WEAK is optional) +//--------------------------------------------------------------------+ + +// client must provide this: return false if the packet buffer was not accepted +bool tud_network_recv_cb(const uint8_t *src, uint16_t size); + +// client must provide this: copy from network stack packet pointer to dst +uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg); + +//------------- ECM/RNDIS -------------// + +// client must provide this: initialize any network state back to the beginning +void tud_network_init_cb(void); + +// client must provide this: 48-bit MAC address +// TODO removed later since it is not part of tinyusb stack +extern uint8_t tud_network_mac_address[6]; + +//------------- NCM -------------// + +// callback to client providing optional indication of internal state of network driver +void tud_network_link_state_cb(bool state); + +//--------------------------------------------------------------------+ +// INTERNAL USBD-CLASS DRIVER API +//--------------------------------------------------------------------+ +void netd_init (void); +void netd_reset (uint8_t rhport); +uint16_t netd_open (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); +bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request); +bool netd_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes); +void netd_report (uint8_t *buf, uint16_t len); + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_NET_DEVICE_H_ */ diff --git a/src/rtt_console.c b/src/rtt_console.c index 0e6dc3eb7..de77743cf 100755 --- a/src/rtt_console.c +++ b/src/rtt_console.c @@ -87,8 +87,6 @@ static EventGroupHandle_t events; #define RTT_CHANNEL_SYSVIEW 1 #define RTT_POLL_INT_MS 1 // faster polling static StreamBufferHandle_t stream_rtt_sysview_to_target; // small stream for host->probe->target sysview communication - static bool ok_sysview_from_target = false; - static bool ok_sysview_to_target = false; #else #define RTT_POLL_INT_MS RTT_CONSOLE_POLL_INT_MS #endif @@ -174,7 +172,7 @@ static bool rtt_check_channel_from_target(uint32_t rtt_cb, uint16_t channel, SEG ok = ok && (aUp->SizeOfBuffer > 0 && aUp->SizeOfBuffer < TARGET_RAM_END - TARGET_RAM_START); ok = ok && ((uint32_t)aUp->pBuffer >= TARGET_RAM_START && (uint32_t)aUp->pBuffer + aUp->SizeOfBuffer <= TARGET_RAM_END); if (ok) { - picoprobe_info("rtt_check_channel_from_target: %u %p %u %u %u\n", channel, aUp->pBuffer, aUp->SizeOfBuffer, aUp->RdOff, aUp->WrOff); + picoprobe_info("rtt_check_channel_from_target: %u %p %5u %5u %5u\n", channel, aUp->pBuffer, aUp->SizeOfBuffer, aUp->RdOff, aUp->WrOff); } return ok; } // rtt_check_channel_from_target @@ -193,7 +191,7 @@ static bool rtt_check_channel_to_target(uint32_t rtt_cb, uint16_t channel, SEGGE ok = ok && (aDown->SizeOfBuffer > 0 && aDown->SizeOfBuffer < TARGET_RAM_END - TARGET_RAM_START); ok = ok && ((uint32_t)aDown->pBuffer >= TARGET_RAM_START && (uint32_t)aDown->pBuffer + aDown->SizeOfBuffer <= TARGET_RAM_END); if (ok) { - picoprobe_info("rtt_check_channel_to_target: %u %p %u %u %u\n", channel, aDown->pBuffer, aDown->SizeOfBuffer, aDown->RdOff, aDown->WrOff); + picoprobe_info("rtt_check_channel_to_target : %u %p %5u %5u %5u\n", channel, aDown->pBuffer, aDown->SizeOfBuffer, aDown->RdOff, aDown->WrOff); } return ok; } // rtt_check_channel_to_target @@ -222,10 +220,6 @@ static unsigned rtt_get_write_space(SEGGER_RTT_BUFFER_DOWN *pRing) -#define USE_EXTRA_THREAD_FOR_FROM_TARGET - -#ifdef USE_EXTRA_THREAD_FOR_FROM_TARGET - static SEGGER_RTT_BUFFER_UP *ft_aUp; static uint16_t ft_channel; static uint32_t ft_rtt_cb; @@ -235,13 +229,19 @@ static bool ft_ok; static void rtt_from_target_thread(void *) /** + * Fetch RTT data from target. * Data transfer is CPU intensive, because SWD access is blocking the CPU. * So the idea is to put this task in an extra thread with affinity to the second core. * Core affinity is set in \a main.c */ { for (;;) { - xEventGroupWaitBits(events, EV_RTT_FROM_TARGET_STRT, pdTRUE, pdFALSE, portMAX_DELAY); + EventBits_t ev; + + ev = xEventGroupWaitBits(events, EV_RTT_FROM_TARGET_STRT, pdTRUE, pdFALSE, portMAX_DELAY); + if (ev == 0) { + continue; + } ft_ok = swd_read_word(ft_rtt_cb + offsetof(SEGGER_RTT_CB, aUp[ft_channel].WrOff), (uint32_t *)&(ft_aUp->WrOff)); @@ -270,17 +270,29 @@ static void rtt_from_target_thread(void *) } } // rtt_from_target_thread -#endif + + +static void rtt_from_target_reset(uint32_t rtt_cb, uint16_t channel, SEGGER_RTT_BUFFER_UP *aUp) +/** + * Reset an upstream buffer. + */ +{ +// printf("rtt_from_target_reset(%lx,%d,%p)\n", rtt_cb, channel, aUp); + + swd_read_word(rtt_cb + offsetof(SEGGER_RTT_CB, aUp[channel].WrOff), (uint32_t *)&(aUp->WrOff)); + aUp->RdOff = aUp->WrOff; + swd_write_word(rtt_cb + offsetof(SEGGER_RTT_CB, aUp[channel].RdOff), aUp->RdOff); +} // rtt_from_target_reset static bool rtt_from_target(uint32_t rtt_cb, uint16_t channel, SEGGER_RTT_BUFFER_UP *aUp, rtt_data_to_host data_to_host, bool *worked) { -#ifdef USE_EXTRA_THREAD_FOR_FROM_TARGET ft_cnt = data_to_host(NULL, 0); if (ft_cnt < sizeof(ft_buf) / 4) { //printf("no space in stream %d: %d\n", channel, ft_cnt); + *worked = true; } else { ft_aUp = aUp; @@ -291,7 +303,7 @@ static bool rtt_from_target(uint32_t rtt_cb, uint16_t channel, SEGGER_RTT_BUFFER xEventGroupWaitBits(events, EV_RTT_FROM_TARGET_END, pdTRUE, pdFALSE, portMAX_DELAY); if (ft_cnt != 0) { - // direct received data to host + // redirect received data to host data_to_host(ft_buf, ft_cnt); led_state(LS_RTT_RX_DATA); @@ -299,54 +311,6 @@ static bool rtt_from_target(uint32_t rtt_cb, uint16_t channel, SEGGER_RTT_BUFFER } } return ft_ok; -#else - bool ok = true; - uint8_t buf[256]; - uint32_t cnt; - - assert(data_to_host != NULL); - - cnt = data_to_host(NULL, 0); - if (cnt < sizeof(buf) / 4) { - //printf("no space in stream %d: %d\n", channel, cnt); - //*worked = true; - } - else { - ft_aUp = aUp; - ft_channel = channel; - ft_rtt_cb = rtt_cb; - - xEventGroupSetBits(events, EV_RTT_FROM_TARGET_STRT); - xEventGroupWaitBits(events, EV_RTT_FROM_TARGET_END, pdTRUE, pdFALSE, portMAX_DELAY); - - ok = ok && swd_read_word(rtt_cb + offsetof(SEGGER_RTT_CB, aUp[channel].WrOff), (uint32_t *)&(aUp->WrOff)); - - if (ok && aUp->WrOff != aUp->RdOff) { - // - // fetch data from target - // - if (aUp->WrOff > aUp->RdOff) { - cnt = MIN(cnt, aUp->WrOff - aUp->RdOff); - } - else { - cnt = MIN(cnt, aUp->SizeOfBuffer - aUp->RdOff); - } - cnt = MIN(cnt, sizeof(buf)); - - memset(buf, 0, sizeof(buf)); - ok = ok && swd_read_memory((uint32_t)aUp->pBuffer + aUp->RdOff, buf, cnt); - aUp->RdOff = (aUp->RdOff + cnt) % aUp->SizeOfBuffer; - ok = ok && swd_write_word(rtt_cb + offsetof(SEGGER_RTT_CB, aUp[channel].RdOff), aUp->RdOff); - - // direct received data to host - data_to_host(buf, cnt); - - led_state(LS_RTT_RX_DATA); - *worked = true; - } - } - return ok; -#endif } // rtt_from_target @@ -424,8 +388,9 @@ static void do_rtt_io(uint32_t rtt_cb) #if INCLUDE_SYSVIEW SEGGER_RTT_BUFFER_UP aUpSysView; // Up buffer, transferring information up from target via debug probe to host SEGGER_RTT_BUFFER_DOWN aDownSysView; // Down buffer, transferring information from host via debug probe to target - ok_sysview_from_target = false; - ok_sysview_to_target = false; + bool ok_sysview_from_target = false; + bool ok_sysview_to_target = false; + bool net_sysview_was_connected = false; #endif bool ok = true; @@ -444,24 +409,24 @@ static void do_rtt_io(uint32_t rtt_cb) #if OPT_TARGET_UART { - static bool worked_uart = false; + static bool working_uart = false; static TickType_t lastTimeWorked; - if ( !worked_uart && xTaskGetTickCount() - lastTimeWorked < pdMS_TO_TICKS(RTT_CONSOLE_POLL_INT_MS)) { + if ( !working_uart && xTaskGetTickCount() - lastTimeWorked < pdMS_TO_TICKS(RTT_CONSOLE_POLL_INT_MS)) { // // pause console IO for a longer time to let SysView the interface // } else { - worked_uart = false; + working_uart = false; if (ok_console_from_target) - ok = ok && rtt_from_target(rtt_cb, RTT_CHANNEL_CONSOLE, &aUpConsole, cdc_uart_write, &worked_uart); + ok = ok && rtt_from_target(rtt_cb, RTT_CHANNEL_CONSOLE, &aUpConsole, cdc_uart_write, &working_uart); if (ok_console_to_target) - ok = ok && rtt_to_target(rtt_cb, stream_rtt_console_to_target, RTT_CHANNEL_CONSOLE, &aDownConsole, &worked_uart); + ok = ok && rtt_to_target(rtt_cb, stream_rtt_console_to_target, RTT_CHANNEL_CONSOLE, &aDownConsole, &working_uart); - probe_rtt_cb = probe_rtt_cb && !worked_uart; + probe_rtt_cb = probe_rtt_cb && !working_uart; lastTimeWorked = xTaskGetTickCount(); } @@ -469,19 +434,27 @@ static void do_rtt_io(uint32_t rtt_cb) #endif #if INCLUDE_SYSVIEW - { - bool worked_sysview = false; + if (net_sysview_is_connected()) { + bool working_sysview = false; + if ( !net_sysview_was_connected) { + net_sysview_was_connected = true; + rtt_from_target_reset(rtt_cb, RTT_CHANNEL_SYSVIEW, &aUpSysView); + } if (ok_sysview_from_target) - ok = ok && rtt_from_target(rtt_cb, RTT_CHANNEL_SYSVIEW, &aUpSysView, net_sysview_send, &worked_sysview); + ok = ok && rtt_from_target(rtt_cb, RTT_CHANNEL_SYSVIEW, &aUpSysView, net_sysview_send, &working_sysview); if (ok_sysview_to_target) - ok = ok && rtt_to_target(rtt_cb, stream_rtt_sysview_to_target, RTT_CHANNEL_SYSVIEW, &aDownSysView, &worked_sysview); + ok = ok && rtt_to_target(rtt_cb, stream_rtt_sysview_to_target, RTT_CHANNEL_SYSVIEW, &aDownSysView, &working_sysview); - probe_rtt_cb = probe_rtt_cb && !worked_sysview; + probe_rtt_cb = probe_rtt_cb && !working_sysview; + } + else { + net_sysview_was_connected = false; } #endif + //printf("%d %d\n", ok, probe_rtt_cb); if (ok && probe_rtt_cb) { // did nothing -> check if RTT channels appeared #if OPT_TARGET_UART @@ -538,7 +511,7 @@ void rtt_io_thread(void *ptr) bool target_online = false; for (;;) { - sw_lock("RTT", false); + sw_lock("RTT-IO", false); // post: we have the interface if ( !target_online) { @@ -585,7 +558,7 @@ void rtt_io_thread(void *ptr) target_disconnect(); vTaskDelay(pdMS_TO_TICKS(200)); // some guard time after disconnect } - sw_unlock("RTT"); + sw_unlock("RTT-IO"); vTaskDelay(pdMS_TO_TICKS(300)); // give the other task the opportunity to catch sw_lock(); } } // rtt_io_thread @@ -661,13 +634,13 @@ void rtt_console_init(uint32_t task_prio) } #endif - xTaskCreate(rtt_io_thread, "RTT", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_rtt_console); + xTaskCreate(rtt_io_thread, "RTT-IO", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_rtt_console); if (task_rtt_console == NULL) { picoprobe_error("rtt_console_init: cannot create task_rtt_console\n"); } - xTaskCreate(rtt_from_target_thread, "RTT_FROM", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_rtt_from_target_thread); + xTaskCreate(rtt_from_target_thread, "RTT-From", configMINIMAL_STACK_SIZE, NULL, task_prio, &task_rtt_from_target_thread); if (task_rtt_from_target_thread == NULL) { picoprobe_error("rtt_console_init: cannot create task_rtt_from_target_thread\n");