├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── arch └── cc.h ├── fs ├── index.html └── pico.png ├── fsdata.c ├── lwipopts.h ├── regen-fsdata.sh ├── tusb_config.h ├── tusb_lwip_glue.c ├── tusb_lwip_glue.h ├── usb_descriptors.c └── webserver.c /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | makefsdata 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tinyusb"] 2 | path = tinyusb 3 | url = https://github.com/raspberrypi/tinyusb.git 4 | branch = pico 5 | [submodule "pico-sdk"] 6 | path = pico-sdk 7 | url = https://github.com/raspberrypi/pico-sdk.git 8 | [submodule "lwip"] 9 | path = lwip 10 | url = https://git.savannah.nongnu.org/git/lwip.git 11 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | # We prefer to have all linked submodules at toplevel 4 | set(PICO_TINYUSB_PATH ${CMAKE_CURRENT_SOURCE_DIR}/tinyusb) 5 | 6 | include(pico-sdk/pico_sdk_init.cmake) 7 | project(pico_webserver) 8 | pico_sdk_init() 9 | 10 | # LWIP 11 | set(LWIP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lwip) 12 | set (LWIP_INCLUDE_DIRS 13 | "${LWIP_DIR}/src/include" 14 | "${CMAKE_CURRENT_SOURCE_DIR}" 15 | ) 16 | include(${LWIP_DIR}/src/Filelists.cmake) 17 | 18 | # Extra stuff from TinyUSB, that is not part of tinyusb_device library 19 | set(TINYUSB_LIBNETWORKING_SOURCES 20 | ${PICO_TINYUSB_PATH}/lib/networking/dhserver.c 21 | ${PICO_TINYUSB_PATH}/lib/networking/rndis_reports.c 22 | ) 23 | 24 | add_executable(${PROJECT_NAME} webserver.c tusb_lwip_glue.c usb_descriptors.c ${TINYUSB_LIBNETWORKING_SOURCES}) 25 | 26 | pico_enable_stdio_usb(${PROJECT_NAME} 0) 27 | pico_enable_stdio_uart(${PROJECT_NAME} 0) 28 | target_include_directories(${PROJECT_NAME} PRIVATE ${LWIP_INCLUDE_DIRS} ${PICO_TINYUSB_PATH}/src ${PICO_TINYUSB_PATH}/lib/networking) 29 | target_link_libraries(${PROJECT_NAME} pico_stdlib pico_unique_id tinyusb_device lwipallapps lwipcore) 30 | pico_add_extra_outputs(${PROJECT_NAME}) 31 | target_compile_definitions(${PROJECT_NAME} PRIVATE PICO_ENTER_USB_BOOT_ON_EXIT=1) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Based on TinyUSB example code: 2 | 3 | == 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | == 26 | 27 | Uses some files from lwip licensed BSD: 28 | 29 | == 30 | Copyright (c) 2001, 2002 Swedish Institute of Computer Science. 31 | All rights reserved. 32 | 33 | Redistribution and use in source and binary forms, with or without modification, 34 | are permitted provided that the following conditions are met: 35 | 36 | 1. Redistributions of source code must retain the above copyright notice, 37 | this list of conditions and the following disclaimer. 38 | 2. Redistributions in binary form must reproduce the above copyright notice, 39 | this list of conditions and the following disclaimer in the documentation 40 | and/or other materials provided with the distribution. 41 | 3. The name of the author may not be used to endorse or promote products 42 | derived from this software without specific prior written permission. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 45 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 46 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 47 | SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 48 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 49 | OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 50 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 51 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 52 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 53 | OF SUCH DAMAGE. 54 | == 55 | 56 | 57 | Other files, build scripts and packaging, public domain: 58 | 59 | == 60 | This is free and unencumbered software released into the public domain. 61 | 62 | Anyone is free to copy, modify, publish, use, compile, sell, or 63 | distribute this software, either in source code form or as a compiled 64 | binary, for any purpose, commercial or non-commercial, and by any 65 | means. 66 | 67 | In jurisdictions that recognize copyright laws, the author or authors 68 | of this software dedicate any and all copyright interest in the 69 | software to the public domain. We make this dedication for the benefit 70 | of the public at large and to the detriment of our heirs and 71 | successors. We intend this dedication to be an overt act of 72 | relinquishment in perpetuity of all present and future rights to this 73 | software under copyright law. 74 | 75 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 76 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 77 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 78 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 79 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 80 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 81 | OTHER DEALINGS IN THE SOFTWARE. 82 | 83 | For more information, please refer to 84 | == 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pico-webserver 2 | 3 | Webserver example that came with TinyUSB slightly modified to run on a Raspberry Pi Pico. 4 | Lets the Pico pretend to be a USB Ethernet device. Runs a webinterface at http://192.168.7.1/ 5 | 6 | ## Build dependencies 7 | 8 | On Debian: 9 | 10 | ``` 11 | sudo apt install git build-essential cmake gcc-arm-none-eabi 12 | ``` 13 | 14 | Your Linux distribution does need to provide a recent CMake (3.13+). 15 | If not, compile [CMake from source](https://cmake.org/download/#latest) first. 16 | 17 | ## Build instructions 18 | 19 | ``` 20 | git clone --depth 1 https://github.com/maxnet/pico-webserver 21 | cd pico-webserver 22 | git submodule update --init 23 | mkdir -p build 24 | cd build 25 | cmake .. 26 | make 27 | ``` 28 | 29 | Copy the resulting pico_webserver.uf2 file to the Pico mass storage device manually. 30 | Webserver will be available at http://192.168.7.1/ 31 | 32 | Content it is serving is in /fs 33 | If you change any files there, run ./regen-fsdata.sh 34 | 35 | By default it shows a webpage that led you toggle the Pico's led, and allows you to switch to BOOTSEL mode. 36 | -------------------------------------------------------------------------------- /arch/cc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * 31 | */ 32 | #ifndef __CC_H__ 33 | #define __CC_H__ 34 | 35 | //#include "cpu.h" 36 | 37 | typedef int sys_prot_t; 38 | 39 | 40 | 41 | /* define compiler specific symbols */ 42 | #if defined (__ICCARM__) 43 | 44 | #define PACK_STRUCT_BEGIN 45 | #define PACK_STRUCT_STRUCT 46 | #define PACK_STRUCT_END 47 | #define PACK_STRUCT_FIELD(x) x 48 | #define PACK_STRUCT_USE_INCLUDES 49 | 50 | #elif defined (__CC_ARM) 51 | 52 | #define PACK_STRUCT_BEGIN __packed 53 | #define PACK_STRUCT_STRUCT 54 | #define PACK_STRUCT_END 55 | #define PACK_STRUCT_FIELD(x) x 56 | 57 | #elif defined (__GNUC__) 58 | 59 | #define PACK_STRUCT_BEGIN 60 | #define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) 61 | #define PACK_STRUCT_END 62 | #define PACK_STRUCT_FIELD(x) x 63 | 64 | #elif defined (__TASKING__) 65 | 66 | #define PACK_STRUCT_BEGIN 67 | #define PACK_STRUCT_STRUCT 68 | #define PACK_STRUCT_END 69 | #define PACK_STRUCT_FIELD(x) x 70 | 71 | #endif 72 | 73 | #ifndef LWIP_PLATFORM_ASSERT 74 | #define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0) 75 | #endif 76 | 77 | #endif /* __CC_H__ */ 78 | -------------------------------------------------------------------------------- /fs/index.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Welcome to your Pico 7 | 8 | 9 | 10 |
11 |

