├── .gitignore
├── fs
├── pico_0.jpg
├── pico_1.jpg
├── Index.html
└── index.html
├── uf2
└── pico_webserver.uf2
├── Peek_2021-03-14_12-40.gif
├── .gitmodules
├── regen-fsdata.sh
├── tusb_lwip_glue.h
├── README.md
├── CMakeLists.txt
├── webserver.c
├── arch
└── cc.h
├── lwipopts.h
├── LICENSE
├── tusb_config.h
├── tusb_lwip_glue.c
└── usb_descriptors.c
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | makefsdata
3 |
--------------------------------------------------------------------------------
/fs/pico_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hermann-SW/pico-webserver/HEAD/fs/pico_0.jpg
--------------------------------------------------------------------------------
/fs/pico_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hermann-SW/pico-webserver/HEAD/fs/pico_1.jpg
--------------------------------------------------------------------------------
/uf2/pico_webserver.uf2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hermann-SW/pico-webserver/HEAD/uf2/pico_webserver.uf2
--------------------------------------------------------------------------------
/Peek_2021-03-14_12-40.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hermann-SW/pico-webserver/HEAD/Peek_2021-03-14_12-40.gif
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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_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
--------------------------------------------------------------------------------
/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 |
20 |
21 |
22 |
23 | You can click the LED and BOOTSEL button on the picture above.
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/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 |
20 |
21 |
22 |
23 | You can click the LED and BOOTSEL button on the picture above.
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/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 -j$(nproc)
27 | ```
28 |
29 | Copy the resulting pico_webserver.uf2 file to the Pico mass storage device manually (find it in uf2 directory for flash&trial as well).
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 |
37 | This is simple demonstration:
38 | 
39 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/webserver.c:
--------------------------------------------------------------------------------
1 | #include "pico/stdlib.h"
2 | #include "pico/bootrom.h"
3 | #include "hardware/watchdog.h"
4 | #include "hardware/structs/watchdog.h"
5 |
6 | #include "tusb_lwip_glue.h"
7 |
8 | #define LED_PIN 25
9 |
10 | char *html[2]={"/index.html","/Index.html"};
11 |
12 | // let our webserver do some dynamic handling
13 | static const char *cgi_toggle_led(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
14 | {
15 | gpio_put(LED_PIN, !gpio_get(LED_PIN));
16 | return html[gpio_get(LED_PIN)];
17 | }
18 |
19 | static const char *cgi_reset_usb_boot(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
20 | {
21 | reset_usb_boot(0, 0);
22 | return html[gpio_get(LED_PIN)];
23 | }
24 |
25 | static const tCGI cgi_handlers[] = {
26 | {
27 | "/toggle_led",
28 | cgi_toggle_led
29 | },
30 | {
31 | "/reset_usb_boot",
32 | cgi_reset_usb_boot
33 | }
34 | };
35 |
36 |
37 | int main()
38 | {
39 | // Initialize tinyusb, lwip, dhcpd and httpd
40 | init_lwip();
41 | wait_for_netif_is_up();
42 | dhcpd_init();
43 | httpd_init();
44 | http_set_cgi_handlers(cgi_handlers, LWIP_ARRAYSIZE(cgi_handlers));
45 |
46 | // For toggle_led
47 | gpio_init(LED_PIN);
48 | gpio_set_dir(LED_PIN, GPIO_OUT);
49 |
50 | while (true)
51 | {
52 | tud_task();
53 | service_traffic();
54 | }
55 |
56 | return 0;
57 | }
58 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | static mutex_t 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 |
--------------------------------------------------------------------------------
/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