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