Welcome to your Pico!

12 | 13 | 14 | 15 | 16 | 17 | BOOTSEL 18 | LED 19 | 20 | 21 | 22 |

23 | You can click the LED and BOOTSEL button on the picture above. 24 | 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /fs/pico.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxnet/pico-webserver/b5e1af22c0aa9e2760e247e4f46a70d43eb7fd21/fs/pico.png -------------------------------------------------------------------------------- /lwipopts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Simon Goldschmidt 30 | * 31 | */ 32 | #ifndef __LWIPOPTS_H__ 33 | #define __LWIPOPTS_H__ 34 | 35 | /* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */ 36 | #define NO_SYS 1 37 | #define MEM_ALIGNMENT 4 38 | #define LWIP_RAW 0 39 | #define LWIP_NETCONN 0 40 | #define LWIP_SOCKET 0 41 | #define LWIP_DHCP 0 42 | #define LWIP_ICMP 1 43 | #define LWIP_UDP 1 44 | #define LWIP_TCP 1 45 | #define ETH_PAD_SIZE 0 46 | #define LWIP_IP_ACCEPT_UDP_PORT(p) ((p) == PP_NTOHS(67)) 47 | 48 | #define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/) 49 | #define TCP_SND_BUF (2 * TCP_MSS) 50 | 51 | #define ETHARP_SUPPORT_STATIC_ENTRIES 1 52 | 53 | #define LWIP_HTTPD_CGI 1 54 | #ifndef LWIP_HTTPD_SSI 55 | #define LWIP_HTTPD_SSI 0 56 | #define LWIP_HTTPD_SSI_INCLUDE_TAG 0 57 | #endif 58 | #define HTTPD_USE_CUSTOM_FSDATA 1 59 | #define HTTPD_FSDATA_FILE "../../../../fsdata.c" 60 | 61 | #define LWIP_SINGLE_NETIF 1 62 | 63 | #endif /* __LWIPOPTS_H__ */ 64 | -------------------------------------------------------------------------------- /regen-fsdata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -f makefsdata ]; then 4 | # Doing this outside cmake as we don't want it cross-compiled but for host 5 | echo Compiling makefsdata 6 | gcc -o makefsdata -Ilwip/src/include -I. lwip/src/apps/http/makefsdata/makefsdata.c 7 | fi 8 | 9 | echo Regenerating fsdata.c 10 | ./makefsdata 11 | echo Done 12 | -------------------------------------------------------------------------------- /tusb_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #ifndef _TUSB_CONFIG_H_ 27 | #define _TUSB_CONFIG_H_ 28 | 29 | #ifdef __cplusplus 30 | extern "C" { 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | // COMMON CONFIGURATION 35 | //-------------------------------------------------------------------- 36 | 37 | // defined by board.mk 38 | #ifndef CFG_TUSB_MCU 39 | #error CFG_TUSB_MCU must be defined 40 | #endif 41 | 42 | // RHPort number used for device can be defined by board.mk, default to port 0 43 | #ifndef BOARD_DEVICE_RHPORT_NUM 44 | #define BOARD_DEVICE_RHPORT_NUM 0 45 | #endif 46 | 47 | // RHPort max operational speed can defined by board.mk 48 | // Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed 49 | #ifndef BOARD_DEVICE_RHPORT_SPEED 50 | #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ 51 | CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56) 52 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED 53 | #else 54 | #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED 55 | #endif 56 | #endif 57 | 58 | // Device mode with rhport and speed defined by board.mk 59 | #if BOARD_DEVICE_RHPORT_NUM == 0 60 | #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 61 | #elif BOARD_DEVICE_RHPORT_NUM == 1 62 | #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) 63 | #else 64 | #error "Incorrect RHPort configuration" 65 | #endif 66 | 67 | #ifndef CFG_TUSB_OS 68 | #define CFG_TUSB_OS OPT_OS_NONE 69 | #endif 70 | 71 | // CFG_TUSB_DEBUG is defined by compiler in DEBUG build 72 | // #define CFG_TUSB_DEBUG 0 73 | 74 | /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. 75 | * Tinyusb use follows macros to declare transferring memory so that they can be put 76 | * into those specific section. 77 | * e.g 78 | * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) 79 | * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) 80 | */ 81 | #ifndef CFG_TUSB_MEM_SECTION 82 | #define CFG_TUSB_MEM_SECTION 83 | #endif 84 | 85 | #ifndef CFG_TUSB_MEM_ALIGN 86 | #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) 87 | #endif 88 | 89 | //-------------------------------------------------------------------- 90 | // DEVICE CONFIGURATION 91 | //-------------------------------------------------------------------- 92 | 93 | #ifndef CFG_TUD_ENDPOINT0_SIZE 94 | #define CFG_TUD_ENDPOINT0_SIZE 64 95 | #endif 96 | 97 | //------------- CLASS -------------// 98 | #define CFG_TUD_CDC 0 99 | #define CFG_TUD_MSC 0 100 | #define CFG_TUD_HID 0 101 | #define CFG_TUD_MIDI 0 102 | #define CFG_TUD_VENDOR 0 103 | #define CFG_TUD_NET 1 104 | 105 | #ifdef __cplusplus 106 | } 107 | #endif 108 | 109 | #endif /* _TUSB_CONFIG_H_ */ 110 | -------------------------------------------------------------------------------- /tusb_lwip_glue.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Based on tinyUSB example that is: Copyright (c) 2020 Peter Lawrence 5 | * Modified for Pico by Floris Bos 6 | * 7 | * influenced by lrndis https://github.com/fetisov/lrndis 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | * 27 | */ 28 | 29 | #include "tusb_lwip_glue.h" 30 | #include "pico/unique_id.h" 31 | 32 | /* lwip context */ 33 | static struct netif netif_data; 34 | 35 | /* shared between tud_network_recv_cb() and service_traffic() */ 36 | static struct pbuf *received_frame; 37 | 38 | /* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */ 39 | /* ideally speaking, this should be generated from the hardware's unique ID (if available) */ 40 | /* it is suggested that the first byte is 0x02 to indicate a link-local address */ 41 | const uint8_t tud_network_mac_address[6] = {0x02,0x02,0x84,0x6A,0x96,0x00}; 42 | 43 | /* network parameters of this MCU */ 44 | static const ip_addr_t ipaddr = IPADDR4_INIT_BYTES(192, 168, 7, 1); 45 | static const ip_addr_t netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0); 46 | static const ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0); 47 | 48 | /* database IP addresses that can be offered to the host; this must be in RAM to store assigned MAC addresses */ 49 | static dhcp_entry_t entries[] = 50 | { 51 | /* mac ip address lease time */ 52 | { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 2), 24 * 60 * 60 }, 53 | { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 3), 24 * 60 * 60 }, 54 | { {0}, IPADDR4_INIT_BYTES(192, 168, 7, 4), 24 * 60 * 60 }, 55 | }; 56 | 57 | static const dhcp_config_t dhcp_config = 58 | { 59 | .router = IPADDR4_INIT_BYTES(0, 0, 0, 0), /* router address (if any) */ 60 | .port = 67, /* listen port */ 61 | .dns = IPADDR4_INIT_BYTES(0, 0, 0, 0), /* dns server (if any) */ 62 | "", /* dns suffix */ 63 | TU_ARRAY_SIZE(entries), /* num entry */ 64 | entries /* entries */ 65 | }; 66 | 67 | static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) 68 | { 69 | (void)netif; 70 | 71 | for (;;) 72 | { 73 | /* if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do */ 74 | if (!tud_ready()) 75 | return ERR_USE; 76 | 77 | /* if the network driver can accept another packet, we make it happen */ 78 | if (tud_network_can_xmit()) 79 | { 80 | tud_network_xmit(p, 0 /* unused for this example */); 81 | return ERR_OK; 82 | } 83 | 84 | /* transfer execution to TinyUSB in the hopes that it will finish transmitting the prior packet */ 85 | tud_task(); 86 | } 87 | } 88 | 89 | static err_t output_fn(struct netif *netif, struct pbuf *p, const ip_addr_t *addr) 90 | { 91 | return etharp_output(netif, p, addr); 92 | } 93 | 94 | static err_t netif_init_cb(struct netif *netif) 95 | { 96 | LWIP_ASSERT("netif != NULL", (netif != NULL)); 97 | netif->mtu = CFG_TUD_NET_MTU; 98 | netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; 99 | netif->state = NULL; 100 | netif->name[0] = 'E'; 101 | netif->name[1] = 'X'; 102 | netif->linkoutput = linkoutput_fn; 103 | netif->output = output_fn; 104 | return ERR_OK; 105 | } 106 | 107 | void init_lwip(void) 108 | { 109 | struct netif *netif = &netif_data; 110 | 111 | /* Fixup MAC address based on flash serial */ 112 | //pico_unique_board_id_t id; 113 | //pico_get_unique_board_id(&id); 114 | //memcpy( (tud_network_mac_address)+1, id.id, 5); 115 | // Fixing up does not work because tud_network_mac_address is const 116 | 117 | /* Initialize tinyUSB */ 118 | tusb_init(); 119 | 120 | /* Initialize lwip */ 121 | lwip_init(); 122 | 123 | /* the lwip virtual MAC address must be different from the host's; to ensure this, we toggle the LSbit */ 124 | netif->hwaddr_len = sizeof(tud_network_mac_address); 125 | memcpy(netif->hwaddr, tud_network_mac_address, sizeof(tud_network_mac_address)); 126 | netif->hwaddr[5] ^= 0x01; 127 | 128 | netif = netif_add(netif, &ipaddr, &netmask, &gateway, NULL, netif_init_cb, ip_input); 129 | netif_set_default(netif); 130 | } 131 | 132 | void tud_network_init_cb(void) 133 | { 134 | /* if the network is re-initializing and we have a leftover packet, we must do a cleanup */ 135 | if (received_frame) 136 | { 137 | pbuf_free(received_frame); 138 | received_frame = NULL; 139 | } 140 | } 141 | 142 | bool tud_network_recv_cb(const uint8_t *src, uint16_t size) 143 | { 144 | /* this shouldn't happen, but if we get another packet before 145 | parsing the previous, we must signal our inability to accept it */ 146 | if (received_frame) return false; 147 | 148 | if (size) 149 | { 150 | struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL); 151 | 152 | if (p) 153 | { 154 | /* pbuf_alloc() has already initialized struct; all we need to do is copy the data */ 155 | memcpy(p->payload, src, size); 156 | 157 | /* store away the pointer for service_traffic() to later handle */ 158 | received_frame = p; 159 | } 160 | } 161 | 162 | return true; 163 | } 164 | 165 | uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) 166 | { 167 | struct pbuf *p = (struct pbuf *)ref; 168 | struct pbuf *q; 169 | uint16_t len = 0; 170 | 171 | (void)arg; /* unused for this example */ 172 | 173 | /* traverse the "pbuf chain"; see ./lwip/src/core/pbuf.c for more info */ 174 | for(q = p; q != NULL; q = q->next) 175 | { 176 | memcpy(dst, (char *)q->payload, q->len); 177 | dst += q->len; 178 | len += q->len; 179 | if (q->len == q->tot_len) break; 180 | } 181 | 182 | return len; 183 | } 184 | 185 | void service_traffic(void) 186 | { 187 | /* handle any packet received by tud_network_recv_cb() */ 188 | if (received_frame) 189 | { 190 | ethernet_input(received_frame, &netif_data); 191 | pbuf_free(received_frame); 192 | received_frame = NULL; 193 | tud_network_recv_renew(); 194 | } 195 | 196 | sys_check_timeouts(); 197 | } 198 | 199 | void dhcpd_init() 200 | { 201 | while (dhserv_init(&dhcp_config) != ERR_OK); 202 | } 203 | 204 | void wait_for_netif_is_up() 205 | { 206 | while (!netif_is_up(&netif_data)); 207 | } 208 | 209 | 210 | /* lwip platform specific routines for Pico */ 211 | auto_init_mutex(lwip_mutex); 212 | static int lwip_mutex_count = 0; 213 | 214 | sys_prot_t sys_arch_protect(void) 215 | { 216 | uint32_t owner; 217 | if (!mutex_try_enter(&lwip_mutex, &owner)) 218 | { 219 | if (owner != get_core_num()) 220 | { 221 | // Wait until other core releases mutex 222 | mutex_enter_blocking(&lwip_mutex); 223 | } 224 | } 225 | 226 | lwip_mutex_count++; 227 | 228 | return 0; 229 | } 230 | 231 | void sys_arch_unprotect(sys_prot_t pval) 232 | { 233 | (void)pval; 234 | 235 | if (lwip_mutex_count) 236 | { 237 | lwip_mutex_count--; 238 | if (!lwip_mutex_count) 239 | { 240 | mutex_exit(&lwip_mutex); 241 | } 242 | } 243 | } 244 | 245 | uint32_t sys_now(void) 246 | { 247 | return to_ms_since_boot( get_absolute_time() ); 248 | } 249 | -------------------------------------------------------------------------------- /tusb_lwip_glue.h: -------------------------------------------------------------------------------- 1 | #ifndef _TUSB_LWIP_GLUE_H_ 2 | #define _TUSB_LWIP_GLUE_H_ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include "tusb.h" 9 | #include "dhserver.h" 10 | #include "dnserver.h" 11 | #include "lwip/init.h" 12 | #include "lwip/timeouts.h" 13 | #include "lwip/apps/httpd.h" 14 | 15 | void init_lwip(); 16 | void wait_for_netif_is_up(); 17 | void dhcpd_init(); 18 | void service_traffic(); 19 | 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif -------------------------------------------------------------------------------- /usb_descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2019 Ha Thach (tinyusb.org) 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | #include "tusb.h" 27 | #include "pico/unique_id.h" 28 | 29 | /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. 30 | * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. 31 | * 32 | * Auto ProductID layout's Bitmap: 33 | * [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB] 34 | */ 35 | #define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) 36 | #define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ 37 | _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) | _PID_MAP(NET, 5) ) 38 | 39 | // String Descriptor Index 40 | enum 41 | { 42 | STRID_LANGID = 0, 43 | STRID_MANUFACTURER, 44 | STRID_PRODUCT, 45 | STRID_SERIAL, 46 | STRID_INTERFACE, 47 | STRID_MAC 48 | }; 49 | 50 | enum 51 | { 52 | ITF_NUM_CDC = 0, 53 | ITF_NUM_CDC_DATA, 54 | ITF_NUM_TOTAL 55 | }; 56 | 57 | enum 58 | { 59 | CONFIG_ID_RNDIS = 0, 60 | CONFIG_ID_ECM = 1, 61 | CONFIG_ID_COUNT 62 | }; 63 | 64 | //--------------------------------------------------------------------+ 65 | // Device Descriptors 66 | //--------------------------------------------------------------------+ 67 | tusb_desc_device_t const desc_device = 68 | { 69 | .bLength = sizeof(tusb_desc_device_t), 70 | .bDescriptorType = TUSB_DESC_DEVICE, 71 | .bcdUSB = 0x0200, 72 | 73 | // Use Interface Association Descriptor (IAD) device class 74 | .bDeviceClass = TUSB_CLASS_MISC, 75 | .bDeviceSubClass = MISC_SUBCLASS_COMMON, 76 | .bDeviceProtocol = MISC_PROTOCOL_IAD, 77 | 78 | .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, 79 | 80 | .idVendor = 0xCafe, 81 | .idProduct = USB_PID, 82 | .bcdDevice = 0x0101, 83 | 84 | .iManufacturer = STRID_MANUFACTURER, 85 | .iProduct = STRID_PRODUCT, 86 | .iSerialNumber = STRID_SERIAL, 87 | 88 | .bNumConfigurations = CONFIG_ID_COUNT // multiple configurations 89 | }; 90 | 91 | // Invoked when received GET DEVICE DESCRIPTOR 92 | // Application return pointer to descriptor 93 | uint8_t const * tud_descriptor_device_cb(void) 94 | { 95 | return (uint8_t const *) &desc_device; 96 | } 97 | 98 | //--------------------------------------------------------------------+ 99 | // Configuration Descriptor 100 | //--------------------------------------------------------------------+ 101 | #define MAIN_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_RNDIS_DESC_LEN) 102 | #define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN) 103 | 104 | #if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX 105 | // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number 106 | // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ... 107 | #define EPNUM_NET_NOTIF 0x81 108 | #define EPNUM_NET_OUT 0x02 109 | #define EPNUM_NET_IN 0x82 110 | 111 | #elif CFG_TUSB_MCU == OPT_MCU_SAMG 112 | // SAMG doesn't support a same endpoint number with different direction IN and OUT 113 | // e.g EP1 OUT & EP1 IN cannot exist together 114 | #define EPNUM_NET_NOTIF 0x81 115 | #define EPNUM_NET_OUT 0x02 116 | #define EPNUM_NET_IN 0x83 117 | 118 | #else 119 | #define EPNUM_NET_NOTIF 0x81 120 | #define EPNUM_NET_OUT 0x02 121 | #define EPNUM_NET_IN 0x82 122 | #endif 123 | 124 | static uint8_t const rndis_configuration[] = 125 | { 126 | // Config number (index+1), interface count, string index, total length, attribute, power in mA 127 | TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS+1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100), 128 | 129 | // Interface number, string index, EP notification address and size, EP data address (out, in) and size. 130 | TUD_RNDIS_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE), 131 | }; 132 | 133 | static uint8_t const ecm_configuration[] = 134 | { 135 | // Config number (index+1), interface count, string index, total length, attribute, power in mA 136 | TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM+1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100), 137 | 138 | // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size. 139 | 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), 140 | }; 141 | 142 | // Configuration array: RNDIS and CDC-ECM 143 | // - Windows only works with RNDIS 144 | // - MacOS only works with CDC-ECM 145 | // - Linux will work on both 146 | // Note index is Num-1x 147 | static uint8_t const * const configuration_arr[2] = 148 | { 149 | [CONFIG_ID_RNDIS] = rndis_configuration, 150 | [CONFIG_ID_ECM ] = ecm_configuration 151 | }; 152 | 153 | // Invoked when received GET CONFIGURATION DESCRIPTOR 154 | // Application return pointer to descriptor 155 | // Descriptor contents must exist long enough for transfer to complete 156 | uint8_t const * tud_descriptor_configuration_cb(uint8_t index) 157 | { 158 | return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL; 159 | } 160 | 161 | //--------------------------------------------------------------------+ 162 | // String Descriptors 163 | //--------------------------------------------------------------------+ 164 | 165 | // array of pointer to string descriptors 166 | static char const* string_desc_arr [] = 167 | { 168 | [STRID_LANGID] = (const char[]) { 0x09, 0x04 }, // supported language is English (0x0409) 169 | [STRID_MANUFACTURER] = "TinyUSB", // Manufacturer 170 | [STRID_PRODUCT] = "Go to http://192.168.7.1/", // Product 171 | //[STRID_SERIAL] = "123456", // Serial 172 | [STRID_INTERFACE] = "TinyUSB Network Interface" // Interface Description 173 | 174 | // STRID_MAC index is handled separately 175 | // STRID_SERIAL index is handled seperately 176 | }; 177 | 178 | static uint16_t _desc_str[32]; 179 | 180 | // Invoked when received GET STRING DESCRIPTOR request 181 | // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete 182 | uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) 183 | { 184 | (void) langid; 185 | 186 | unsigned int chr_count = 0; 187 | 188 | if (STRID_LANGID == index) 189 | { 190 | memcpy(&_desc_str[1], string_desc_arr[STRID_LANGID], 2); 191 | chr_count = 1; 192 | } 193 | else if (STRID_SERIAL == index) 194 | { 195 | pico_unique_board_id_t id; 196 | pico_get_unique_board_id(&id); 197 | 198 | for (unsigned i=0; i> 4) & 0xf]; 201 | _desc_str[1+chr_count++] = "0123456789ABCDEF"[(id.id[i] >> 0) & 0xf]; 202 | } 203 | } 204 | else if (STRID_MAC == index) 205 | { 206 | // Convert MAC address into UTF-16 207 | 208 | for (unsigned i=0; i> 4) & 0xf]; 211 | _desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf]; 212 | } 213 | } 214 | else 215 | { 216 | // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. 217 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors 218 | 219 | if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; 220 | 221 | const char* str = string_desc_arr[index]; 222 | 223 | // Cap at max char 224 | chr_count = strlen(str); 225 | if ( chr_count > (TU_ARRAY_SIZE(_desc_str) - 1)) chr_count = TU_ARRAY_SIZE(_desc_str) - 1; 226 | 227 | // Convert ASCII string into UTF-16 228 | for (unsigned int i=0; i