From ee8c6e9bb6eb784de5c7670efd94cf1e32265f58 Mon Sep 17 00:00:00 2001 From: robert Date: Wed, 20 May 2026 10:47:08 -0400 Subject: [PATCH] Add RP examples --- tutorials/pico-sdk/pico-rmii/Makefile | 37 ++- tutorials/pico-sdk/pico-rmii/main.c | 58 ++-- tutorials/pico-sdk/pico-rmii/mongoose.c | 1 - tutorials/pico-sdk/pico-rmii/mongoose.h | 1 - tutorials/pico-sdk/pico-rndis/CMakeLists.txt | 33 +++ tutorials/pico-sdk/pico-rndis/Makefile | 41 +++ tutorials/pico-sdk/pico-rndis/README.md | 7 + tutorials/pico-sdk/pico-rndis/main.c | 92 ++++++ .../pico-sdk/pico-rndis/mongoose_config.h | 4 + tutorials/pico-sdk/pico-rndis/msc_disk.c | 252 ++++++++++++++++ .../pico-sdk/pico-rndis/netif/ethernet.h | 2 + tutorials/pico-sdk/pico-rndis/tusb_config.h | 12 + .../pico-sdk/pico-rndis/usb_descriptors.c | 271 ++++++++++++++++++ tutorials/pico-sdk/rm2-pico/CMakeLists.txt | 24 ++ tutorials/pico-sdk/rm2-pico/Makefile | 40 +++ tutorials/pico-sdk/rm2-pico/hal.h | 155 ++++++++++ tutorials/pico-sdk/rm2-pico/main.c | 153 ++++++++++ tutorials/pico-sdk/rm2-pico/mongoose_config.h | 5 + 18 files changed, 1154 insertions(+), 34 deletions(-) delete mode 120000 tutorials/pico-sdk/pico-rmii/mongoose.c delete mode 120000 tutorials/pico-sdk/pico-rmii/mongoose.h create mode 100644 tutorials/pico-sdk/pico-rndis/CMakeLists.txt create mode 100644 tutorials/pico-sdk/pico-rndis/Makefile create mode 100644 tutorials/pico-sdk/pico-rndis/README.md create mode 100644 tutorials/pico-sdk/pico-rndis/main.c create mode 100644 tutorials/pico-sdk/pico-rndis/mongoose_config.h create mode 100644 tutorials/pico-sdk/pico-rndis/msc_disk.c create mode 100644 tutorials/pico-sdk/pico-rndis/netif/ethernet.h create mode 100644 tutorials/pico-sdk/pico-rndis/tusb_config.h create mode 100644 tutorials/pico-sdk/pico-rndis/usb_descriptors.c create mode 100644 tutorials/pico-sdk/rm2-pico/CMakeLists.txt create mode 100644 tutorials/pico-sdk/rm2-pico/Makefile create mode 100644 tutorials/pico-sdk/rm2-pico/hal.h create mode 100644 tutorials/pico-sdk/rm2-pico/main.c create mode 100644 tutorials/pico-sdk/rm2-pico/mongoose_config.h diff --git a/tutorials/pico-sdk/pico-rmii/Makefile b/tutorials/pico-sdk/pico-rmii/Makefile index 8ed60d61..2f3998ab 100644 --- a/tutorials/pico-sdk/pico-rmii/Makefile +++ b/tutorials/pico-sdk/pico-rmii/Makefile @@ -1,20 +1,41 @@ +# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/ + +CWD ?= $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +SDK_PATH ?= $(CWD)/pico-sdk RM = rm -rf MKBUILD = test -d build || mkdir build + ifeq ($(OS),Windows_NT) RM = cmd /C del /Q /F /S MKBUILD = if not exist build mkdir build endif -all example: - true +.PHONY: build -build: pico-sdk +all build: build/firmware.uf2 + +build/firmware.uf2: pico_sdk_import.cmake mongoose.c mongoose.h + +pico_sdk_import.cmake: $(SDK_PATH) + cp $(SDK_PATH)/external/$@ $@ + +mongoose.c mongoose.h: + cp ../../../$@ . + +build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile $(MKBUILD) - cd build && cmake -G "Unix Makefiles" .. && make + cd build && PICO_SDK_PATH=$(SDK_PATH) cmake -G "Unix Makefiles" .. && make -pico-sdk: - git clone --depth 1 -b 2.1.0 https://github.com/raspberrypi/pico-sdk $@ - cd $@ && git submodule update --init +flash: build/firmware.uf2 + picotool load $< -f clean: - $(RM) pico-sdk build + $(RM) build pico_sdk_import.cmake mongoose.c mongoose.h + +fullclean: clean + $(RM) $(SDK_PATH) + +$(SDK_PATH): + git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@ + cd $@ && git submodule update --quiet --init + diff --git a/tutorials/pico-sdk/pico-rmii/main.c b/tutorials/pico-sdk/pico-rmii/main.c index 9b9008f3..aa8a86d8 100644 --- a/tutorials/pico-sdk/pico-rmii/main.c +++ b/tutorials/pico-sdk/pico-rmii/main.c @@ -3,30 +3,12 @@ #include "pico/stdlib.h" #include "hardware/clocks.h" - -#include "mongoose.h" - #include "driver_rp2040_rmii.h" +#include "mongoose.h" #define CLKREFPIN 20 // either 20 or 22 -static void fn(struct mg_connection *c, int ev, void *ev_data) { - if (ev == MG_EV_HTTP_MSG) { - mg_http_reply(c, 200, "", "ok\n"); - } -} - - -int main(void) { - // use the RMII reference clock (50MHz) as our system clock - clock_configure_gpin(clk_sys, CLKREFPIN, 50 * MHZ, 50 * MHZ); - sleep_ms(100); - stdio_init_all(); - - struct mg_mgr mgr; // Initialise Mongoose event manager - mg_mgr_init(&mgr); // and attach it to the interface - mg_log_set(MG_LL_DEBUG); // Set log level - +static void driver_init(struct mg_mgr *mgr) { MG_INFO(("Init TCP/IP")); // Initialise Mongoose network stack and specific driver // Set consecutive GPIOs for RMII (tx and rx) and SMI function groups @@ -47,11 +29,39 @@ int main(void) { .driver = &mg_tcpip_driver_rp2040_rmii, .driver_data = &driver_data, }; - mg_tcpip_init(&mgr, &mif); - mg_http_listen(&mgr, "http://0.0.0.0", fn, NULL); // HTTP listener - MG_INFO(("Init done, starting main loop")); + mg_tcpip_init(mgr, &mif); +} - for (;;) mg_mgr_poll(&mgr, 0); // Infinite event loop +static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + uint64_t tick = to_ms_since_boot(get_absolute_time()); + if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { + mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), tick); + } else { + mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n", tick); + } + } +} +// In an RTOS environment, give this task 8 KB of stack space. +static void run_mongoose(void) { + struct mg_mgr mgr; + mg_mgr_init(&mgr); + driver_init(&mgr); + mg_log_set(MG_LL_DEBUG); // Set log level + MG_INFO(("Hardware initialised, starting firmware...")); + mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + for (;;) { + mg_mgr_poll(&mgr, 1); + } +} + +int main(void) { + // use the RMII reference clock (50MHz) as our system clock + clock_configure_gpin(clk_sys, CLKREFPIN, 50 * MHZ, 50 * MHZ); + sleep_ms(100); + stdio_init_all(); + run_mongoose(); return 0; } diff --git a/tutorials/pico-sdk/pico-rmii/mongoose.c b/tutorials/pico-sdk/pico-rmii/mongoose.c deleted file mode 120000 index 5e522bbc..00000000 --- a/tutorials/pico-sdk/pico-rmii/mongoose.c +++ /dev/null @@ -1 +0,0 @@ -../../../mongoose.c \ No newline at end of file diff --git a/tutorials/pico-sdk/pico-rmii/mongoose.h b/tutorials/pico-sdk/pico-rmii/mongoose.h deleted file mode 120000 index ee4ac823..00000000 --- a/tutorials/pico-sdk/pico-rmii/mongoose.h +++ /dev/null @@ -1 +0,0 @@ -../../../mongoose.h \ No newline at end of file diff --git a/tutorials/pico-sdk/pico-rndis/CMakeLists.txt b/tutorials/pico-sdk/pico-rndis/CMakeLists.txt new file mode 100644 index 00000000..379ba90d --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.13) +include(pico-sdk/pico_sdk_init.cmake) + +project(firmware) +pico_sdk_init() + +add_executable(firmware + main.c + msc_disk.c + usb_descriptors.c + mongoose.c + pico-sdk/lib/tinyusb/lib/networking/rndis_reports.c) + +target_include_directories(firmware PUBLIC + . + pico-sdk/lib/tinyusb/lib/networking) + +target_link_libraries(firmware pico_stdlib pico_rand hardware_spi tinyusb_device) +pico_add_extra_outputs(firmware) # create map/bin/hex file etc. + +pico_enable_stdio_usb(firmware 0) # Route stdio +pico_enable_stdio_uart(firmware 1) # to the UART + +# Mongoose build flags in mongoose_config.h + +# Example build options +add_definitions(-DHTTP_URL="http://0.0.0.0/") + +# Extra build flags (enable if needed) +add_definitions(-DDUAL_CONFIG=0) + +# Prevent extreme optimization from breaking OTA +target_compile_options(firmware PRIVATE -fno-tree-loop-distribute-patterns) diff --git a/tutorials/pico-sdk/pico-rndis/Makefile b/tutorials/pico-sdk/pico-rndis/Makefile new file mode 100644 index 00000000..2f3998ab --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/Makefile @@ -0,0 +1,41 @@ +# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/ + +CWD ?= $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +SDK_PATH ?= $(CWD)/pico-sdk +RM = rm -rf +MKBUILD = test -d build || mkdir build + +ifeq ($(OS),Windows_NT) + RM = cmd /C del /Q /F /S + MKBUILD = if not exist build mkdir build +endif + +.PHONY: build + +all build: build/firmware.uf2 + +build/firmware.uf2: pico_sdk_import.cmake mongoose.c mongoose.h + +pico_sdk_import.cmake: $(SDK_PATH) + cp $(SDK_PATH)/external/$@ $@ + +mongoose.c mongoose.h: + cp ../../../$@ . + +build/firmware.uf2: $(SDK_PATH) $(wildcard *.[ch]) CMakeLists.txt Makefile + $(MKBUILD) + cd build && PICO_SDK_PATH=$(SDK_PATH) cmake -G "Unix Makefiles" .. && make + +flash: build/firmware.uf2 + picotool load $< -f + +clean: + $(RM) build pico_sdk_import.cmake mongoose.c mongoose.h + +fullclean: clean + $(RM) $(SDK_PATH) + +$(SDK_PATH): + git clone -c advice.detachedHead=false --quiet --depth 1 -b 2.1.1 https://github.com/raspberrypi/pico-sdk $@ + cd $@ && git submodule update --quiet --init + diff --git a/tutorials/pico-sdk/pico-rndis/README.md b/tutorials/pico-sdk/pico-rndis/README.md new file mode 100644 index 00000000..e3c8e8a1 --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/README.md @@ -0,0 +1,7 @@ +# Device Dashboard via RNDIS on an RP2040 + +Your headless Raspberry Pi Pico-based hardware can also have a device dashboard on a web browser when you connect it to your computer via USB + +- See detailed tutorial at https://mongoose.ws/tutorials/rp2040/pico-rndis-dashboard/ + +Note for Mac users: For this example to run on Mac, please set `DUAL_CONFIG=1` in `CMakeLists.txt` before building; this enables CDC-ECM. Please notice that Windows may not recognize the mass-storage device in that case. diff --git a/tutorials/pico-sdk/pico-rndis/main.c b/tutorials/pico-sdk/pico-rndis/main.c new file mode 100644 index 00000000..8ed79a04 --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/main.c @@ -0,0 +1,92 @@ +// Copyright (c) 2022 Cesanta Software Limited +// All rights reserved + +#include "mongoose.h" +#include "pico/stdlib.h" +#include "tusb.h" + +static struct mg_tcpip_if *s_ifp; + +uint8_t tud_network_mac_address[6] = {2, 2, 0x84, 0x6A, 0x96, 0}; + +static void blink_task(void *arg) { // Blink periodically + gpio_put(PICO_DEFAULT_LED_PIN, !gpio_get_out_level(PICO_DEFAULT_LED_PIN)); + (void) arg; +} + +bool tud_network_recv_cb(const uint8_t *buf, uint16_t len) { + mg_tcpip_qwrite((void *) buf, len, s_ifp); + // MG_INFO(("RECV %hu", len)); + // mg_hexdump(buf, len); + tud_network_recv_renew(); + return true; +} + +void tud_network_init_cb(void) {} + +uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) { + // MG_INFO(("SEND %hu", arg)); + memcpy(dst, ref, arg); + return arg; +} + +static size_t usb_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) { + if (!tud_ready()) return 0; + while (!tud_network_can_xmit(len)) tud_task(); + tud_network_xmit((void *) buf, len); + (void) ifp; + return len; +} + +static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) { + (void) ifp; + tud_task(); + return s1 ? tud_inited() && tud_ready() && tud_connected() : false; +} + +static void driver_init(struct mg_mgr *mgr) { + struct mg_tcpip_driver driver = {.tx = usb_tx, .poll = usb_poll}; + struct mg_tcpip_if mif = {.mac = {2, 0, 1, 2, 3, 0x77}, + .ip = mg_htonl(MG_U32(192, 168, 3, 1)), + .mask = mg_htonl(MG_U32(255, 255, 255, 0)), + .enable_dhcp_server = true, + .driver = &driver, + .recv_queue.size = 4096}; + s_ifp = &mif; + mg_tcpip_init(mgr, &mif); + tusb_init(); +} + +static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + uint64_t tick = to_ms_since_boot(get_absolute_time()); + if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { + mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), tick); + } else { + mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n", tick); + } + } +} + +// In an RTOS environment, give this task 8 KB of stack space. +static void run_mongoose(void) { + struct mg_mgr mgr; + mg_mgr_init(&mgr); + driver_init(&mgr); + mg_log_set(MG_LL_DEBUG); // Set log level + MG_INFO(("Hardware initialised, starting firmware...")); + mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_task, &mgr); + mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + for (;;) { + mg_mgr_poll(&mgr, 1); + } +} + +int main(void) { + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + stdio_init_all(); + run_mongoose(); + return 0; +} diff --git a/tutorials/pico-sdk/pico-rndis/mongoose_config.h b/tutorials/pico-sdk/pico-rndis/mongoose_config.h new file mode 100644 index 00000000..29d510b4 --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/mongoose_config.h @@ -0,0 +1,4 @@ +#define MG_ARCH MG_ARCH_PICOSDK + +#define MG_ENABLE_TCPIP 1 +#define MG_ENABLE_TCPIP_DRIVER_INIT 0 diff --git a/tutorials/pico-sdk/pico-rndis/msc_disk.c b/tutorials/pico-sdk/pico-rndis/msc_disk.c new file mode 100644 index 00000000..b9b4db83 --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/msc_disk.c @@ -0,0 +1,252 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +// Modified by Cesanta Software + +#include "tusb.h" + +// Some MCU doesn't have enough 8KB SRAM to store the whole disk + +enum +{ + DISK_BLOCK_NUM = 16, // 8KB is the smallest size that Windows allows to mount + DISK_BLOCK_SIZE = 512 +}; + + +#define INDEX_CONTENTS \ +"" + +// readonly +const uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = +{ + //------------- Block0: Boot Sector -------------// + // byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM; + // sector_per_cluster = 1; reserved_sectors = 1; + // fat_num = 1; fat12_root_entry_num = 16; + // sector_per_fat = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0; + // drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29; + // filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB 0 "; + // FAT magic code at offset 510-511 + { + 0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00, + 0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T' , 'i' , 'n' , 'y' , 'U' , + 'S' , 'B' , ' ' , 'C' , ' ' , ' ' , 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00, + + // Zero up to 2 last bytes of FAT magic code + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA + }, + + //------------- Block1: FAT12 Table -------------// + { + 0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file + }, + + //------------- Block2: Root Directory -------------// + { + // first entry is volume label + 'M' , 'o' , 'n' , 'g' , 'o' , 'o' , 's' , 'e' , ' ' , ' ' , ' ' , 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // second entry is our file + 'I' , 'N' , 'D' , 'E' , 'X' , ' ' , ' ' , ' ' , 'H' , 'T' , 'M' , 0x20, 0x00, 0xC6, 0x52, 0x6D, + 0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00, + sizeof(INDEX_CONTENTS)-1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes) + }, + + //------------- Block3: file Content -------------// + INDEX_CONTENTS +}; + + +// Invoked to determine max LUN +uint8_t tud_msc_get_maxlun_cb(void) +{ + return 1; // single LUN +} + +// Invoked when received SCSI_CMD_INQUIRY +// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) +{ + (void) lun; // use same ID for both LUNs + + const char vid[] = "TinyUSB"; + const char pid[] = "Mass Storage"; + const char rev[] = "1.0"; + + memcpy(vendor_id , vid, strlen(vid)); + memcpy(product_id , pid, strlen(pid)); + memcpy(product_rev, rev, strlen(rev)); +} + +// Invoked when received Test Unit Ready command. +// return true allowing host to read/write this LUN e.g SD card inserted +bool tud_msc_test_unit_ready_cb(uint8_t lun) +{ + (void) lun; + return true; // Flash/RAM disks are always ready +} + +// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size +// Application update block count and block size +void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) +{ + (void) lun; + + *block_count = DISK_BLOCK_NUM; + *block_size = DISK_BLOCK_SIZE; +} + +// Invoked when received Start Stop Unit command +// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage +// - Start = 1 : active mode, if load_eject = 1 : load disk storage +bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) +{ + (void) lun; + (void) power_condition; + + if ( load_eject ) + { + if (start) + { + // load disk storage + }else + { + // unload disk storage + } + } + + return true; +} + +// Callback invoked when received READ10 command. +// Copy disk's data to buffer (up to bufsize) and return number of copied bytes. +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) +{ + // out of ramdisk + if ( lba >= DISK_BLOCK_NUM ) return -1; + + (void) lun; + uint8_t const* addr = msc_disk0[lba] + offset; + memcpy(buffer, addr, bufsize); + + return (int32_t) bufsize; +} + +bool tud_msc_is_writable_cb (uint8_t lun) +{ + (void) lun; + +// readonly + return false; +} + +// Callback invoked when received WRITE10 command. +// Process data in buffer to disk's storage and return number of written bytes +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) +{ + // out of ramdisk + if ( lba >= DISK_BLOCK_NUM ) return -1; + +#if 0 + uint8_t* addr = (lun ? msc_disk1[lba] : msc_disk0[lba]) + offset; + memcpy(addr, buffer, bufsize); +#else // readonly + (void) lun; (void) lba; (void) offset; (void) buffer; +#endif + + return (int32_t) bufsize; +} + +// Callback invoked when received an SCSI command not in built-in list below +// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE +// - READ10 and WRITE10 has their own callbacks +int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) +{ + // read10 & write10 has their own callback and MUST not be handled here + + void const* response = NULL; + int32_t resplen = 0; + + // most scsi handled is input + bool in_xfer = true; + + switch (scsi_cmd[0]) + { + default: + // Set Sense = Invalid Command Operation + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + + // negative means error -> tinyusb could stall and/or response with failed status + resplen = -1; + break; + } + + // return resplen must not larger than bufsize + if ( resplen > bufsize ) resplen = bufsize; + + if ( response && (resplen > 0) ) + { + if(in_xfer) + { + memcpy(buffer, response, (size_t) resplen); + }else + { + // SCSI output + } + } + + return resplen; +} diff --git a/tutorials/pico-sdk/pico-rndis/netif/ethernet.h b/tutorials/pico-sdk/pico-rndis/netif/ethernet.h new file mode 100644 index 00000000..d48aeb14 --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/netif/ethernet.h @@ -0,0 +1,2 @@ +#pragma once +#define SIZEOF_ETH_HDR 14 diff --git a/tutorials/pico-sdk/pico-rndis/tusb_config.h b/tutorials/pico-sdk/pico-rndis/tusb_config.h new file mode 100644 index 00000000..8393b2e8 --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/tusb_config.h @@ -0,0 +1,12 @@ +#pragma once + +// Windows only supports RNDIS, and apparently doesn't like composite devices with two configurations. +// Mac only supports CDC-ECM, which we present as the second config in a dual configuration device +// Linux supports either RNDIS, CDC-ECM or CDC-NCM +#define CFG_TUD_ECM_RNDIS 1 +#define CFG_TUD_MSC 1 // This enables mass storage +#define CFG_TUD_MSC_EP_BUFSIZE 512 // Also Add msc_disk.c and descritors +#define CFG_TUD_NCM 0 +#define BOARD_DEVICE_RHPORT_NUM 0 +#define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) diff --git a/tutorials/pico-sdk/pico-rndis/usb_descriptors.c b/tutorials/pico-sdk/pico-rndis/usb_descriptors.c new file mode 100644 index 00000000..87176f88 --- /dev/null +++ b/tutorials/pico-sdk/pico-rndis/usb_descriptors.c @@ -0,0 +1,271 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +// Modified by Cesanta Software + +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(ECM_RNDIS, 5) | _PID_MAP(NCM, 5) ) + +// String Descriptor Index +enum +{ + STRID_LANGID = 0, + STRID_MANUFACTURER, + STRID_PRODUCT, + STRID_SERIAL, + STRID_INTERFACE, + STRID_MAC +}; + +enum +{ + ITF_NUM_CDC = 0, + ITF_NUM_CDC_DATA, + ITF_NUM_MSC, + ITF_NUM_TOTAL +}; + +enum +{ +#if CFG_TUD_ECM_RNDIS + CONFIG_ID_RNDIS = 0, +#if DUAL_CONFIG + CONFIG_ID_ECM = 1, +#endif +#else + CONFIG_ID_NCM = 0, +#endif + CONFIG_ID_COUNT +}; + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + + // Use Interface Association Descriptor (IAD) device class + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = USB_PID, + .bcdDevice = 0x0101, + + .iManufacturer = STRID_MANUFACTURER, + .iProduct = STRID_PRODUCT, + .iSerialNumber = STRID_SERIAL, + + .bNumConfigurations = CONFIG_ID_COUNT // multiple configurations +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ +#define MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN + TUD_RNDIS_DESC_LEN) +#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN + TUD_CDC_ECM_DESC_LEN) +#define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN + TUD_CDC_NCM_DESC_LEN) + +#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX + // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number + // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ... + #define EPNUM_NET_NOTIF 0x81 + #define EPNUM_NET_OUT 0x02 + #define EPNUM_NET_IN 0x82 + #define EPNUM_MSC_OUT 0x05 + #define EPNUM_MSC_IN 0x85 + +#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X + // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT + // e.g EP1 OUT & EP1 IN cannot exist together + #define EPNUM_NET_NOTIF 0x81 + #define EPNUM_NET_OUT 0x02 + #define EPNUM_NET_IN 0x83 + #define EPNUM_MSC_OUT 0x04 + #define EPNUM_MSC_IN 0x85 + +#else + #define EPNUM_NET_NOTIF 0x81 + #define EPNUM_NET_OUT 0x02 + #define EPNUM_NET_IN 0x82 + #define EPNUM_MSC_OUT 0x03 + #define EPNUM_MSC_IN 0x83 +#endif + + // Interface number, string index, EP Out & EP In address, EP size +# define MSCDESC TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64) + +#if CFG_TUD_ECM_RNDIS + +static uint8_t const rndis_configuration[] = +{ + // Config number (index+1), interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS+1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100), + + // Interface number, string index, EP notification address and size, EP data address (out, in) and size. + TUD_RNDIS_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE), + + MSCDESC +}; + +#if DUAL_CONFIG +static uint8_t const ecm_configuration[] = +{ + // Config number (index+1), interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM+1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100), + + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU), + + MSCDESC +}; +#endif + +#else + +static uint8_t const ncm_configuration[] = +{ + // Config number (index+1), interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM+1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100), + + // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. + TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU), + + MSCDESC +}; + +#endif + +// Configuration array: RNDIS and CDC-ECM +// - Windows only works with RNDIS +// - MacOS only works with CDC-ECM +// - Linux will work on both +static uint8_t const * const configuration_arr[2] = +{ +#if CFG_TUD_ECM_RNDIS + [CONFIG_ID_RNDIS] = rndis_configuration +#if DUAL_CONFIG + , [CONFIG_ID_ECM ] = ecm_configuration +#endif +#else + [CONFIG_ID_NCM ] = ncm_configuration +#endif +}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +static char const* string_desc_arr [] = +{ + [STRID_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409) + [STRID_MANUFACTURER] = "TinyUSB", // Manufacturer + [STRID_PRODUCT] = "TinyUSB Device", // Product + [STRID_SERIAL] = "123456", // Serial + [STRID_INTERFACE] = "TinyUSB Network Interface" // Interface Description + + // STRID_MAC index is handled separately +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + unsigned int chr_count = 0; + + if (STRID_LANGID == index) + { + memcpy(&_desc_str[1], string_desc_arr[STRID_LANGID], 2); + chr_count = 1; + } + else if (STRID_MAC == index) + { + // Convert MAC address into UTF-16 + + for (unsigned i=0; i> 4) & 0xf]; + _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf]; + } + } + else + { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t) strlen(str); + if ( chr_count > (TU_ARRAY_SIZE(_desc_str) - 1)) chr_count = TU_ARRAY_SIZE(_desc_str) - 1; + + // Convert ASCII string into UTF-16 + for (unsigned int i=0; i 0 + .used_gpio_ranges = 0x0 +#endif +}; + +static inline pio_sm_config piospi_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + piospi_wrap_target, offset + piospi_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} + +static inline pio_sm_config piospi_init(PIO pio, uint sm, uint addr, uint data, uint clk) { + pio_gpio_init(pio, data); + pio_gpio_init(pio, clk); + pio_sm_set_pins_with_mask(pio, sm, 0, (1 << data) | (1 << clk)); + pio_sm_config c = piospi_program_get_default_config(addr); + sm_config_set_out_pins(&c, data, 1); + sm_config_set_in_pins(&c, data); + sm_config_set_set_pins(&c, data, 1); + sm_config_set_sideset_pins(&c, clk); + pio_sm_set_consecutive_pindirs(pio, sm, data, 1, true); + pio_sm_set_consecutive_pindirs(pio, sm, clk, 1, true); + pio_sm_set_pindirs_with_mask(pio, sm, (1 << data) | (1 << clk), (1 << data) | (1 << clk)); + sm_config_set_in_shift(&c, false, true, 8); // push bytes, MSB first, auto +#if PICO_RP2040 + sm_config_set_clkdiv(&c, 18); // Run at 133/1 = <50MHz (2x data rate) +#else + sm_config_set_clkdiv(&c, 20); // Run at 150/1 = <50MHz (2x data rate) +#endif + return c; +} +static inline void piospi_tx(PIO pio, uint sm, pio_sm_config *c, uint addr, uint data) { + sm_config_set_fifo_join(c, PIO_FIFO_JOIN_TX); + sm_config_set_out_shift(c, false, true, 8); // pull bytes, MSB first, auto + pio_sm_init(pio, sm, addr, c); +} +static inline void piospi_rx(PIO pio, uint sm, pio_sm_config *c, uint addr, uint data) { + sm_config_set_fifo_join(c, PIO_FIFO_JOIN_NONE); + sm_config_set_out_shift(c, false, true, 32); // pull words, MSB first, auto + pio_sm_init(pio, sm, addr + piospi_offset_rx_, c); +} +static inline void piospi_done(PIO pio, uint sm) { + while(!pio_interrupt_get(pio, 0)); + pio_sm_set_enabled(pio, sm, false); + pio_interrupt_clear(pio, 0); +} + +static pio_sm_config s_piospi_sm_config; +static uint s_piospi_sm_addr; + +void piospi_write(uint8_t *data, size_t len) { + size_t initial_len = len; + piospi_tx(SPIPIO, SPISM, &s_piospi_sm_config, s_piospi_sm_addr, DATAPIN); + __compiler_memory_barrier(); + pio_sm_put(SPIPIO, SPISM, (len * 8) - 1); // bits to transmit + pio_sm_set_enabled(SPIPIO, SPISM, true); + while (len--) pio_sm_put_blocking(SPIPIO, SPISM, (*data++) << 24); + piospi_done(SPIPIO, SPISM); +} + +// Read data block from SPI interface +void piospi_read(uint8_t *data, size_t len) { + piospi_rx(SPIPIO, SPISM, &s_piospi_sm_config, s_piospi_sm_addr, DATAPIN); + __compiler_memory_barrier(); + pio_sm_put(SPIPIO, SPISM, (len * 8) - 1); // bits to receive + pio_sm_set_enabled(SPIPIO, SPISM, true); + while (len--) *data++ = pio_sm_get_blocking(SPIPIO, SPISM); + piospi_done(SPIPIO, SPISM); +} + +void hwspecific_spi_init(void) { + gpio_init(PWRPIN); + gpio_set_dir(PWRPIN, GPIO_OUT); + gpio_put(PWRPIN, 0); + gpio_init(CSPIN); + gpio_set_dir(CSPIN, GPIO_OUT); + gpio_put(CSPIN, 0); + // init SPI pins so they are idle and stable during power up + gpio_init(DATAPIN); + gpio_set_dir(DATAPIN, GPIO_OUT); + gpio_put(DATAPIN, 0); + gpio_init(CLKPIN); + gpio_set_dir(CLKPIN, GPIO_OUT); + gpio_put(CLKPIN, 0); + mg_delayms(100); + gpio_put(CSPIN, 1); + gpio_put(PWRPIN, 1); + s_piospi_sm_addr = pio_add_program(SPIPIO, &piospi_program); + s_piospi_sm_config = piospi_init(SPIPIO, SPISM, s_piospi_sm_addr, DATAPIN, CLKPIN); + mg_delayms(50); +} + +void hwspecific_spi_begin(void *arg) { + gpio_put(CSPIN, 0); + (void) arg; +} + +// either write or read, not both +void hwspecific_spi_txn(void *arg, uint8_t *txdata, uint8_t *rxdata, size_t len) { + if (txdata != NULL) piospi_write(txdata, len); + if (rxdata != NULL) piospi_read(rxdata, len); + (void) arg; +} + +void hwspecific_spi_end(void *arg) { + gpio_put(CSPIN, 1); + (void) arg; +} diff --git a/tutorials/pico-sdk/rm2-pico/main.c b/tutorials/pico-sdk/rm2-pico/main.c new file mode 100644 index 00000000..13e3808c --- /dev/null +++ b/tutorials/pico-sdk/rm2-pico/main.c @@ -0,0 +1,153 @@ +// Copyright (c) 2025 Cesanta Software Limited +// All rights reserved + +#include "mongoose.h" +#include "hal.h" + + +#define WIFI_SSID "YOUR_WIFI_NETWORK_NAME" // SET THIS! +#define WIFI_PASS "YOUR_WIFI_PASSWORD" // SET THIS! + + +static const struct mg_tcpip_spi spi = {NULL, hwspecific_spi_begin, hwspecific_spi_end, hwspecific_spi_txn}; + +#ifndef CYW43_RESOURCE_ATTRIBUTE +#define CYW43_RESOURCE_ATTRIBUTE +#endif +#include "pico-sdk/lib/cyw43-driver/firmware/w43439A0_7_95_49_00_combined.h" +#include "pico-sdk/lib/cyw43-driver/firmware/wifi_nvram_43439.h" +static const struct mg_tcpip_driver_cyw_firmware fw = { + (const uint8_t *)w43439A0_7_95_49_00_combined, (size_t)CYW43_WIFI_FW_LEN, + (const uint8_t *)wifi_nvram_4343, (size_t)sizeof(wifi_nvram_4343), + (const uint8_t *)(w43439A0_7_95_49_00_combined + sizeof(w43439A0_7_95_49_00_combined) - CYW43_CLM_LEN), (size_t)CYW43_CLM_LEN}; + +// mif user states +enum {AP, SCANNING, STOPPING_AP, CONNECTING, READY}; +static unsigned int state; + + +static void mif_fn(struct mg_tcpip_if *ifp, int ev, void *ev_data) { + // TODO(): should we include this inside ifp ? add an fn_data ? + if (ev == MG_TCPIP_EV_ST_CHG) { + MG_INFO(("State change: %u", *(uint8_t *) ev_data)); + } + switch(state) { + case AP: // we are in AP mode, wait for a user connection to trigger a scan or a connection to a network + if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_READY) { + MG_INFO(("Access Point READY !")); + + // simulate user request to scan for networks + bool res = mg_wifi_scan(); + MG_INFO(("Starting scan: %s", res ? "OK":"FAIL")); + if (res) state = SCANNING; + } + break; + case SCANNING: + if (ev == MG_TCPIP_EV_WIFI_SCAN_RESULT) { + struct mg_wifi_scan_bss_data *bss = (struct mg_wifi_scan_bss_data *) ev_data; + MG_INFO(("BSS: %.*s (%u) (%M) %d dBm %u", bss->SSID.len, bss->SSID.buf, bss->channel, mg_print_mac, bss->BSSID, (int) bss->RSSI, bss->security)); + } else if (ev == MG_TCPIP_EV_WIFI_SCAN_END) { + //struct mg_tcpip_driver_cyw_data *d = (struct mg_tcpip_driver_cyw_data *) ifp->driver_data; + MG_INFO(("Wi-Fi scan finished")); + + // simulate user selection of a network (1/2: stop AP) + bool res = mg_wifi_ap_stop(); + MG_INFO(("Manually stopping AP: %s", res ? "OK":"FAIL")); + if (res) state = STOPPING_AP; + // else we have a hw/fw problem + } + break; + case STOPPING_AP: + if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_DOWN) { + struct mg_wifi_data *wifi = &((struct mg_tcpip_driver_cyw_data *) ifp->driver_data)->wifi; + wifi->apmode = false; + + // simulate user selection of a network (2/2: actual connect) + bool res = mg_wifi_connect(wifi); + MG_INFO(("Manually connecting: %s", res ? "OK":"FAIL")); + if (res) { + state = CONNECTING; + } // else manually start AP as below + } + break; + case CONNECTING: + if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_READY) { + MG_INFO(("READY!")); + state = READY; + + // simulate user code disconnection and go back to AP mode (1/2: disconnect) + bool res = mg_wifi_disconnect(); + MG_INFO(("Manually disconnecting: %s", res ? "OK":"FAIL")); + } else if (ev == MG_TCPIP_EV_WIFI_CONNECT_ERR) { + MG_ERROR(("Wi-Fi connect failed")); + // manually start AP as below + } + break; + case READY: + // go back to AP mode after a disconnection (simulation 2/2), you could retry + if (ev == MG_TCPIP_EV_ST_CHG && *(uint8_t *) ev_data == MG_TCPIP_STATE_DOWN) { + struct mg_wifi_data *wifi = &((struct mg_tcpip_driver_cyw_data *) ifp->driver_data)->wifi; + bool res = mg_wifi_ap_start(wifi); + MG_INFO(("Disconnected")); + MG_INFO(("Manually starting AP: %s", res ? "OK":"FAIL")); + if (res) { + state = AP; + wifi->apmode = true; + } + } + break; + } +} + +static struct mg_tcpip_driver_cyw_data d = { + {WIFI_SSID, WIFI_PASS, "mongoose", "mongoose", 0, 0, 0, 0, 10, true}, (struct mg_tcpip_spi *)&spi, (struct mg_tcpip_driver_cyw_firmware *)&fw, false}; + +static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) { + if (ev == MG_EV_HTTP_MSG) { // New HTTP request received + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + uint64_t tick = to_ms_since_boot(get_absolute_time()); + if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) { + mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), tick); + } else { + mg_http_reply(c, 200, "", "Hi from Mongoose, tick %llu\n", tick); + } + } +} + +static void driver_init(struct mg_mgr *mgr) { + d.wifi.apip = MG_IPV4(192, 168, 169, 1), + d.wifi.apmask = MG_IPV4(255, 255, 255, 0), + state = d.wifi.apmode ? AP : CONNECTING; + // Initialise Mongoose network stack + // Either set use_dhcp or enter a static config. + // For static configuration, specify IP/mask/GW in network byte order + struct mg_tcpip_if mif = { + .ip = 0, + .driver = (struct mg_tcpip_driver *)&mg_tcpip_driver_cyw, + .driver_data = (struct mg_tcpip_driver_cyw_data*)&d, + .fn = mif_fn, +// .recv_queue.size = 8192 + }; + mg_tcpip_init(mgr, &mif); +} + +// In an RTOS environment, give this task 8 KB of stack space. +static void run_mongoose(void) { + struct mg_mgr mgr; + mg_mgr_init(&mgr); + driver_init(&mgr); + mg_log_set(MG_LL_DEBUG); // Set log level + MG_INFO(("Hardware initialised, starting firmware...")); + mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL); + for (;;) { + mg_mgr_poll(&mgr, 1); + } +} + +int main(void) { + // initialize stdio + stdio_init_all(); + hwspecific_spi_init(); + run_mongoose(); + return 0; +} diff --git a/tutorials/pico-sdk/rm2-pico/mongoose_config.h b/tutorials/pico-sdk/rm2-pico/mongoose_config.h new file mode 100644 index 00000000..0ea64963 --- /dev/null +++ b/tutorials/pico-sdk/rm2-pico/mongoose_config.h @@ -0,0 +1,5 @@ +#define MG_ARCH MG_ARCH_PICOSDK + +#define MG_ENABLE_TCPIP 1 +#define MG_ENABLE_DRIVER_CYW 1 +#define MG_ENABLE_TCPIP_DRIVER_INIT 0