Add TinyUSB example

This commit is contained in:
cpq 2022-12-19 11:43:25 +00:00
parent 6921227ec8
commit 57a5c0ba38
40 changed files with 23502 additions and 313 deletions

View File

@ -47,7 +47,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- run: sudo apt-get install libmbedtls-dev libpcap-dev - run: sudo apt -y install libmbedtls-dev libpcap-dev gcc-arm-none-eabi
- run: make clean examples - run: make clean examples
- run: make clean test MG_ENABLE_POLL=1 - run: make clean test MG_ENABLE_POLL=1
macos: macos:

View File

@ -46,10 +46,9 @@ all:
tall: mg_prefix unamalgamated test mip_test arm examples vc98 vc17 vc22 mingw mingw++ fuzz tall: mg_prefix unamalgamated test mip_test arm examples vc98 vc17 vc22 mingw mingw++ fuzz
mip_test: test/mip_test.c mongoose.c mongoose.h Makefile mip_test: test/mip_test.c mongoose.c mongoose.h Makefile mip_tap_test
$(CC) test/mip_test.c $(INCS) $(WARN) $(OPTS) $(C_WARN) $(ASAN) -o $@ $(CC) test/mip_test.c $(INCS) $(WARN) $(OPTS) $(C_WARN) $(ASAN) -o $@
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@ ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./$@
$(MAKE) mip_tap_test
mip_tap_test: test/mip_tap_test.c mongoose.c mongoose.h Makefile mip_tap_test: test/mip_tap_test.c mongoose.c mongoose.h Makefile
$(CC) test/mip_tap_test.c $(INCS) $(WARN) $(OPTS) $(C_WARN) $(ASAN) -o $@ $(CC) test/mip_tap_test.c $(INCS) $(WARN) $(OPTS) $(C_WARN) $(ASAN) -o $@

View File

@ -9,6 +9,7 @@ struct mip_spi spi = {
[](void *) { digitalWrite(SS_PIN, HIGH); }, // end transaction [](void *) { digitalWrite(SS_PIN, HIGH); }, // end transaction
[](void *, uint8_t c) { return SPI.transfer(c); }, // execute transaction [](void *, uint8_t c) { return SPI.transfer(c); }, // execute transaction
}; };
struct mip_if mif = {.mac = {2, 0, 1, 2, 3, 5}}; // MIP network interface
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@ -22,8 +23,6 @@ void setup() {
delay(3000); delay(3000);
MG_INFO(("Starting TCP/IP stack...")); MG_INFO(("Starting TCP/IP stack..."));
struct mip_if mif = {.mac = {2, 0, 1, 2, 3, 5}};
mif.use_dhcp = true;
mif.driver = &mip_driver_w5500; mif.driver = &mip_driver_w5500;
mif.driver_data = &spi; mif.driver_data = &spi;
mip_init(&mgr, &mif); mip_init(&mgr, &mif);
@ -32,7 +31,7 @@ void setup() {
mg_timer_add( mg_timer_add(
&mgr, 5000, MG_TIMER_REPEAT, &mgr, 5000, MG_TIMER_REPEAT,
[](void *) { [](void *) {
MG_INFO(("ethernet: %s", mip_driver_w5500.up(&spi) ? "up" : "down")); MG_INFO(("ethernet: %s", mip_driver_w5500.up(&mif) ? "up" : "down"));
}, },
NULL); NULL);

View File

@ -18,24 +18,24 @@ void signal_handler(int signo) {
s_signo = signo; s_signo = signo;
} }
static size_t pcap_tx(const void *buf, size_t len, void *userdata) { static size_t pcap_tx(const void *buf, size_t len, struct mip_if *ifp) {
int res = pcap_inject((pcap_t *) userdata, buf, len); int res = pcap_inject((pcap_t *) ifp->driver_data, buf, len);
if (res == PCAP_ERROR) { if (res == PCAP_ERROR) {
MG_ERROR(("pcap_inject: %d", res)); MG_ERROR(("pcap_inject: %d", res));
} }
return res == PCAP_ERROR ? 0 : len; return res == PCAP_ERROR ? 0 : len;
} }
static bool pcap_up(void *userdata) { static bool pcap_up(struct mip_if *ifp) {
return userdata ? true : false; return ifp->driver_data ? true : false;
} }
static size_t pcap_rx(void *buf, size_t len, void *userdata) { static size_t pcap_rx(void *buf, size_t len, struct mip_if *ifp) {
size_t received = 0; size_t received = 0;
struct pcap_pkthdr *hdr = NULL; struct pcap_pkthdr *hdr = NULL;
const unsigned char *pkt = NULL; const unsigned char *pkt = NULL;
usleep(1000); // Sleep 1 millisecond. This is to avoid 100% CPU usleep(1000); // Sleep 1 millisecond. This is to avoid 100% CPU
if (pcap_next_ex((pcap_t *) userdata, &hdr, &pkt) == 1) { // Yes, read if (pcap_next_ex((pcap_t *) ifp->driver_data, &hdr, &pkt) == 1) {
received = hdr->len < len ? hdr->len : len; received = hdr->len < len ? hdr->len : len;
memcpy(buf, pkt, received); memcpy(buf, pkt, received);
} }
@ -133,7 +133,7 @@ int main(int argc, char *argv[]) {
mg_log_set(MG_LL_DEBUG); // Set log level mg_log_set(MG_LL_DEBUG); // Set log level
struct mip_driver driver = {.tx = pcap_tx, .up = pcap_up, .rx = pcap_rx}; struct mip_driver driver = {.tx = pcap_tx, .up = pcap_up, .rx = pcap_rx};
struct mip_if mif = {.use_dhcp = true, .driver = &driver, .driver_data = ph}; struct mip_if mif = {.driver = &driver, .driver_data = ph};
sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1], sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1],
&mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]); &mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]);
mip_init(&mgr, &mif); mip_init(&mgr, &mif);

View File

@ -22,8 +22,8 @@ void signal_handler(int signo) {
s_signo = signo; s_signo = signo;
} }
static size_t tap_tx(const void *buf, size_t len, void *userdata) { static size_t tap_tx(const void *buf, size_t len, struct mip_if *ifp) {
ssize_t res = write(*(int*) userdata, buf, len); ssize_t res = write(*(int*) ifp->driver_data, buf, len);
if (res < 0) { if (res < 0) {
MG_ERROR(("tap_tx failed: %d", errno)); MG_ERROR(("tap_tx failed: %d", errno));
return 0; return 0;
@ -31,12 +31,12 @@ static size_t tap_tx(const void *buf, size_t len, void *userdata) {
return (size_t) res; return (size_t) res;
} }
static bool tap_up(void *userdata) { static bool tap_up(struct mip_if *ifp) {
return userdata ? true : false; return ifp->driver_data ? true : false;
} }
static size_t tap_rx(void *buf, size_t len, void *userdata) { static size_t tap_rx(void *buf, size_t len, struct mip_if *ifp) {
ssize_t received = read(*(int *) userdata, buf, len); ssize_t received = read(*(int *) ifp->driver_data, buf, len);
usleep(1); // This is to avoid 100% CPU usleep(1); // This is to avoid 100% CPU
if (received < 0) return 0; if (received < 0) return 0;
return (size_t) received; return (size_t) received;
@ -93,7 +93,7 @@ int main(int argc, char *argv[]) {
mg_mgr_init(&mgr); // Initialise event manager mg_mgr_init(&mgr); // Initialise event manager
struct mip_driver driver = {.tx = tap_tx, .up = tap_up, .rx = tap_rx}; struct mip_driver driver = {.tx = tap_tx, .up = tap_up, .rx = tap_rx};
struct mip_if mif = {.use_dhcp = true, .driver = &driver, .driver_data = &fd}; struct mip_if mif = {.driver = &driver, .driver_data = &fd};
sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1], sscanf(mac, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &mif.mac[0], &mif.mac[1],
&mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]); &mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]);
mip_init(&mgr, &mif); mip_init(&mgr, &mif);

View File

@ -68,12 +68,11 @@ int main(void) {
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr); mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr);
// Initialise Mongoose network stack // Initialise Mongoose network stack
// Specify MAC address, either set use_dhcp or enter a static config. // Specify MAC address, and IP/mask/GW in network byte order for static
// For static configuration, specify IP/mask/GW in network byte order // IP configuration. If IP/mask/GW are unset, DHCP is going to be used
struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h
struct mip_if mif = { struct mip_if mif = {
.mac = {2, 0, 1, 2, 3, 5}, .mac = {2, 0, 1, 2, 3, 5},
.use_dhcp = true,
.driver = &mip_driver_stm32, .driver = &mip_driver_stm32,
.driver_data = &driver_data, .driver_data = &driver_data,
}; };

View File

@ -44,14 +44,13 @@ static void server(void *args) {
mg_log_set(MG_LL_DEBUG); // Set log level mg_log_set(MG_LL_DEBUG); // Set log level
// Initialise Mongoose network stack // Initialise Mongoose network stack
// Specify MAC address, either set use_dhcp or enter a static config. // Specify MAC address, and IP/mask/GW in network byte order for static
// For static configuration, specify IP/mask/GW in network byte order // IP configuration. If IP/mask/GW are unset, DHCP is going to be used
MG_INFO(("Initializing Ethernet driver")); MG_INFO(("Initializing Ethernet driver"));
ethernet_init(); ethernet_init();
struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h
struct mip_if mif = { struct mip_if mif = {
.mac = {2, 0, 1, 2, 3, 5}, .mac = {2, 0, 1, 2, 3, 5},
.use_dhcp = true,
.driver = &mip_driver_stm32, .driver = &mip_driver_stm32,
.driver_data = &driver_data, .driver_data = &driver_data,
}; };

Binary file not shown.

View File

@ -0,0 +1,48 @@
CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \
-Wformat-truncation -fno-common -Wconversion \
-g3 -Os -ffunction-sections -fdata-sections \
-I. -Iinclude -I../../.. \
-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 $(EXTRA_CFLAGS)
LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c startup.c syscalls.c ../../../mongoose.c
TINYUSB_VERSION ?= 0.14.0
TINYUSB_REPO ?= https://github.com/hathach/tinyusb
SOURCES += tinyusb/src/tusb.c \
tinyusb/src/common/tusb_fifo.c \
tinyusb/src/device/usbd.c \
tinyusb/src/device/usbd_control.c \
tinyusb/src/class/net/ecm_rndis_device.c \
tinyusb/src/class/net/ncm_device.c \
tinyusb/src/portable/synopsys/dwc2/dcd_dwc2.c \
tinyusb/lib/networking/rndis_reports.c \
usb_descriptors.c
CFLAGS += -Itinyusb/src -Itinyusb/lib/networking
CFLAGS += -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_MIP=1 -DMG_ENABLE_PACKED_FS=1 -DMG_IO_SIZE=512 -DMG_ENABLE_CUSTOM_MILLIS=1
CFLAGS += -DSTM32F429xx
CFLAGS += -Wno-conversion -Wno-sign-conversion
ifeq ($(OS),Windows_NT)
RM = cmd /C del /Q /F
else
RM = rm -f
endif
all build example: firmware.bin
tinyusb:
git clone --depth 1 -b $(TINYUSB_VERSION) $(TINYUSB_REPO) $@
$(SOURCES): tinyusb
firmware.elf: $(SOURCES)
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
flash: firmware.bin
st-flash --reset write firmware.bin 0x8000000
clean:
$(RM) firmware.*

View File

@ -0,0 +1,266 @@
/**************************************************************************//**
* @file cmsis_compiler.h
* @brief CMSIS compiler generic header file
* @version V5.0.4
* @date 10. January 2018
******************************************************************************/
/*
* Copyright (c) 2009-2018 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __CMSIS_COMPILER_H
#define __CMSIS_COMPILER_H
#include <stdint.h>
/*
* Arm Compiler 4/5
*/
#if defined ( __CC_ARM )
#include "cmsis_armcc.h"
/*
* Arm Compiler 6 (armclang)
*/
#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050)
#include "cmsis_armclang.h"
/*
* GNU Compiler
*/
#elif defined ( __GNUC__ )
#include "cmsis_gcc.h"
/*
* IAR Compiler
*/
#elif defined ( __ICCARM__ )
#include <cmsis_iccarm.h>
/*
* TI Arm Compiler
*/
#elif defined ( __TI_ARM__ )
#include <cmsis_ccs.h>
#ifndef __ASM
#define __ASM __asm
#endif
#ifndef __INLINE
#define __INLINE inline
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static inline
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE __STATIC_INLINE
#endif
#ifndef __NO_RETURN
#define __NO_RETURN __attribute__((noreturn))
#endif
#ifndef __USED
#define __USED __attribute__((used))
#endif
#ifndef __WEAK
#define __WEAK __attribute__((weak))
#endif
#ifndef __PACKED
#define __PACKED __attribute__((packed))
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT struct __attribute__((packed))
#endif
#ifndef __PACKED_UNION
#define __PACKED_UNION union __attribute__((packed))
#endif
#ifndef __UNALIGNED_UINT32 /* deprecated */
struct __attribute__((packed)) T_UINT32 { uint32_t v; };
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
#endif
#ifndef __UNALIGNED_UINT16_WRITE
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT16_READ
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
#endif
#ifndef __UNALIGNED_UINT32_WRITE
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT32_READ
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
#endif
#ifndef __ALIGNED
#define __ALIGNED(x) __attribute__((aligned(x)))
#endif
#ifndef __RESTRICT
#warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.
#define __RESTRICT
#endif
/*
* TASKING Compiler
*/
#elif defined ( __TASKING__ )
/*
* The CMSIS functions have been implemented as intrinsics in the compiler.
* Please use "carm -?i" to get an up to date list of all intrinsics,
* Including the CMSIS ones.
*/
#ifndef __ASM
#define __ASM __asm
#endif
#ifndef __INLINE
#define __INLINE inline
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static inline
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE __STATIC_INLINE
#endif
#ifndef __NO_RETURN
#define __NO_RETURN __attribute__((noreturn))
#endif
#ifndef __USED
#define __USED __attribute__((used))
#endif
#ifndef __WEAK
#define __WEAK __attribute__((weak))
#endif
#ifndef __PACKED
#define __PACKED __packed__
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT struct __packed__
#endif
#ifndef __PACKED_UNION
#define __PACKED_UNION union __packed__
#endif
#ifndef __UNALIGNED_UINT32 /* deprecated */
struct __packed__ T_UINT32 { uint32_t v; };
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
#endif
#ifndef __UNALIGNED_UINT16_WRITE
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT16_READ
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
#endif
#ifndef __UNALIGNED_UINT32_WRITE
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT32_READ
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
#endif
#ifndef __ALIGNED
#define __ALIGNED(x) __align(x)
#endif
#ifndef __RESTRICT
#warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.
#define __RESTRICT
#endif
/*
* COSMIC Compiler
*/
#elif defined ( __CSMC__ )
#include <cmsis_csm.h>
#ifndef __ASM
#define __ASM _asm
#endif
#ifndef __INLINE
#define __INLINE inline
#endif
#ifndef __STATIC_INLINE
#define __STATIC_INLINE static inline
#endif
#ifndef __STATIC_FORCEINLINE
#define __STATIC_FORCEINLINE __STATIC_INLINE
#endif
#ifndef __NO_RETURN
// NO RETURN is automatically detected hence no warning here
#define __NO_RETURN
#endif
#ifndef __USED
#warning No compiler specific solution for __USED. __USED is ignored.
#define __USED
#endif
#ifndef __WEAK
#define __WEAK __weak
#endif
#ifndef __PACKED
#define __PACKED @packed
#endif
#ifndef __PACKED_STRUCT
#define __PACKED_STRUCT @packed struct
#endif
#ifndef __PACKED_UNION
#define __PACKED_UNION @packed union
#endif
#ifndef __UNALIGNED_UINT32 /* deprecated */
@packed struct T_UINT32 { uint32_t v; };
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
#endif
#ifndef __UNALIGNED_UINT16_WRITE
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT16_READ
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
#endif
#ifndef __UNALIGNED_UINT32_WRITE
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
#endif
#ifndef __UNALIGNED_UINT32_READ
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
#endif
#ifndef __ALIGNED
#warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored.
#define __ALIGNED(x)
#endif
#ifndef __RESTRICT
#warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.
#define __RESTRICT
#endif
#else
#error Unknown compiler.
#endif
#endif /* __CMSIS_COMPILER_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
/**************************************************************************//**
* @file cmsis_version.h
* @brief CMSIS Core(M) Version definitions
* @version V5.0.2
* @date 19. April 2017
******************************************************************************/
/*
* Copyright (c) 2009-2017 ARM Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined ( __ICCARM__ )
#pragma system_include /* treat file as system include file for MISRA check */
#elif defined (__clang__)
#pragma clang system_header /* treat file as system include file */
#endif
#ifndef __CMSIS_VERSION_H
#define __CMSIS_VERSION_H
/* CMSIS Version definitions */
#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */
#define __CM_CMSIS_VERSION_SUB ( 1U) /*!< [15:0] CMSIS Core(M) sub version */
#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \
__CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,270 @@
/******************************************************************************
* @file mpu_armv7.h
* @brief CMSIS MPU API for Armv7-M MPU
* @version V5.0.4
* @date 10. January 2018
******************************************************************************/
/*
* Copyright (c) 2017-2018 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined ( __ICCARM__ )
#pragma system_include /* treat file as system include file for MISRA check */
#elif defined (__clang__)
#pragma clang system_header /* treat file as system include file */
#endif
#ifndef ARM_MPU_ARMV7_H
#define ARM_MPU_ARMV7_H
#define ARM_MPU_REGION_SIZE_32B ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes
#define ARM_MPU_REGION_SIZE_64B ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes
#define ARM_MPU_REGION_SIZE_128B ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes
#define ARM_MPU_REGION_SIZE_256B ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes
#define ARM_MPU_REGION_SIZE_512B ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes
#define ARM_MPU_REGION_SIZE_1KB ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte
#define ARM_MPU_REGION_SIZE_2KB ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes
#define ARM_MPU_REGION_SIZE_4KB ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes
#define ARM_MPU_REGION_SIZE_8KB ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes
#define ARM_MPU_REGION_SIZE_16KB ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes
#define ARM_MPU_REGION_SIZE_32KB ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes
#define ARM_MPU_REGION_SIZE_64KB ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes
#define ARM_MPU_REGION_SIZE_128KB ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes
#define ARM_MPU_REGION_SIZE_256KB ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes
#define ARM_MPU_REGION_SIZE_512KB ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes
#define ARM_MPU_REGION_SIZE_1MB ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte
#define ARM_MPU_REGION_SIZE_2MB ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes
#define ARM_MPU_REGION_SIZE_4MB ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes
#define ARM_MPU_REGION_SIZE_8MB ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes
#define ARM_MPU_REGION_SIZE_16MB ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes
#define ARM_MPU_REGION_SIZE_32MB ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes
#define ARM_MPU_REGION_SIZE_64MB ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes
#define ARM_MPU_REGION_SIZE_128MB ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes
#define ARM_MPU_REGION_SIZE_256MB ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes
#define ARM_MPU_REGION_SIZE_512MB ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes
#define ARM_MPU_REGION_SIZE_1GB ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte
#define ARM_MPU_REGION_SIZE_2GB ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes
#define ARM_MPU_REGION_SIZE_4GB ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes
#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access
#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only
#define ARM_MPU_AP_URO 2U ///!< MPU Access Permission unprivileged access read-only
#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access
#define ARM_MPU_AP_PRO 5U ///!< MPU Access Permission privileged access read-only
#define ARM_MPU_AP_RO 6U ///!< MPU Access Permission read-only access
/** MPU Region Base Address Register Value
*
* \param Region The region to be configured, number 0 to 15.
* \param BaseAddress The base address for the region.
*/
#define ARM_MPU_RBAR(Region, BaseAddress) \
(((BaseAddress) & MPU_RBAR_ADDR_Msk) | \
((Region) & MPU_RBAR_REGION_Msk) | \
(MPU_RBAR_VALID_Msk))
/**
* MPU Memory Access Attributes
*
* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.
* \param IsShareable Region is shareable between multiple bus masters.
* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache.
* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.
*/
#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable) \
((((TypeExtField ) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) | \
(((IsShareable ) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) | \
(((IsCacheable ) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) | \
(((IsBufferable ) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk))
/**
* MPU Region Attribute and Size Register Value
*
* \param DisableExec Instruction access disable bit, 1= disable instruction fetches.
* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode.
* \param AccessAttributes Memory access attribution, see \ref ARM_MPU_ACCESS_.
* \param SubRegionDisable Sub-region disable field.
* \param Size Region size of the region to be configured, for example 4K, 8K.
*/
#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size) \
((((DisableExec ) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | \
(((AccessPermission) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | \
(((AccessAttributes) ) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk)))
/**
* MPU Region Attribute and Size Register Value
*
* \param DisableExec Instruction access disable bit, 1= disable instruction fetches.
* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode.
* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.
* \param IsShareable Region is shareable between multiple bus masters.
* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache.
* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.
* \param SubRegionDisable Sub-region disable field.
* \param Size Region size of the region to be configured, for example 4K, 8K.
*/
#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \
ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size)
/**
* MPU Memory Access Attribute for strongly ordered memory.
* - TEX: 000b
* - Shareable
* - Non-cacheable
* - Non-bufferable
*/
#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U)
/**
* MPU Memory Access Attribute for device memory.
* - TEX: 000b (if non-shareable) or 010b (if shareable)
* - Shareable or non-shareable
* - Non-cacheable
* - Bufferable (if shareable) or non-bufferable (if non-shareable)
*
* \param IsShareable Configures the device memory as shareable or non-shareable.
*/
#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U))
/**
* MPU Memory Access Attribute for normal memory.
* - TEX: 1BBb (reflecting outer cacheability rules)
* - Shareable or non-shareable
* - Cacheable or non-cacheable (reflecting inner cacheability rules)
* - Bufferable or non-bufferable (reflecting inner cacheability rules)
*
* \param OuterCp Configures the outer cache policy.
* \param InnerCp Configures the inner cache policy.
* \param IsShareable Configures the memory as shareable or non-shareable.
*/
#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) & 2U), ((InnerCp) & 1U))
/**
* MPU Memory Access Attribute non-cacheable policy.
*/
#define ARM_MPU_CACHEP_NOCACHE 0U
/**
* MPU Memory Access Attribute write-back, write and read allocate policy.
*/
#define ARM_MPU_CACHEP_WB_WRA 1U
/**
* MPU Memory Access Attribute write-through, no write allocate policy.
*/
#define ARM_MPU_CACHEP_WT_NWA 2U
/**
* MPU Memory Access Attribute write-back, no write allocate policy.
*/
#define ARM_MPU_CACHEP_WB_NWA 3U
/**
* Struct for a single MPU Region
*/
typedef struct {
uint32_t RBAR; //!< The region base address register value (RBAR)
uint32_t RASR; //!< The region attribute and size register value (RASR) \ref MPU_RASR
} ARM_MPU_Region_t;
/** Enable the MPU.
* \param MPU_Control Default access permissions for unconfigured regions.
*/
__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control)
{
__DSB();
__ISB();
MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk;
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
#endif
}
/** Disable the MPU.
*/
__STATIC_INLINE void ARM_MPU_Disable(void)
{
__DSB();
__ISB();
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
#endif
MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;
}
/** Clear and disable the given MPU region.
* \param rnr Region number to be cleared.
*/
__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr)
{
MPU->RNR = rnr;
MPU->RASR = 0U;
}
/** Configure an MPU region.
* \param rbar Value for RBAR register.
* \param rsar Value for RSAR register.
*/
__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr)
{
MPU->RBAR = rbar;
MPU->RASR = rasr;
}
/** Configure the given MPU region.
* \param rnr Region number to be configured.
* \param rbar Value for RBAR register.
* \param rsar Value for RSAR register.
*/
__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr)
{
MPU->RNR = rnr;
MPU->RBAR = rbar;
MPU->RASR = rasr;
}
/** Memcopy with strictly ordered memory access, e.g. for register targets.
* \param dst Destination data is copied to.
* \param src Source data is copied from.
* \param len Amount of data words to be copied.
*/
__STATIC_INLINE void orderedCpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len)
{
uint32_t i;
for (i = 0U; i < len; ++i)
{
dst[i] = src[i];
}
}
/** Load the given number of MPU regions from a table.
* \param table Pointer to the MPU configuration table.
* \param cnt Amount of regions to be configured.
*/
__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt)
{
const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U;
while (cnt > MPU_TYPE_RALIASES) {
orderedCpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize);
table += MPU_TYPE_RALIASES;
cnt -= MPU_TYPE_RALIASES;
}
orderedCpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,301 @@
/**
******************************************************************************
* @file stm32f4xx.h
* @author MCD Application Team
* @brief CMSIS STM32F4xx Device Peripheral Access Layer Header File.
*
* The file is the unique include file that the application programmer
* is using in the C source code, usually in main.c. This file contains:
* - Configuration section that allows to select:
* - The STM32F4xx device used in the target application
* - To use or not the peripheral's drivers in application code(i.e.
* code will be based on direct access to peripheral's registers
* rather than drivers API), this option is controlled by
* "#define USE_HAL_DRIVER"
*
******************************************************************************
* @attention
*
* Copyright (c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/** @addtogroup CMSIS
* @{
*/
/** @addtogroup stm32f4xx
* @{
*/
#ifndef __STM32F4xx_H
#define __STM32F4xx_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/** @addtogroup Library_configuration_section
* @{
*/
/**
* @brief STM32 Family
*/
#if !defined (STM32F4)
#define STM32F4
#endif /* STM32F4 */
/* Uncomment the line below according to the target STM32 device used in your
application
*/
#if !defined (STM32F405xx) && !defined (STM32F415xx) && !defined (STM32F407xx) && !defined (STM32F417xx) && \
!defined (STM32F427xx) && !defined (STM32F437xx) && !defined (STM32F429xx) && !defined (STM32F439xx) && \
!defined (STM32F401xC) && !defined (STM32F401xE) && !defined (STM32F410Tx) && !defined (STM32F410Cx) && \
!defined (STM32F410Rx) && !defined (STM32F411xE) && !defined (STM32F446xx) && !defined (STM32F469xx) && \
!defined (STM32F479xx) && !defined (STM32F412Cx) && !defined (STM32F412Rx) && !defined (STM32F412Vx) && \
!defined (STM32F412Zx) && !defined (STM32F413xx) && !defined (STM32F423xx)
/* #define STM32F405xx */ /*!< STM32F405RG, STM32F405VG and STM32F405ZG Devices */
/* #define STM32F415xx */ /*!< STM32F415RG, STM32F415VG and STM32F415ZG Devices */
/* #define STM32F407xx */ /*!< STM32F407VG, STM32F407VE, STM32F407ZG, STM32F407ZE, STM32F407IG and STM32F407IE Devices */
/* #define STM32F417xx */ /*!< STM32F417VG, STM32F417VE, STM32F417ZG, STM32F417ZE, STM32F417IG and STM32F417IE Devices */
/* #define STM32F427xx */ /*!< STM32F427VG, STM32F427VI, STM32F427ZG, STM32F427ZI, STM32F427IG and STM32F427II Devices */
/* #define STM32F437xx */ /*!< STM32F437VG, STM32F437VI, STM32F437ZG, STM32F437ZI, STM32F437IG and STM32F437II Devices */
/* #define STM32F429xx */ /*!< STM32F429VG, STM32F429VI, STM32F429ZG, STM32F429ZI, STM32F429BG, STM32F429BI, STM32F429NG,
STM32F439NI, STM32F429IG and STM32F429II Devices */
/* #define STM32F439xx */ /*!< STM32F439VG, STM32F439VI, STM32F439ZG, STM32F439ZI, STM32F439BG, STM32F439BI, STM32F439NG,
STM32F439NI, STM32F439IG and STM32F439II Devices */
/* #define STM32F401xC */ /*!< STM32F401CB, STM32F401CC, STM32F401RB, STM32F401RC, STM32F401VB and STM32F401VC Devices */
/* #define STM32F401xE */ /*!< STM32F401CD, STM32F401RD, STM32F401VD, STM32F401CE, STM32F401RE and STM32F401VE Devices */
/* #define STM32F410Tx */ /*!< STM32F410T8 and STM32F410TB Devices */
/* #define STM32F410Cx */ /*!< STM32F410C8 and STM32F410CB Devices */
/* #define STM32F410Rx */ /*!< STM32F410R8 and STM32F410RB Devices */
/* #define STM32F411xE */ /*!< STM32F411CC, STM32F411RC, STM32F411VC, STM32F411CE, STM32F411RE and STM32F411VE Devices */
/* #define STM32F446xx */ /*!< STM32F446MC, STM32F446ME, STM32F446RC, STM32F446RE, STM32F446VC, STM32F446VE, STM32F446ZC,
and STM32F446ZE Devices */
/* #define STM32F469xx */ /*!< STM32F469AI, STM32F469II, STM32F469BI, STM32F469NI, STM32F469AG, STM32F469IG, STM32F469BG,
STM32F469NG, STM32F469AE, STM32F469IE, STM32F469BE and STM32F469NE Devices */
/* #define STM32F479xx */ /*!< STM32F479AI, STM32F479II, STM32F479BI, STM32F479NI, STM32F479AG, STM32F479IG, STM32F479BG
and STM32F479NG Devices */
/* #define STM32F412Cx */ /*!< STM32F412CEU and STM32F412CGU Devices */
/* #define STM32F412Zx */ /*!< STM32F412ZET, STM32F412ZGT, STM32F412ZEJ and STM32F412ZGJ Devices */
/* #define STM32F412Vx */ /*!< STM32F412VET, STM32F412VGT, STM32F412VEH and STM32F412VGH Devices */
/* #define STM32F412Rx */ /*!< STM32F412RET, STM32F412RGT, STM32F412REY and STM32F412RGY Devices */
/* #define STM32F413xx */ /*!< STM32F413CH, STM32F413MH, STM32F413RH, STM32F413VH, STM32F413ZH, STM32F413CG, STM32F413MG,
STM32F413RG, STM32F413VG and STM32F413ZG Devices */
/* #define STM32F423xx */ /*!< STM32F423CH, STM32F423RH, STM32F423VH and STM32F423ZH Devices */
#endif
/* Tip: To avoid modifying this file each time you need to switch between these
devices, you can define the device in your toolchain compiler preprocessor.
*/
#if !defined (USE_HAL_DRIVER)
/**
* @brief Comment the line below if you will not use the peripherals drivers.
In this case, these drivers will not be included and the application code will
be based on direct access to peripherals registers
*/
/*#define USE_HAL_DRIVER */
#endif /* USE_HAL_DRIVER */
/**
* @brief CMSIS version number V2.6.8
*/
#define __STM32F4xx_CMSIS_VERSION_MAIN (0x02U) /*!< [31:24] main version */
#define __STM32F4xx_CMSIS_VERSION_SUB1 (0x06U) /*!< [23:16] sub1 version */
#define __STM32F4xx_CMSIS_VERSION_SUB2 (0x08U) /*!< [15:8] sub2 version */
#define __STM32F4xx_CMSIS_VERSION_RC (0x00U) /*!< [7:0] release candidate */
#define __STM32F4xx_CMSIS_VERSION ((__STM32F4xx_CMSIS_VERSION_MAIN << 24)\
|(__STM32F4xx_CMSIS_VERSION_SUB1 << 16)\
|(__STM32F4xx_CMSIS_VERSION_SUB2 << 8 )\
|(__STM32F4xx_CMSIS_VERSION_RC))
/**
* @}
*/
/** @addtogroup Device_Included
* @{
*/
#if defined(STM32F405xx)
#include "stm32f405xx.h"
#elif defined(STM32F415xx)
#include "stm32f415xx.h"
#elif defined(STM32F407xx)
#include "stm32f407xx.h"
#elif defined(STM32F417xx)
#include "stm32f417xx.h"
#elif defined(STM32F427xx)
#include "stm32f427xx.h"
#elif defined(STM32F437xx)
#include "stm32f437xx.h"
#elif defined(STM32F429xx)
#include "stm32f429xx.h"
#elif defined(STM32F439xx)
#include "stm32f439xx.h"
#elif defined(STM32F401xC)
#include "stm32f401xc.h"
#elif defined(STM32F401xE)
#include "stm32f401xe.h"
#elif defined(STM32F410Tx)
#include "stm32f410tx.h"
#elif defined(STM32F410Cx)
#include "stm32f410cx.h"
#elif defined(STM32F410Rx)
#include "stm32f410rx.h"
#elif defined(STM32F411xE)
#include "stm32f411xe.h"
#elif defined(STM32F446xx)
#include "stm32f446xx.h"
#elif defined(STM32F469xx)
#include "stm32f469xx.h"
#elif defined(STM32F479xx)
#include "stm32f479xx.h"
#elif defined(STM32F412Cx)
#include "stm32f412cx.h"
#elif defined(STM32F412Zx)
#include "stm32f412zx.h"
#elif defined(STM32F412Rx)
#include "stm32f412rx.h"
#elif defined(STM32F412Vx)
#include "stm32f412vx.h"
#elif defined(STM32F413xx)
#include "stm32f413xx.h"
#elif defined(STM32F423xx)
#include "stm32f423xx.h"
#else
#error "Please select first the target STM32F4xx device used in your application (in stm32f4xx.h file)"
#endif
/**
* @}
*/
/** @addtogroup Exported_types
* @{
*/
typedef enum
{
RESET = 0U,
SET = !RESET
} FlagStatus, ITStatus;
typedef enum
{
DISABLE = 0U,
ENABLE = !DISABLE
} FunctionalState;
#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
typedef enum
{
SUCCESS = 0U,
ERROR = !SUCCESS
} ErrorStatus;
/**
* @}
*/
/** @addtogroup Exported_macro
* @{
*/
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))
#define READ_BIT(REG, BIT) ((REG) & (BIT))
#define CLEAR_REG(REG) ((REG) = (0x0))
#define WRITE_REG(REG, VAL) ((REG) = (VAL))
#define READ_REG(REG) ((REG))
#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL)))
/* Use of CMSIS compiler intrinsics for register exclusive access */
/* Atomic 32-bit register access macro to set one or several bits */
#define ATOMIC_SET_BIT(REG, BIT) \
do { \
uint32_t val; \
do { \
val = __LDREXW((__IO uint32_t *)&(REG)) | (BIT); \
} while ((__STREXW(val,(__IO uint32_t *)&(REG))) != 0U); \
} while(0)
/* Atomic 32-bit register access macro to clear one or several bits */
#define ATOMIC_CLEAR_BIT(REG, BIT) \
do { \
uint32_t val; \
do { \
val = __LDREXW((__IO uint32_t *)&(REG)) & ~(BIT); \
} while ((__STREXW(val,(__IO uint32_t *)&(REG))) != 0U); \
} while(0)
/* Atomic 32-bit register access macro to clear and set one or several bits */
#define ATOMIC_MODIFY_REG(REG, CLEARMSK, SETMASK) \
do { \
uint32_t val; \
do { \
val = (__LDREXW((__IO uint32_t *)&(REG)) & ~(CLEARMSK)) | (SETMASK); \
} while ((__STREXW(val,(__IO uint32_t *)&(REG))) != 0U); \
} while(0)
/* Atomic 16-bit register access macro to set one or several bits */
#define ATOMIC_SETH_BIT(REG, BIT) \
do { \
uint16_t val; \
do { \
val = __LDREXH((__IO uint16_t *)&(REG)) | (BIT); \
} while ((__STREXH(val,(__IO uint16_t *)&(REG))) != 0U); \
} while(0)
/* Atomic 16-bit register access macro to clear one or several bits */
#define ATOMIC_CLEARH_BIT(REG, BIT) \
do { \
uint16_t val; \
do { \
val = __LDREXH((__IO uint16_t *)&(REG)) & ~(BIT); \
} while ((__STREXH(val,(__IO uint16_t *)&(REG))) != 0U); \
} while(0)
/* Atomic 16-bit register access macro to clear and set one or several bits */
#define ATOMIC_MODIFYH_REG(REG, CLEARMSK, SETMASK) \
do { \
uint16_t val; \
do { \
val = (__LDREXH((__IO uint16_t *)&(REG)) & ~(CLEARMSK)) | (SETMASK); \
} while ((__STREXH(val,(__IO uint16_t *)&(REG))) != 0U); \
} while(0)
/**
* @}
*/
#if defined (USE_HAL_DRIVER)
#include "stm32f4xx_hal.h"
#endif /* USE_HAL_DRIVER */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __STM32F4xx_H */
/**
* @}
*/
/**
* @}
*/

View File

@ -0,0 +1,104 @@
/**
******************************************************************************
* @file system_stm32f4xx.h
* @author MCD Application Team
* @brief CMSIS Cortex-M4 Device System Source File for STM32F4xx devices.
******************************************************************************
* @attention
*
* Copyright (c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/** @addtogroup CMSIS
* @{
*/
/** @addtogroup stm32f4xx_system
* @{
*/
/**
* @brief Define to prevent recursive inclusion
*/
#ifndef __SYSTEM_STM32F4XX_H
#define __SYSTEM_STM32F4XX_H
#ifdef __cplusplus
extern "C" {
#endif
/** @addtogroup STM32F4xx_System_Includes
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Exported_types
* @{
*/
/* This variable is updated in three ways:
1) by calling CMSIS function SystemCoreClockUpdate()
2) by calling HAL API function HAL_RCC_GetSysClockFreq()
3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
Note: If you use this function to configure the system clock; then there
is no need to call the 2 first functions listed above, since SystemCoreClock
variable is updated automatically.
*/
extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */
extern const uint8_t AHBPrescTable[16]; /*!< AHB prescalers table values */
extern const uint8_t APBPrescTable[8]; /*!< APB prescalers table values */
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Exported_Constants
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Exported_Macros
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Exported_Functions
* @{
*/
extern void SystemInit(void);
extern void SystemCoreClockUpdate(void);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /*__SYSTEM_STM32F4XX_H */
/**
* @}
*/
/**
* @}
*/

View File

@ -0,0 +1,29 @@
ENTRY(_reset);
MEMORY {
flash(rx) : ORIGIN = 0x08000000, LENGTH = 2048k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 192k /* remaining 64k in a separate address space */
}
_estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */
SECTIONS {
.vectors : { KEEP(*(.vectors)) } > flash
.text : { *(.text*) } > flash
.rodata : { *(.rodata*) } > flash
.data : {
_sdata = .; /* for init_ram() */
*(.first_data)
*(.data SORT(.data.*))
_edata = .; /* for init_ram() */
} > sram AT > flash
_sidata = LOADADDR(.data);
.bss : {
_sbss = .; /* for init_ram() */
*(.bss SORT(.bss.*) COMMON)
_ebss = .; /* for init_ram() */
} > sram
. = ALIGN(8);
_end = .; /* for cmsis_gcc.h and init_ram() */
}

View File

@ -0,0 +1,119 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
#include "mcu.h"
#include "mongoose.h"
#include "tusb.h"
#define LED PIN('B', 7) // On-board LED pin (blue)
static uint64_t s_ticks;
static struct mip_if *s_ifp;
uint32_t SystemCoreClock = FREQ;
const uint8_t tud_network_mac_address[6] = {2, 2, 0x84, 0x6A, 0x96, 0};
static void blink_cb(void *arg) { // Blink periodically
gpio_toggle(LED);
(void) arg;
}
uint64_t mg_millis(void) { // Declare our own uptime function
return s_ticks; // Return number of milliseconds since boot
}
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}
bool tud_network_recv_cb(const uint8_t *buf, uint16_t len) {
mip_rxcb((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) {
}
void OTG_FS_IRQHandler(void) { // USB interrupt handler
tud_int_handler(0); // Pass control to TinyUSB
}
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 mip_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_up(struct mip_if *ifp) {
(void) ifp;
return tud_inited() && tud_ready() && tud_connected();
}
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/api/debug")) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
mg_log_set(level);
mg_http_reply(c, 200, "", "Debug level set to %d\n", level);
} else {
mg_http_reply(c, 200, "", "hi\n");
}
}
(void) fn_data;
}
int main(void) {
clock_init(); // Set clock
SysTick_Config(SystemCoreClock / 1000); // Defined in core_cm4.h
gpio_set_mode(LED, GPIO_MODE_OUTPUT); // Setup blue LED
uart_init(USART3, 115200); // It is wired to the debug port
struct mg_mgr mgr; // Initialise Mongoose event manager
mg_mgr_init(&mgr); // and attach it to the MIP interface
mg_log_set(MG_LL_DEBUG); // Set log level
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr);
MG_INFO(("Init TCP/IP stack ..."));
struct mip_driver driver = {.tx = usb_tx, .up = usb_up};
struct mip_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,
.queue.len = 4096};
s_ifp = &mif;
mip_init(&mgr, &mif);
mg_http_listen(&mgr, "tcp://0.0.0.0:80", fn, &mgr);
MG_INFO(("Init USB ..."));
gpio_init(PIN('A', 11), GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 10); // D+
gpio_init(PIN('A', 12), GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 10); // D-
gpio_init(PIN('A', 9), GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 0); // VBUS
gpio_init(PIN('A', 10), GPIO_MODE_AF, GPIO_OTYPE_OPEN_DRAIN, GPIO_SPEED_HIGH,
GPIO_PULL_UP, 10); // ID
RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN; // Enable USB FS clock
USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_NOVBUSSENS; // VBUS sensing disable
USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBUSBSEN; // VBUS sensing enable
tusb_init();
MG_INFO(("Init done, starting main loop ..."));
for (;;) {
mg_mgr_poll(&mgr, 0);
tud_task();
}
return 0;
}

View File

@ -0,0 +1,150 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
//
// https://www.st.com/resource/en/reference_manual/dm00031020-stm32f405-415-stm32f407-417-stm32f427-437-and-stm32f429-439-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf
// https://www.st.com/resource/en/datasheet/stm32f429zi.pdf
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "stm32f429xx.h"
#define BIT(x) (1UL << (x))
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK)
// 6.3.3: APB1 clock <= 45MHz; APB2 clock <= 90MHz
// 3.5.1, Table 11: configure flash latency (WS) in accordance to clock freq
// 33.4: The AHB clock must be at least 25 MHz when Ethernet is used
enum { APB1_PRE = 5 /* AHB clock / 4 */, APB2_PRE = 4 /* AHB clock / 2 */ };
enum { PLL_HSI = 16, PLL_M = 8, PLL_N = 168, PLL_P = 2, PLL_Q = 7 };
#define PLL_FREQ (PLL_HSI * PLL_N / PLL_M / PLL_P)
#define FLASH_LATENCY 5
#define FREQ (PLL_FREQ * 1000000) // Core 168 MHz, USB 48 MHz
static inline void spin(volatile uint32_t count) {
while (count--) asm("nop");
}
static inline void systick_init(uint32_t ticks) {
if ((ticks - 1) > 0xffffff) return; // Systick timer is 24 bit
SysTick->LOAD = ticks - 1;
SysTick->VAL = 0;
SysTick->CTRL = BIT(0) | BIT(1) | BIT(2); // Enable systick
RCC->APB2ENR |= BIT(14); // Enable SYSCFG
}
#define GPIO(bank) ((GPIO_TypeDef *) (GPIOA_BASE + 0x400U * (bank)))
enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG };
enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN };
enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE };
enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN };
static inline void gpio_set_mode(uint16_t pin, uint8_t mode) {
GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); // GPIO bank
int n = PINNO(pin); // Pin number
RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
gpio->MODER &= ~(3U << (n * 2)); // Clear existing setting
gpio->MODER |= (mode & 3U) << (n * 2); // Set new mode
}
static inline void gpio_set_af(uint16_t pin, uint8_t af_num) {
GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); // GPIO bank
int n = PINNO(pin); // Pin number
gpio->AFR[n >> 3] &= ~(15UL << ((n & 7) * 4));
gpio->AFR[n >> 3] |= ((uint32_t) af_num) << ((n & 7) * 4);
}
static inline void gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = GPIO(PINBANK(pin));
gpio->BSRR |= (1U << PINNO(pin)) << (val ? 0 : 16);
}
static inline void gpio_toggle(uint16_t pin) {
GPIO_TypeDef *gpio = GPIO(PINBANK(pin));
uint32_t mask = BIT(PINNO(pin));
gpio->BSRR |= mask << (gpio->ODR & mask ? 16 : 0);
}
static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
GPIO_TypeDef *gpio = GPIO(PINBANK(pin)); // GPIO bank
uint8_t n = (uint8_t) (PINNO(pin));
RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
SETBITS(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);
SETBITS(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));
SETBITS(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));
SETBITS(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
((uint32_t) af) << ((n & 7) * 4));
SETBITS(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));
}
#define UART1 USART1
#define UART2 USART2
#define UART3 USART3
static inline void uart_init(USART_TypeDef *uart, unsigned long baud) {
uint8_t af = 0; // Alternate function
uint16_t rx = 0, tx = 0; // pins
if (uart == UART1) RCC->APB2ENR |= BIT(4);
if (uart == UART2) RCC->APB1ENR |= BIT(17);
if (uart == UART3) RCC->APB1ENR |= BIT(18);
if (uart == UART1) af = 4, tx = PIN('A', 9), rx = PIN('A', 10);
if (uart == UART2) af = 4, tx = PIN('A', 2), rx = PIN('A', 3);
if (uart == UART3) af = 7, tx = PIN('D', 8), rx = PIN('D', 9);
gpio_set_mode(tx, GPIO_MODE_AF);
gpio_set_af(tx, af);
gpio_set_mode(rx, GPIO_MODE_AF);
gpio_set_af(rx, af);
uart->CR1 = 0; // Disable this UART
uart->BRR = FREQ / APB2_PRE / baud; // FREQ is a CPU frequency
uart->CR1 |= BIT(13) | BIT(2) | BIT(3); // Set UE, RE, TE
}
static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
uart->DR = byte;
while ((uart->SR & BIT(7)) == 0) spin(1);
}
static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++);
}
static inline int uart_read_ready(USART_TypeDef *uart) {
return uart->SR & BIT(5); // If RXNE bit is set, data is ready
}
static inline uint8_t uart_read_byte(USART_TypeDef *uart) {
return (uint8_t) (uart->DR & 255);
}
static inline bool timer_expired(uint32_t *t, uint32_t prd, uint32_t now) {
if (now + prd < *t) *t = 0; // Time wrapped? Reset timer
if (*t == 0) *t = now + prd; // Firt poll? Set expiration
if (*t > now) return false; // Not expired yet, return
*t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time
return true; // Expired, return true
}
static inline void clock_init(void) { // Set clock frequency
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
FLASH->ACR |= FLASH_LATENCY | BIT(8) | BIT(9); // Flash latency, caches
RCC->PLLCFGR &= ~((BIT(17) - 1) | (15U << 24)); // Clear PLL multipliers
RCC->PLLCFGR |= (((PLL_P - 2) / 2) & 3) << 16; // Set PLL_P
RCC->PLLCFGR |= PLL_M | (PLL_N << 6) | (PLL_Q << 24); // Set PLL_M and PLL_N
RCC->CR |= BIT(24); // Enable PLL
while ((RCC->CR & BIT(25)) == 0) spin(1); // Wait until done
RCC->CFGR = (APB1_PRE << 10) | (APB2_PRE << 13); // Set prescalers
RCC->CFGR |= 2; // Set clock source to PLL
while ((RCC->CFGR & 12) == 0) spin(1); // Wait until done
}

View File

@ -0,0 +1,2 @@
#pragma once
#define SIZEOF_ETH_HDR 14

View File

@ -0,0 +1,160 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
// Startup code
__attribute__((naked, noreturn)) void _reset(void) {
// Initialise memory
extern long _sbss, _ebss, _sdata, _edata, _sidata;
for (long *src = &_sbss; src < &_ebss; src++) *src = 0;
for (long *src = &_sdata, *dst = &_sidata; src < &_edata;) *src++ = *dst++;
// Call main()
extern void main(void);
main();
for (;;) (void) 0;
}
void __attribute__((weak)) DefaultIRQHandler(void) {
for (;;) (void) 0;
}
#define WEAK_ALIAS __attribute__((weak, alias("DefaultIRQHandler")))
WEAK_ALIAS void NMI_Handler(void);
WEAK_ALIAS void HardFault_Handler(void);
WEAK_ALIAS void MemManage_Handler(void);
WEAK_ALIAS void BusFault_Handler(void);
WEAK_ALIAS void UsageFault_Handler(void);
WEAK_ALIAS void SVC_Handler(void);
WEAK_ALIAS void DebugMon_Handler(void);
WEAK_ALIAS void PendSV_Handler(void);
WEAK_ALIAS void SysTick_Handler(void);
WEAK_ALIAS void WWDG_IRQHandler(void);
WEAK_ALIAS void PVD_IRQHandler(void);
WEAK_ALIAS void TAMP_STAMP_IRQHandler(void);
WEAK_ALIAS void RTC_WKUP_IRQHandler(void);
WEAK_ALIAS void FLASH_IRQHandler(void);
WEAK_ALIAS void RCC_IRQHandler(void);
WEAK_ALIAS void EXTI0_IRQHandler(void);
WEAK_ALIAS void EXTI1_IRQHandler(void);
WEAK_ALIAS void EXTI2_IRQHandler(void);
WEAK_ALIAS void EXTI3_IRQHandler(void);
WEAK_ALIAS void EXTI4_IRQHandler(void);
WEAK_ALIAS void EXTI9_5_IRQHandler(void);
WEAK_ALIAS void EXTI15_10_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream0_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream1_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream2_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream3_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream4_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream5_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream6_IRQHandler(void);
WEAK_ALIAS void ADC_IRQHandler(void);
WEAK_ALIAS void CAN1_TX_IRQHandler(void);
WEAK_ALIAS void CAN1_RX0_IRQHandler(void);
WEAK_ALIAS void CAN1_RX1_IRQHandler(void);
WEAK_ALIAS void CAN1_SCE_IRQHandler(void);
WEAK_ALIAS void TIM1_BRK_TIM9_IRQHandler(void);
WEAK_ALIAS void TIM1_UP_TIM10_IRQHandler(void);
WEAK_ALIAS void TIM1_TRG_COM_TIM11_IRQHandler(void);
WEAK_ALIAS void TIM1_CC_IRQHandler(void);
WEAK_ALIAS void TIM2_IRQHandler(void);
WEAK_ALIAS void TIM3_IRQHandler(void);
WEAK_ALIAS void TIM4_IRQHandler(void);
WEAK_ALIAS void I2C1_EV_IRQHandler(void);
WEAK_ALIAS void I2C1_ER_IRQHandler(void);
WEAK_ALIAS void I2C2_EV_IRQHandler(void);
WEAK_ALIAS void I2C2_ER_IRQHandler(void);
WEAK_ALIAS void SPI1_IRQHandler(void);
WEAK_ALIAS void SPI2_IRQHandler(void);
WEAK_ALIAS void USART1_IRQHandler(void);
WEAK_ALIAS void USART2_IRQHandler(void);
WEAK_ALIAS void USART3_IRQHandler(void);
WEAK_ALIAS void RTC_Alarm_IRQHandler(void);
WEAK_ALIAS void OTG_FS_WKUP_IRQHandler(void);
WEAK_ALIAS void TIM8_BRK_TIM12_IRQHandler(void);
WEAK_ALIAS void TIM8_UP_TIM13_IRQHandler(void);
WEAK_ALIAS void TIM8_TRG_COM_TIM14_IRQHandler(void);
WEAK_ALIAS void TIM8_CC_IRQHandler(void);
WEAK_ALIAS void DMA1_Stream7_IRQHandler(void);
WEAK_ALIAS void FMC_IRQHandler(void);
WEAK_ALIAS void SDMMC1_IRQHandler(void);
WEAK_ALIAS void TIM5_IRQHandler(void);
WEAK_ALIAS void SPI3_IRQHandler(void);
WEAK_ALIAS void UART4_IRQHandler(void);
WEAK_ALIAS void UART5_IRQHandler(void);
WEAK_ALIAS void TIM6_DAC_IRQHandler(void);
WEAK_ALIAS void TIM7_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream0_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream1_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream2_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream3_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream4_IRQHandler(void);
WEAK_ALIAS void ETH_IRQHandler(void);
WEAK_ALIAS void ETH_WKUP_IRQHandler(void);
WEAK_ALIAS void CAN2_TX_IRQHandler(void);
WEAK_ALIAS void CAN2_RX0_IRQHandler(void);
WEAK_ALIAS void CAN2_RX1_IRQHandler(void);
WEAK_ALIAS void CAN2_SCE_IRQHandler(void);
WEAK_ALIAS void OTG_FS_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream5_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream6_IRQHandler(void);
WEAK_ALIAS void DMA2_Stream7_IRQHandler(void);
WEAK_ALIAS void USART6_IRQHandler(void);
WEAK_ALIAS void I2C3_EV_IRQHandler(void);
WEAK_ALIAS void I2C3_ER_IRQHandler(void);
WEAK_ALIAS void OTG_HS_EP1_OUT_IRQHandler(void);
WEAK_ALIAS void OTG_HS_EP1_IN_IRQHandler(void);
WEAK_ALIAS void OTG_HS_WKUP_IRQHandler(void);
WEAK_ALIAS void OTG_HS_IRQHandler(void);
WEAK_ALIAS void DCMI_IRQHandler(void);
WEAK_ALIAS void RNG_IRQHandler(void);
WEAK_ALIAS void FPU_IRQHandler(void);
WEAK_ALIAS void UART7_IRQHandler(void);
WEAK_ALIAS void UART8_IRQHandler(void);
WEAK_ALIAS void SPI4_IRQHandler(void);
WEAK_ALIAS void SPI5_IRQHandler(void);
WEAK_ALIAS void SPI6_IRQHandler(void);
WEAK_ALIAS void SAI1_IRQHandler(void);
WEAK_ALIAS void LTDC_IRQHandler(void);
WEAK_ALIAS void LTDC_ER_IRQHandler(void);
WEAK_ALIAS void DMA2D_IRQHandler(void);
// IRQ table
extern void _estack();
__attribute__((section(".vectors"))) void (*tab[16 + 91])(void) = {
// Cortex interrupts
_estack, _reset, NMI_Handler, HardFault_Handler, MemManage_Handler,
BusFault_Handler, UsageFault_Handler, 0, 0, 0, 0, SVC_Handler,
DebugMon_Handler, 0, PendSV_Handler, SysTick_Handler,
// Interrupts from peripherals
WWDG_IRQHandler, PVD_IRQHandler, TAMP_STAMP_IRQHandler, RTC_WKUP_IRQHandler,
FLASH_IRQHandler, RCC_IRQHandler, EXTI0_IRQHandler, EXTI1_IRQHandler,
EXTI2_IRQHandler, EXTI3_IRQHandler, EXTI4_IRQHandler,
DMA1_Stream0_IRQHandler, DMA1_Stream1_IRQHandler, DMA1_Stream2_IRQHandler,
DMA1_Stream3_IRQHandler, DMA1_Stream4_IRQHandler, DMA1_Stream5_IRQHandler,
DMA1_Stream6_IRQHandler, ADC_IRQHandler, CAN1_TX_IRQHandler,
CAN1_RX0_IRQHandler, CAN1_RX1_IRQHandler, CAN1_SCE_IRQHandler,
EXTI9_5_IRQHandler, TIM1_BRK_TIM9_IRQHandler, TIM1_UP_TIM10_IRQHandler,
TIM1_TRG_COM_TIM11_IRQHandler, TIM1_CC_IRQHandler, TIM2_IRQHandler,
TIM3_IRQHandler, TIM4_IRQHandler, I2C1_EV_IRQHandler, I2C1_ER_IRQHandler,
I2C2_EV_IRQHandler, I2C2_ER_IRQHandler, SPI1_IRQHandler, SPI2_IRQHandler,
USART1_IRQHandler, USART2_IRQHandler, USART3_IRQHandler,
EXTI15_10_IRQHandler, RTC_Alarm_IRQHandler, OTG_FS_WKUP_IRQHandler,
TIM8_BRK_TIM12_IRQHandler, TIM8_UP_TIM13_IRQHandler,
TIM8_TRG_COM_TIM14_IRQHandler, TIM8_CC_IRQHandler, DMA1_Stream7_IRQHandler,
FMC_IRQHandler, SDMMC1_IRQHandler, TIM5_IRQHandler, SPI3_IRQHandler,
UART4_IRQHandler, UART5_IRQHandler, TIM6_DAC_IRQHandler, TIM7_IRQHandler,
DMA2_Stream0_IRQHandler, DMA2_Stream1_IRQHandler, DMA2_Stream2_IRQHandler,
DMA2_Stream3_IRQHandler, DMA2_Stream4_IRQHandler, ETH_IRQHandler,
ETH_WKUP_IRQHandler, CAN2_TX_IRQHandler, CAN2_RX0_IRQHandler,
CAN2_RX1_IRQHandler, CAN2_SCE_IRQHandler, OTG_FS_IRQHandler,
DMA2_Stream5_IRQHandler, DMA2_Stream6_IRQHandler, DMA2_Stream7_IRQHandler,
USART6_IRQHandler, I2C3_EV_IRQHandler, I2C3_ER_IRQHandler,
OTG_HS_EP1_OUT_IRQHandler, OTG_HS_EP1_IN_IRQHandler, OTG_HS_WKUP_IRQHandler,
OTG_HS_IRQHandler, DCMI_IRQHandler, 0, RNG_IRQHandler, FPU_IRQHandler,
UART7_IRQHandler, UART8_IRQHandler, SPI4_IRQHandler, SPI5_IRQHandler,
SPI6_IRQHandler, SAI1_IRQHandler, LTDC_IRQHandler, LTDC_ER_IRQHandler,
DMA2D_IRQHandler};

View File

@ -0,0 +1,84 @@
#include <sys/stat.h>
#include "mcu.h"
#include "tusb.h"
int _fstat(int fd, struct stat *st) {
if (fd < 0) return -1;
st->st_mode = S_IFCHR;
return 0;
}
void *_sbrk(int incr) {
extern char _end;
static unsigned char *heap = NULL;
unsigned char *prev_heap;
if (heap == NULL) heap = (unsigned char *) &_end;
prev_heap = heap;
heap += incr;
return prev_heap;
}
int _open(const char *path) {
(void) path;
return -1;
}
int _close(int fd) {
(void) fd;
return -1;
}
int _isatty(int fd) {
(void) fd;
return 1;
}
int _lseek(int fd, int ptr, int dir) {
(void) fd, (void) ptr, (void) dir;
return 0;
}
void _exit(int status) {
(void) status;
for (;;) asm volatile("BKPT #0");
}
void _kill(int pid, int sig) {
(void) pid, (void) sig;
}
int _getpid(void) {
return -1;
}
int _write(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
if (fd == 1) uart_write_buf(UART3, ptr, (size_t) len);
return -1;
}
int _read(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
int _unlink(const char *a) {
(void) a;
return -1;
}
int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
int mkdir(const char *path, mode_t mode) {
(void) path, (void) mode;
return -1;
}

View File

@ -0,0 +1,10 @@
#pragma once
#define TUD_OPT_RHPORT 0
#define CFG_TUSB_MCU OPT_MCU_STM32F4
#define CFG_TUSB_OS OPT_OS_NONE
#define CFG_TUSB_DEBUG 0
#define CFG_TUH_ENABLED 0
#define CFG_TUD_ENABLED 1
#define CFG_TUD_ENDPOINT0_SIZE 64
#define CFG_TUD_ECM_RNDIS 1

View File

@ -0,0 +1,248 @@
/*
* 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.
*
*/
#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_TOTAL
};
enum
{
#if CFG_TUD_ECM_RNDIS
CONFIG_ID_RNDIS = 0,
CONFIG_ID_ECM = 1,
#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_RNDIS_DESC_LEN)
#define ALT_CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_ECM_DESC_LEN)
#define NCM_CONFIG_TOTAL_LEN (TUD_CONFIG_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 etc ...
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x82
#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
#else
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x82
#endif
#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),
};
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),
};
#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),
};
#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,
[CONFIG_ID_ECM ] = ecm_configuration
#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<sizeof(tud_network_mac_address); i++)
{
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
}
}
else
{
// Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
// https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL;
const char* str = string_desc_arr[index];
// Cap at max char
chr_count = (uint8_t) strlen(str);
if ( chr_count > (TU_ARRAY_SIZE(_desc_str) - 1)) chr_count = TU_ARRAY_SIZE(_desc_str) - 1;
// Convert ASCII string into UTF-16
for (unsigned int i=0; i<chr_count; i++)
{
_desc_str[1+i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8 ) | (2*chr_count + 2));
return _desc_str;
}

View File

@ -68,12 +68,11 @@ int main(void) {
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr); mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr);
// Initialise Mongoose network stack // Initialise Mongoose network stack
// Specify MAC address, either set use_dhcp or enter a static config. // Specify MAC address, and IP/mask/GW in network byte order for static
// For static configuration, specify IP/mask/GW in network byte order // IP configuration. If IP/mask/GW are unset, DHCP is going to be used
struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h
struct mip_if mif = { struct mip_if mif = {
.mac = {2, 0, 1, 2, 3, 5}, .mac = {2, 0, 1, 2, 3, 5},
.use_dhcp = true,
.driver = &mip_driver_stm32, .driver = &mip_driver_stm32,
.driver_data = &driver_data, .driver_data = &driver_data,
}; };

View File

@ -43,14 +43,13 @@ static void server(void *args) {
mg_log_set(MG_LL_DEBUG); // Set log level mg_log_set(MG_LL_DEBUG); // Set log level
// Initialise Mongoose network stack // Initialise Mongoose network stack
// Specify MAC address, either set use_dhcp or enter a static config. // Specify MAC address, and IP/mask/GW in network byte order for static
// For static configuration, specify IP/mask/GW in network byte order // IP configuration. If IP/mask/GW are unset, DHCP is going to be used
MG_INFO(("Initializing Ethernet driver")); MG_INFO(("Initializing Ethernet driver"));
ethernet_init(); ethernet_init();
struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h struct mip_driver_stm32 driver_data = {.mdc_cr = 4}; // See driver_stm32.h
struct mip_if mif = { struct mip_if mif = {
.mac = {2, 0, 1, 2, 3, 5}, .mac = {2, 0, 1, 2, 3, 5},
.use_dhcp = true,
.driver = &mip_driver_stm32, .driver = &mip_driver_stm32,
.driver_data = &driver_data, .driver_data = &driver_data,
}; };

View File

@ -84,12 +84,11 @@ int main(void) {
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr); mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr);
// Initialize Mongoose network stack // Initialize Mongoose network stack
// Specify MAC address, either set use_dhcp or enter a static config. // Specify MAC address, and IP/mask/GW in network byte order for static
// For static configuration, specify IP/mask/GW in network byte order // IP configuration. If IP/mask/GW are unset, DHCP is going to be used
struct mip_driver_tm4c driver_data = {.mdc_cr = 1}; // See driver_tm4c.h struct mip_driver_tm4c driver_data = {.mdc_cr = 1}; // See driver_tm4c.h
struct mip_if mif = { struct mip_if mif = {
.mac = {2, 0, 1, 2, 3, 5}, .mac = {2, 0, 1, 2, 3, 5},
.use_dhcp = true,
.driver = &mip_driver_tm4c, .driver = &mip_driver_tm4c,
.driver_data = &driver_data, .driver_data = &driver_data,
}; };

View File

@ -56,14 +56,13 @@ static void server(void *args) {
mg_log_set(MG_LL_DEBUG); // Set log level mg_log_set(MG_LL_DEBUG); // Set log level
// Initialise Mongoose network stack // Initialise Mongoose network stack
// Specify MAC address, either set use_dhcp or enter a static config. // Specify MAC address, and IP/mask/GW in network byte order for static
// For static configuration, specify IP/mask/GW in network byte order // IP configuration. If IP/mask/GW are unset, DHCP is going to be used
MG_INFO(("Initializing Ethernet driver")); MG_INFO(("Initializing Ethernet driver"));
ethernet_init(); ethernet_init();
struct mip_driver_tm4c driver_data = {.mdc_cr = 1}; // See driver_tm4c.h struct mip_driver_tm4c driver_data = {.mdc_cr = 1}; // See driver_tm4c.h
struct mip_if mif = { struct mip_if mif = {
.mac = {2, 0, 1, 2, 3, 5}, .mac = {2, 0, 1, 2, 3, 5},
.use_dhcp = true,
.driver = &mip_driver_tm4c, .driver = &mip_driver_tm4c,
.driver_data = &driver_data, .driver_data = &driver_data,
}; };

View File

@ -1,41 +0,0 @@
#include "mip.h"
#if MG_ENABLE_MIP
// Instruction set
enum { OP_RCR, OP_RBM, OP_WCR, OP_WBM, OP_BFS, OP_BFC, OP_SRC };
static uint8_t rd(struct mip_spi *spi, uint8_t op, uint8_t addr) {
spi->begin(spi->spi);
spi->txn(spi->spi, (uint8_t) ((op << 5) | (addr & 0x1f)));
uint8_t value = spi->txn(spi->spi, 255);
if (addr & 0x80) value = spi->txn(spi->spi, 255);
spi->end(spi->spi);
return value;
}
static bool mip_driver_enc28j60_init(uint8_t *mac, void *data) {
(void) mac, (void) data;
rd((struct mip_spi *) data, OP_SRC, 0x1f);
return false;
}
static size_t mip_driver_enc28j60_tx(const void *buf, size_t len, void *data) {
(void) buf, (void) len, (void) data;
return 0;
}
static size_t mip_driver_enc28j60_rx(void *buf, size_t len, void *data) {
(void) buf, (void) len, (void) data;
return 0;
}
static bool mip_driver_enc28j60_up(void *data) {
(void) data;
return false;
}
struct mip_driver mip_driver_enc28j60 = {
mip_driver_enc28j60_init, mip_driver_enc28j60_tx, mip_driver_enc28j60_rx,
mip_driver_enc28j60_up, NULL};
#endif

View File

@ -28,8 +28,7 @@ static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static void (*s_rx)(void *, size_t, void *); // Recv callback static struct mip_if *s_ifp; // MIP interface
static void *s_rxdata; // Recv callback data
enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1 }; // PHY constants enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1 }; // PHY constants
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) { static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
@ -102,8 +101,10 @@ static int guess_mdc_cr(void) {
return result; return result;
} }
static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) { static bool mip_driver_stm32_init(struct mip_if *ifp) {
struct mip_driver_stm32 *d = (struct mip_driver_stm32 *) userdata; struct mip_driver_stm32 *d = (struct mip_driver_stm32 *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors // Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) { for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = BIT(31); // Own s_rxdesc[i][0] = BIT(31); // Own
@ -142,20 +143,15 @@ static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) {
ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
// MAC address filtering // MAC address filtering
ETH->MACA0HR = ((uint32_t) mac[5] << 8U) | mac[4]; ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
ETH->MACA0LR = (uint32_t) (mac[3] << 24) | ((uint32_t) mac[2] << 16) | ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) mac[1] << 8) | mac[0]; ((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
return true; return true;
} }
static void mip_driver_stm32_setrx(void (*rx)(void *, size_t, void *),
void *rxdata) {
s_rx = rx;
s_rxdata = rxdata;
}
static uint32_t s_txno; static uint32_t s_txno;
static size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) { static size_t mip_driver_stm32_tx(const void *buf, size_t len, struct mip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) { if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len)); MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big len = 0; // Frame is too big
@ -173,12 +169,12 @@ static size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) {
ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS
ETH->DMATPDR = 0; // and resume ETH->DMATPDR = 0; // and resume
return len; return len;
(void) userdata; (void) ifp;
} }
static bool mip_driver_stm32_up(void *userdata) { static bool mip_driver_stm32_up(struct mip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR); uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
(void) userdata; (void) ifp;
return bsr & BIT(2) ? 1 : 0; return bsr & BIT(2) ? 1 : 0;
} }
@ -195,8 +191,7 @@ void ETH_IRQHandler(void) {
uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1)); uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
// printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
// ETH->DMASR); // ETH->DMASR);
if (s_rx != NULL) mip_rxcb(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
s_rx(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_rxdata);
} }
s_rxdesc[s_rxno][0] = BIT(31); s_rxdesc[s_rxno][0] = BIT(31);
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
@ -207,6 +202,5 @@ void ETH_IRQHandler(void) {
} }
struct mip_driver mip_driver_stm32 = { struct mip_driver mip_driver_stm32 = {
mip_driver_stm32_init, mip_driver_stm32_tx, NULL, mip_driver_stm32_up, mip_driver_stm32_init, mip_driver_stm32_tx, NULL, mip_driver_stm32_up};
mip_driver_stm32_setrx};
#endif #endif

View File

@ -34,8 +34,7 @@ static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static void (*s_rx)(void *, size_t, void *); // Recv callback static struct mip_if *s_ifp; // MIP interface
static void *s_rxdata; // Recv callback data
enum { EPHY_ADDR = 0, EPHYBMCR = 0, EPHYBMSR = 1 }; // PHY constants enum { EPHY_ADDR = 0, EPHYBMCR = 0, EPHYBMSR = 1 }; // PHY constants
static inline void tm4cspin(volatile uint32_t count) { static inline void tm4cspin(volatile uint32_t count) {
@ -124,8 +123,10 @@ static int guess_mdc_cr(void) {
return result; return result;
} }
static bool mip_driver_tm4c_init(uint8_t *mac, void *userdata) { static bool mip_driver_tm4c_init(struct mip_if *ifp) {
struct mip_driver_tm4c *d = (struct mip_driver_tm4c *) userdata; struct mip_driver_tm4c *d = (struct mip_driver_tm4c *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors // Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) { for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = BIT(31); // Own s_rxdesc[i][0] = BIT(31); // Own
@ -165,23 +166,18 @@ static bool mip_driver_tm4c_init(uint8_t *mac, void *userdata) {
EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
EMAC->EMACDMAOPMODE = EMAC->EMACDMAOPMODE =
BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
EMAC->EMACADDR0H = ((uint32_t) mac[5] << 8U) | mac[4]; EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
EMAC->EMACADDR0L = (uint32_t) (mac[3] << 24) | ((uint32_t) mac[2] << 16) | EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) mac[1] << 8) | mac[0]; ((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
// NOTE(scaprile) There are 3 additional slots for filtering, disabled by // NOTE(scaprile) There are 3 additional slots for filtering, disabled by
// default. This also applies to the STM32 driver (at least for F7) // default. This also applies to the STM32 driver (at least for F7)
return true; return true;
} }
static void mip_driver_tm4c_setrx(void (*rx)(void *, size_t, void *),
void *rxdata) {
s_rx = rx;
s_rxdata = rxdata;
}
static uint32_t s_txno; static uint32_t s_txno;
static size_t mip_driver_tm4c_tx(const void *buf, size_t len, void *userdata) { static size_t mip_driver_tm4c_tx(const void *buf, size_t len, struct mip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) { if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len)); MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // fail len = 0; // fail
@ -201,12 +197,12 @@ static size_t mip_driver_tm4c_tx(const void *buf, size_t len, void *userdata) {
EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF
EMAC->EMACTXPOLLD = 0; // and resume EMAC->EMACTXPOLLD = 0; // and resume
return len; return len;
(void) userdata; (void) ifp;
} }
static bool mip_driver_tm4c_up(void *userdata) { static bool mip_driver_tm4c_up(struct mip_if *ifp) {
uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR); uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
(void) userdata; (void) ifp;
return (bmsr & BIT(2)) ? 1 : 0; return (bmsr & BIT(2)) ? 1 : 0;
} }
@ -223,8 +219,7 @@ void EMAC0_IRQHandler(void) {
uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1)); uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
// printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
// EMAC->EMACDMARIS); // EMAC->EMACDMARIS);
if (s_rx != NULL) mip_rxcb(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
s_rx(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_rxdata);
} }
s_rxdesc[s_rxno][0] = BIT(31); s_rxdesc[s_rxno][0] = BIT(31);
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
@ -235,6 +230,5 @@ void EMAC0_IRQHandler(void) {
} }
struct mip_driver mip_driver_tm4c = {mip_driver_tm4c_init, mip_driver_tm4c_tx, struct mip_driver mip_driver_tm4c = {mip_driver_tm4c_init, mip_driver_tm4c_tx,
NULL, mip_driver_tm4c_up, NULL, mip_driver_tm4c_up};
mip_driver_tm4c_setrx};
#endif #endif

View File

@ -27,8 +27,8 @@ static uint8_t w5500_r1(struct mip_spi *s, uint8_t block, uint16_t addr) { uint
static uint16_t w5500_r2(struct mip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); } static uint16_t w5500_r2(struct mip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
// clang-format on // clang-format on
static size_t w5500_rx(void *buf, size_t buflen, void *data) { static size_t w5500_rx(void *buf, size_t buflen, struct mip_if *ifp) {
struct mip_spi *s = (struct mip_spi *) data; struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len
while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable
// printf("RSR: %d\n", (int) n); // printf("RSR: %d\n", (int) n);
@ -46,8 +46,8 @@ static size_t w5500_rx(void *buf, size_t buflen, void *data) {
return r; return r;
} }
static size_t w5500_tx(const void *buf, size_t buflen, void *data) { static size_t w5500_tx(const void *buf, size_t buflen, struct mip_if *ifp) {
struct mip_spi *s = (struct mip_spi *) data; struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
uint16_t n = 0, len = (uint16_t) buflen; uint16_t n = 0, len = (uint16_t) buflen;
while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
@ -65,8 +65,8 @@ static size_t w5500_tx(const void *buf, size_t buflen, void *data) {
return len; return len;
} }
static bool w5500_init(uint8_t *mac, void *data) { static bool w5500_init(struct mip_if *ifp) {
struct mip_spi *s = (struct mip_spi *) data; struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
s->end(s->spi); s->end(s->spi);
w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80 w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80
w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset
@ -77,14 +77,13 @@ static bool w5500_init(uint8_t *mac, void *data) {
w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW
w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN
return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW
(void) mac;
} }
static bool w5500_up(void *data) { static bool w5500_up(struct mip_if *ifp) {
uint8_t phycfgr = w5500_r1((struct mip_spi *) data, W5500_CR, 0x2e); struct mip_spi *spi = (struct mip_spi *) ifp->driver_data;
uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e);
return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up) return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up)
} }
struct mip_driver mip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up, struct mip_driver mip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up};
NULL};
#endif #endif

View File

@ -259,7 +259,7 @@ static size_t ether_output(struct mip_if *ifp, size_t len) {
// size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size) // size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size)
// if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min; // if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min;
// mg_hexdump(ifp->tx.ptr, len); // mg_hexdump(ifp->tx.ptr, len);
return ifp->driver->tx(ifp->tx.ptr, len, ifp->driver_data); return ifp->driver->tx(ifp->tx.ptr, len, ifp);
} }
static void arp_ask(struct mip_if *ifp, uint32_t ip) { static void arp_ask(struct mip_if *ifp, uint32_t ip) {
@ -280,7 +280,10 @@ static void onstatechange(struct mip_if *ifp) {
if (ifp->state == MIP_STATE_READY) { if (ifp->state == MIP_STATE_READY) {
MG_INFO(("READY, IP: %I", 4, &ifp->ip)); MG_INFO(("READY, IP: %I", 4, &ifp->ip));
MG_INFO((" GW: %I", 4, &ifp->gw)); MG_INFO((" GW: %I", 4, &ifp->gw));
MG_INFO((" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000)); if (ifp->lease_expire > ifp->now) {
MG_INFO(
(" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000));
}
arp_ask(ifp, ifp->gw); arp_ask(ifp, ifp->gw);
} else if (ifp->state == MIP_STATE_UP) { } else if (ifp->state == MIP_STATE_UP) {
MG_ERROR(("Link up")); MG_ERROR(("Link up"));
@ -410,7 +413,7 @@ static void rx_icmp(struct mip_if *ifp, struct pkt *pkt) {
} }
} }
static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) { static void rx_dhcp_client(struct mip_if *ifp, struct pkt *pkt) {
uint32_t ip = 0, gw = 0, mask = 0; uint32_t ip = 0, gw = 0, mask = 0;
uint8_t *p = pkt->dhcp->options, uint8_t *p = pkt->dhcp->options,
*end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len]; *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
@ -437,6 +440,43 @@ static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) {
} }
} }
// Simple DHCP server that assigns a next IP address: ifp->ip + 1
static void rx_dhcp_server(struct mip_if *ifp, struct pkt *pkt) {
uint8_t op = 0, *p = pkt->dhcp->options,
*end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
if (end < (uint8_t *) (pkt->dhcp + 1)) return;
// struct dhcp *req = pkt->dhcp;
struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
res.yiaddr = ifp->ip;
((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
while (p + 1 < end && p[0] != 255) { // Parse options
if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type
op = p[2];
}
p += p[1] + 2;
}
if (op == 1 || op == 3) { // DHCP Discover or DHCP Request
uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK
uint8_t opts[] = {
53, 1, msg, // Message type
1, 4, 0, 0, 0, 0, // Subnet mask
54, 4, 0, 0, 0, 0, // Server ID
12, 3, 'm', 'i', 'p', // Host name: "mip"
51, 4, 255, 255, 255, 255, // Lease time
255 // End of options
};
memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6);
memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask));
memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip));
memcpy(&res.options, opts, sizeof(opts));
res.magic = pkt->dhcp->magic;
res.xid = pkt->dhcp->xid;
arp_cache_add(ifp, res.yiaddr, pkt->eth->src);
tx_udp(ifp, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr, mg_htons(68),
&res, sizeof(res));
}
}
static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt, static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
bool lsn) { bool lsn) {
struct mg_connection *c = NULL; struct mg_connection *c = NULL;
@ -671,13 +711,15 @@ static void rx_ip(struct mip_if *ifp, struct pkt *pkt) {
} else if (pkt->ip->proto == 17) { } else if (pkt->ip->proto == 17) {
pkt->udp = (struct udp *) (pkt->ip + 1); pkt->udp = (struct udp *) (pkt->ip + 1);
if (pkt->pay.len < sizeof(*pkt->udp)) return; if (pkt->pay.len < sizeof(*pkt->udp)) return;
// MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
// mg_htons(udp->dport)));
mkpay(pkt, pkt->udp + 1); mkpay(pkt, pkt->udp + 1);
if (pkt->udp->dport == mg_htons(68)) { if (pkt->udp->dport == mg_htons(68)) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1); pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
mkpay(pkt, pkt->dhcp + 1); mkpay(pkt, pkt->dhcp + 1);
rx_dhcp(ifp, pkt); rx_dhcp_client(ifp, pkt);
} else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
mkpay(pkt, pkt->dhcp + 1);
rx_dhcp_server(ifp, pkt);
} else { } else {
rx_udp(ifp, pkt); rx_udp(ifp, pkt);
} }
@ -710,7 +752,6 @@ static void rx_ip6(struct mip_if *ifp, struct pkt *pkt) {
static void mip_rx(struct mip_if *ifp, void *buf, size_t len) { static void mip_rx(struct mip_if *ifp, void *buf, size_t len) {
const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255};
// struct pkt pkt = {.raw = {.buf = (uint8_t *) buf, .len = len}};
struct pkt pkt; struct pkt pkt;
memset(&pkt, 0, sizeof(pkt)); memset(&pkt, 0, sizeof(pkt));
pkt.raw.ptr = (char *) buf; pkt.raw.ptr = (char *) buf;
@ -753,13 +794,13 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
// Handle physical interface up/down status // Handle physical interface up/down status
if (expired_1000ms && ifp->driver->up) { if (expired_1000ms && ifp->driver->up) {
bool up = ifp->driver->up(ifp->driver_data); bool up = ifp->driver->up(ifp);
bool current = ifp->state != MIP_STATE_DOWN; bool current = ifp->state != MIP_STATE_DOWN;
if (up != current) { if (up != current) {
ifp->state = up == false ? MIP_STATE_DOWN ifp->state = up == false ? MIP_STATE_DOWN
: ifp->use_dhcp ? MIP_STATE_UP : ifp->enable_dhcp_client ? MIP_STATE_UP
: MIP_STATE_READY; : MIP_STATE_READY;
if (!up && ifp->use_dhcp) ifp->ip = 0; if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
onstatechange(ifp); onstatechange(ifp);
} }
} }
@ -768,7 +809,7 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
if (ifp->ip == 0 && expired_1000ms) { if (ifp->ip == 0 && expired_1000ms) {
tx_dhcp_discover(ifp); // If IP not configured, send DHCP tx_dhcp_discover(ifp); // If IP not configured, send DHCP
} else if (ifp->use_dhcp == false && expired_1000ms && } else if (ifp->enable_dhcp_client == false && expired_1000ms && ifp->gw &&
arp_cache_find(ifp, ifp->gw) == NULL) { arp_cache_find(ifp, ifp->gw) == NULL) {
arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache
} }
@ -776,8 +817,7 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
// Read data from the network // Read data from the network
size_t len = ifp->queue.len > 0 size_t len = ifp->queue.len > 0
? q_read(&ifp->queue, (void *) ifp->rx.ptr) ? q_read(&ifp->queue, (void *) ifp->rx.ptr)
: ifp->driver->rx((void *) ifp->rx.ptr, ifp->rx.len, : ifp->driver->rx((void *) ifp->rx.ptr, ifp->rx.len, ifp);
ifp->driver_data);
qp_mark(QP_FRAMEPOPPED, (int) q_space(&ifp->queue)); qp_mark(QP_FRAMEPOPPED, (int) q_space(&ifp->queue));
mip_rx(ifp, (void *) ifp->rx.ptr, len); mip_rx(ifp, (void *) ifp->rx.ptr, len);
qp_mark(QP_FRAMEDONE, (int) q_space(&ifp->queue)); qp_mark(QP_FRAMEDONE, (int) q_space(&ifp->queue));
@ -809,8 +849,7 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
// This function executes in interrupt context, thus it should copy data // This function executes in interrupt context, thus it should copy data
// somewhere fast. Note that newlib's malloc is not thread safe, thus use // somewhere fast. Note that newlib's malloc is not thread safe, thus use
// our lock-free queue with preallocated buffer to copy data and return asap // our lock-free queue with preallocated buffer to copy data and return asap
static void on_rx(void *buf, size_t len, void *userdata) { void mip_rxcb(void *buf, size_t len, struct mip_if *ifp) {
struct mip_if *ifp = (struct mip_if *) userdata;
if (q_write(&ifp->queue, buf, len)) { if (q_write(&ifp->queue, buf, len)) {
qp_mark(QP_FRAMEPUSHED, (int) q_space(&ifp->queue)); qp_mark(QP_FRAMEPUSHED, (int) q_space(&ifp->queue));
} else { } else {
@ -821,22 +860,19 @@ static void on_rx(void *buf, size_t len, void *userdata) {
} }
void mip_init(struct mg_mgr *mgr, struct mip_if *ifp) { void mip_init(struct mg_mgr *mgr, struct mip_if *ifp) {
if (ifp->driver->init && !ifp->driver->init(ifp->mac, ifp->driver_data)) { if (ifp->driver->init && !ifp->driver->init(ifp)) {
MG_ERROR(("driver init failed")); MG_ERROR(("driver init failed"));
} else { } else {
size_t maxpktsize = 1540; size_t maxpktsize = 1540;
ifp->rx.ptr = (char *) calloc(1, maxpktsize), ifp->rx.len = maxpktsize; ifp->rx.ptr = (char *) calloc(1, maxpktsize), ifp->rx.len = maxpktsize;
ifp->tx.ptr = (char *) calloc(1, maxpktsize), ifp->tx.len = maxpktsize; ifp->tx.ptr = (char *) calloc(1, maxpktsize), ifp->tx.len = maxpktsize;
if (ifp->driver->setrx) { if (ifp->queue.len) ifp->queue.buf = (uint8_t *) calloc(1, ifp->queue.len);
ifp->queue.len = MIP_QSIZE;
ifp->queue.buf = (uint8_t *) calloc(1, ifp->queue.len);
ifp->driver->setrx(on_rx, ifp);
}
ifp->timer_1000ms = mg_millis(); ifp->timer_1000ms = mg_millis();
arp_cache_init(ifp->arp_cache, MIP_ARP_ENTRIES, 12); arp_cache_init(ifp->arp_cache, MIP_ARP_ENTRIES, 12);
mgr->priv = ifp; mgr->priv = ifp;
ifp->mgr = mgr; ifp->mgr = mgr;
mgr->extraconnsize = sizeof(struct connstate); mgr->extraconnsize = sizeof(struct connstate);
if (ifp->ip == 0) ifp->enable_dhcp_client = true;
#ifdef MIP_QPROFILE #ifdef MIP_QPROFILE
qp_init(); qp_init();
#endif #endif

View File

@ -3,13 +3,13 @@
#include "arch.h" #include "arch.h"
#include "net.h" #include "net.h"
struct mip_if; // MIP network interface
struct mip_driver { struct mip_driver {
bool (*init)(uint8_t *mac, void *data); // Initialise driver bool (*init)(struct mip_if *); // Initialise driver
size_t (*tx)(const void *, size_t, void *data); // Transmit frame size_t (*tx)(const void *, size_t, struct mip_if *); // Transmit frame
size_t (*rx)(void *buf, size_t len, void *data); // Receive frame (polling) size_t (*rx)(void *buf, size_t len, struct mip_if *); // Receive frame (poll)
bool (*up)(void *data); // Up/down status bool (*up)(struct mip_if *); // Up/down status
// Set receive callback for interrupt-driven drivers
void (*setrx)(void (*fn)(void *buf, size_t len, void *rxdata), void *rxdata);
}; };
// Receive queue - single producer, single consumer queue. Interrupt-based // Receive queue - single producer, single consumer queue. Interrupt-based
@ -27,13 +27,15 @@ struct queue {
// Network interface // Network interface
struct mip_if { struct mip_if {
uint8_t mac[6]; // MAC address. Must be set to a valid MAC uint8_t mac[6]; // MAC address. Must be set to a valid MAC
uint32_t ip, mask, gw; // IP address, mask, default gateway. Can be 0 uint32_t ip, mask, gw; // IP address, mask, default gateway
struct mg_str rx; // Output (TX) buffer struct mg_str rx; // Output (TX) buffer
struct mg_str tx; // Input (RX) buffer struct mg_str tx; // Input (RX) buffer
bool use_dhcp; // Enable DCHP bool enable_dhcp_client; // Enable DCHP client
bool enable_dhcp_server; // Enable DCHP server
struct mip_driver *driver; // Low level driver struct mip_driver *driver; // Low level driver
void *driver_data; // Driver-specific data void *driver_data; // Driver-specific data
struct mg_mgr *mgr; // Mongoose event manager struct mg_mgr *mgr; // Mongoose event manager
struct queue queue; // Set queue.len for interrupt based drivers
// Internal state, user can use it but should not change it // Internal state, user can use it but should not change it
uint64_t now; // Current time uint64_t now; // Current time
@ -46,14 +48,13 @@ struct mip_if {
#define MIP_STATE_DOWN 0 // Interface is down #define MIP_STATE_DOWN 0 // Interface is down
#define MIP_STATE_UP 1 // Interface is up #define MIP_STATE_UP 1 // Interface is up
#define MIP_STATE_READY 2 // Interface is up and has IP #define MIP_STATE_READY 2 // Interface is up and has IP
struct queue queue; // Receive queue
}; };
void mip_init(struct mg_mgr *, struct mip_if *); void mip_init(struct mg_mgr *, struct mip_if *);
void mip_free(struct mip_if *); void mip_free(struct mip_if *);
void mip_rxcb(void *buf, size_t len, struct mip_if *ifp);
extern struct mip_driver mip_driver_stm32; extern struct mip_driver mip_driver_stm32;
extern struct mip_driver mip_driver_enc28j60;
extern struct mip_driver mip_driver_w5500; extern struct mip_driver mip_driver_w5500;
extern struct mip_driver mip_driver_tm4c; extern struct mip_driver mip_driver_tm4c;

View File

@ -5922,51 +5922,6 @@ size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) {
return c->send.len; return c->send.len;
} }
#ifdef MG_ENABLE_LINES
#line 1 "mip/driver_enc28j60.c"
#endif
#if MG_ENABLE_MIP
// Instruction set
enum { OP_RCR, OP_RBM, OP_WCR, OP_WBM, OP_BFS, OP_BFC, OP_SRC };
static uint8_t rd(struct mip_spi *spi, uint8_t op, uint8_t addr) {
spi->begin(spi->spi);
spi->txn(spi->spi, (uint8_t) ((op << 5) | (addr & 0x1f)));
uint8_t value = spi->txn(spi->spi, 255);
if (addr & 0x80) value = spi->txn(spi->spi, 255);
spi->end(spi->spi);
return value;
}
static bool mip_driver_enc28j60_init(uint8_t *mac, void *data) {
(void) mac, (void) data;
rd((struct mip_spi *) data, OP_SRC, 0x1f);
return false;
}
static size_t mip_driver_enc28j60_tx(const void *buf, size_t len, void *data) {
(void) buf, (void) len, (void) data;
return 0;
}
static size_t mip_driver_enc28j60_rx(void *buf, size_t len, void *data) {
(void) buf, (void) len, (void) data;
return 0;
}
static bool mip_driver_enc28j60_up(void *data) {
(void) data;
return false;
}
struct mip_driver mip_driver_enc28j60 = {
mip_driver_enc28j60_init, mip_driver_enc28j60_tx, mip_driver_enc28j60_rx,
mip_driver_enc28j60_up, NULL};
#endif
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
#line 1 "mip/driver_stm32.c" #line 1 "mip/driver_stm32.c"
#endif #endif
@ -6000,8 +5955,7 @@ static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static void (*s_rx)(void *, size_t, void *); // Recv callback static struct mip_if *s_ifp; // MIP interface
static void *s_rxdata; // Recv callback data
enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1 }; // PHY constants enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1 }; // PHY constants
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) { static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
@ -6074,8 +6028,10 @@ static int guess_mdc_cr(void) {
return result; return result;
} }
static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) { static bool mip_driver_stm32_init(struct mip_if *ifp) {
struct mip_driver_stm32 *d = (struct mip_driver_stm32 *) userdata; struct mip_driver_stm32 *d = (struct mip_driver_stm32 *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors // Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) { for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = BIT(31); // Own s_rxdesc[i][0] = BIT(31); // Own
@ -6114,20 +6070,15 @@ static bool mip_driver_stm32_init(uint8_t *mac, void *userdata) {
ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
// MAC address filtering // MAC address filtering
ETH->MACA0HR = ((uint32_t) mac[5] << 8U) | mac[4]; ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
ETH->MACA0LR = (uint32_t) (mac[3] << 24) | ((uint32_t) mac[2] << 16) | ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) mac[1] << 8) | mac[0]; ((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
return true; return true;
} }
static void mip_driver_stm32_setrx(void (*rx)(void *, size_t, void *),
void *rxdata) {
s_rx = rx;
s_rxdata = rxdata;
}
static uint32_t s_txno; static uint32_t s_txno;
static size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) { static size_t mip_driver_stm32_tx(const void *buf, size_t len, struct mip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) { if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len)); MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // Frame is too big len = 0; // Frame is too big
@ -6145,12 +6096,12 @@ static size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) {
ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS
ETH->DMATPDR = 0; // and resume ETH->DMATPDR = 0; // and resume
return len; return len;
(void) userdata; (void) ifp;
} }
static bool mip_driver_stm32_up(void *userdata) { static bool mip_driver_stm32_up(struct mip_if *ifp) {
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR); uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
(void) userdata; (void) ifp;
return bsr & BIT(2) ? 1 : 0; return bsr & BIT(2) ? 1 : 0;
} }
@ -6167,8 +6118,7 @@ void ETH_IRQHandler(void) {
uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1)); uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
// printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
// ETH->DMASR); // ETH->DMASR);
if (s_rx != NULL) mip_rxcb(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
s_rx(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_rxdata);
} }
s_rxdesc[s_rxno][0] = BIT(31); s_rxdesc[s_rxno][0] = BIT(31);
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
@ -6179,8 +6129,7 @@ void ETH_IRQHandler(void) {
} }
struct mip_driver mip_driver_stm32 = { struct mip_driver mip_driver_stm32 = {
mip_driver_stm32_init, mip_driver_stm32_tx, NULL, mip_driver_stm32_up, mip_driver_stm32_init, mip_driver_stm32_tx, NULL, mip_driver_stm32_up};
mip_driver_stm32_setrx};
#endif #endif
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
@ -6222,8 +6171,7 @@ static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
static void (*s_rx)(void *, size_t, void *); // Recv callback static struct mip_if *s_ifp; // MIP interface
static void *s_rxdata; // Recv callback data
enum { EPHY_ADDR = 0, EPHYBMCR = 0, EPHYBMSR = 1 }; // PHY constants enum { EPHY_ADDR = 0, EPHYBMCR = 0, EPHYBMSR = 1 }; // PHY constants
static inline void tm4cspin(volatile uint32_t count) { static inline void tm4cspin(volatile uint32_t count) {
@ -6312,8 +6260,10 @@ static int guess_mdc_cr(void) {
return result; return result;
} }
static bool mip_driver_tm4c_init(uint8_t *mac, void *userdata) { static bool mip_driver_tm4c_init(struct mip_if *ifp) {
struct mip_driver_tm4c *d = (struct mip_driver_tm4c *) userdata; struct mip_driver_tm4c *d = (struct mip_driver_tm4c *) ifp->driver_data;
s_ifp = ifp;
// Init RX descriptors // Init RX descriptors
for (int i = 0; i < ETH_DESC_CNT; i++) { for (int i = 0; i < ETH_DESC_CNT; i++) {
s_rxdesc[i][0] = BIT(31); // Own s_rxdesc[i][0] = BIT(31); // Own
@ -6353,23 +6303,18 @@ static bool mip_driver_tm4c_init(uint8_t *mac, void *userdata) {
EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
EMAC->EMACDMAOPMODE = EMAC->EMACDMAOPMODE =
BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
EMAC->EMACADDR0H = ((uint32_t) mac[5] << 8U) | mac[4]; EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
EMAC->EMACADDR0L = (uint32_t) (mac[3] << 24) | ((uint32_t) mac[2] << 16) | EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) |
((uint32_t) mac[1] << 8) | mac[0]; ((uint32_t) ifp->mac[2] << 16) |
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
// NOTE(scaprile) There are 3 additional slots for filtering, disabled by // NOTE(scaprile) There are 3 additional slots for filtering, disabled by
// default. This also applies to the STM32 driver (at least for F7) // default. This also applies to the STM32 driver (at least for F7)
return true; return true;
} }
static void mip_driver_tm4c_setrx(void (*rx)(void *, size_t, void *),
void *rxdata) {
s_rx = rx;
s_rxdata = rxdata;
}
static uint32_t s_txno; static uint32_t s_txno;
static size_t mip_driver_tm4c_tx(const void *buf, size_t len, void *userdata) { static size_t mip_driver_tm4c_tx(const void *buf, size_t len, struct mip_if *ifp) {
if (len > sizeof(s_txbuf[s_txno])) { if (len > sizeof(s_txbuf[s_txno])) {
MG_ERROR(("Frame too big, %ld", (long) len)); MG_ERROR(("Frame too big, %ld", (long) len));
len = 0; // fail len = 0; // fail
@ -6389,12 +6334,12 @@ static size_t mip_driver_tm4c_tx(const void *buf, size_t len, void *userdata) {
EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF
EMAC->EMACTXPOLLD = 0; // and resume EMAC->EMACTXPOLLD = 0; // and resume
return len; return len;
(void) userdata; (void) ifp;
} }
static bool mip_driver_tm4c_up(void *userdata) { static bool mip_driver_tm4c_up(struct mip_if *ifp) {
uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR); uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
(void) userdata; (void) ifp;
return (bmsr & BIT(2)) ? 1 : 0; return (bmsr & BIT(2)) ? 1 : 0;
} }
@ -6411,8 +6356,7 @@ void EMAC0_IRQHandler(void) {
uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1)); uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
// printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0], // printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
// EMAC->EMACDMARIS); // EMAC->EMACDMARIS);
if (s_rx != NULL) mip_rxcb(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
s_rx(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_rxdata);
} }
s_rxdesc[s_rxno][0] = BIT(31); s_rxdesc[s_rxno][0] = BIT(31);
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0; if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
@ -6423,8 +6367,7 @@ void EMAC0_IRQHandler(void) {
} }
struct mip_driver mip_driver_tm4c = {mip_driver_tm4c_init, mip_driver_tm4c_tx, struct mip_driver mip_driver_tm4c = {mip_driver_tm4c_init, mip_driver_tm4c_tx,
NULL, mip_driver_tm4c_up, NULL, mip_driver_tm4c_up};
mip_driver_tm4c_setrx};
#endif #endif
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
@ -6459,8 +6402,8 @@ static uint8_t w5500_r1(struct mip_spi *s, uint8_t block, uint16_t addr) { uint
static uint16_t w5500_r2(struct mip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); } static uint16_t w5500_r2(struct mip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
// clang-format on // clang-format on
static size_t w5500_rx(void *buf, size_t buflen, void *data) { static size_t w5500_rx(void *buf, size_t buflen, struct mip_if *ifp) {
struct mip_spi *s = (struct mip_spi *) data; struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len
while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable
// printf("RSR: %d\n", (int) n); // printf("RSR: %d\n", (int) n);
@ -6478,8 +6421,8 @@ static size_t w5500_rx(void *buf, size_t buflen, void *data) {
return r; return r;
} }
static size_t w5500_tx(const void *buf, size_t buflen, void *data) { static size_t w5500_tx(const void *buf, size_t buflen, struct mip_if *ifp) {
struct mip_spi *s = (struct mip_spi *) data; struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
uint16_t n = 0, len = (uint16_t) buflen; uint16_t n = 0, len = (uint16_t) buflen;
while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
@ -6497,8 +6440,8 @@ static size_t w5500_tx(const void *buf, size_t buflen, void *data) {
return len; return len;
} }
static bool w5500_init(uint8_t *mac, void *data) { static bool w5500_init(struct mip_if *ifp) {
struct mip_spi *s = (struct mip_spi *) data; struct mip_spi *s = (struct mip_spi *) ifp->driver_data;
s->end(s->spi); s->end(s->spi);
w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80 w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80
w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset
@ -6509,16 +6452,15 @@ static bool w5500_init(uint8_t *mac, void *data) {
w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW
w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN
return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW
(void) mac;
} }
static bool w5500_up(void *data) { static bool w5500_up(struct mip_if *ifp) {
uint8_t phycfgr = w5500_r1((struct mip_spi *) data, W5500_CR, 0x2e); struct mip_spi *spi = (struct mip_spi *) ifp->driver_data;
uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e);
return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up) return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up)
} }
struct mip_driver mip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up, struct mip_driver mip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up};
NULL};
#endif #endif
#ifdef MG_ENABLE_LINES #ifdef MG_ENABLE_LINES
@ -6785,7 +6727,7 @@ static size_t ether_output(struct mip_if *ifp, size_t len) {
// size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size) // size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size)
// if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min; // if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min;
// mg_hexdump(ifp->tx.ptr, len); // mg_hexdump(ifp->tx.ptr, len);
return ifp->driver->tx(ifp->tx.ptr, len, ifp->driver_data); return ifp->driver->tx(ifp->tx.ptr, len, ifp);
} }
static void arp_ask(struct mip_if *ifp, uint32_t ip) { static void arp_ask(struct mip_if *ifp, uint32_t ip) {
@ -6806,7 +6748,10 @@ static void onstatechange(struct mip_if *ifp) {
if (ifp->state == MIP_STATE_READY) { if (ifp->state == MIP_STATE_READY) {
MG_INFO(("READY, IP: %I", 4, &ifp->ip)); MG_INFO(("READY, IP: %I", 4, &ifp->ip));
MG_INFO((" GW: %I", 4, &ifp->gw)); MG_INFO((" GW: %I", 4, &ifp->gw));
MG_INFO((" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000)); if (ifp->lease_expire > ifp->now) {
MG_INFO(
(" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000));
}
arp_ask(ifp, ifp->gw); arp_ask(ifp, ifp->gw);
} else if (ifp->state == MIP_STATE_UP) { } else if (ifp->state == MIP_STATE_UP) {
MG_ERROR(("Link up")); MG_ERROR(("Link up"));
@ -6936,7 +6881,7 @@ static void rx_icmp(struct mip_if *ifp, struct pkt *pkt) {
} }
} }
static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) { static void rx_dhcp_client(struct mip_if *ifp, struct pkt *pkt) {
uint32_t ip = 0, gw = 0, mask = 0; uint32_t ip = 0, gw = 0, mask = 0;
uint8_t *p = pkt->dhcp->options, uint8_t *p = pkt->dhcp->options,
*end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len]; *end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
@ -6963,6 +6908,43 @@ static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) {
} }
} }
// Simple DHCP server that assigns a next IP address: ifp->ip + 1
static void rx_dhcp_server(struct mip_if *ifp, struct pkt *pkt) {
uint8_t op = 0, *p = pkt->dhcp->options,
*end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
if (end < (uint8_t *) (pkt->dhcp + 1)) return;
// struct dhcp *req = pkt->dhcp;
struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
res.yiaddr = ifp->ip;
((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
while (p + 1 < end && p[0] != 255) { // Parse options
if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type
op = p[2];
}
p += p[1] + 2;
}
if (op == 1 || op == 3) { // DHCP Discover or DHCP Request
uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK
uint8_t opts[] = {
53, 1, msg, // Message type
1, 4, 0, 0, 0, 0, // Subnet mask
54, 4, 0, 0, 0, 0, // Server ID
12, 3, 'm', 'i', 'p', // Host name: "mip"
51, 4, 255, 255, 255, 255, // Lease time
255 // End of options
};
memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6);
memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask));
memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip));
memcpy(&res.options, opts, sizeof(opts));
res.magic = pkt->dhcp->magic;
res.xid = pkt->dhcp->xid;
arp_cache_add(ifp, res.yiaddr, pkt->eth->src);
tx_udp(ifp, ifp->ip, mg_htons(67), op == 1 ? ~0U : res.yiaddr, mg_htons(68),
&res, sizeof(res));
}
}
static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt, static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
bool lsn) { bool lsn) {
struct mg_connection *c = NULL; struct mg_connection *c = NULL;
@ -7197,13 +7179,15 @@ static void rx_ip(struct mip_if *ifp, struct pkt *pkt) {
} else if (pkt->ip->proto == 17) { } else if (pkt->ip->proto == 17) {
pkt->udp = (struct udp *) (pkt->ip + 1); pkt->udp = (struct udp *) (pkt->ip + 1);
if (pkt->pay.len < sizeof(*pkt->udp)) return; if (pkt->pay.len < sizeof(*pkt->udp)) return;
// MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
// mg_htons(udp->dport)));
mkpay(pkt, pkt->udp + 1); mkpay(pkt, pkt->udp + 1);
if (pkt->udp->dport == mg_htons(68)) { if (pkt->udp->dport == mg_htons(68)) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1); pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
mkpay(pkt, pkt->dhcp + 1); mkpay(pkt, pkt->dhcp + 1);
rx_dhcp(ifp, pkt); rx_dhcp_client(ifp, pkt);
} else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
mkpay(pkt, pkt->dhcp + 1);
rx_dhcp_server(ifp, pkt);
} else { } else {
rx_udp(ifp, pkt); rx_udp(ifp, pkt);
} }
@ -7236,7 +7220,6 @@ static void rx_ip6(struct mip_if *ifp, struct pkt *pkt) {
static void mip_rx(struct mip_if *ifp, void *buf, size_t len) { static void mip_rx(struct mip_if *ifp, void *buf, size_t len) {
const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255};
// struct pkt pkt = {.raw = {.buf = (uint8_t *) buf, .len = len}};
struct pkt pkt; struct pkt pkt;
memset(&pkt, 0, sizeof(pkt)); memset(&pkt, 0, sizeof(pkt));
pkt.raw.ptr = (char *) buf; pkt.raw.ptr = (char *) buf;
@ -7279,13 +7262,13 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
// Handle physical interface up/down status // Handle physical interface up/down status
if (expired_1000ms && ifp->driver->up) { if (expired_1000ms && ifp->driver->up) {
bool up = ifp->driver->up(ifp->driver_data); bool up = ifp->driver->up(ifp);
bool current = ifp->state != MIP_STATE_DOWN; bool current = ifp->state != MIP_STATE_DOWN;
if (up != current) { if (up != current) {
ifp->state = up == false ? MIP_STATE_DOWN ifp->state = up == false ? MIP_STATE_DOWN
: ifp->use_dhcp ? MIP_STATE_UP : ifp->enable_dhcp_client ? MIP_STATE_UP
: MIP_STATE_READY; : MIP_STATE_READY;
if (!up && ifp->use_dhcp) ifp->ip = 0; if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
onstatechange(ifp); onstatechange(ifp);
} }
} }
@ -7294,7 +7277,7 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
if (ifp->ip == 0 && expired_1000ms) { if (ifp->ip == 0 && expired_1000ms) {
tx_dhcp_discover(ifp); // If IP not configured, send DHCP tx_dhcp_discover(ifp); // If IP not configured, send DHCP
} else if (ifp->use_dhcp == false && expired_1000ms && } else if (ifp->enable_dhcp_client == false && expired_1000ms && ifp->gw &&
arp_cache_find(ifp, ifp->gw) == NULL) { arp_cache_find(ifp, ifp->gw) == NULL) {
arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache
} }
@ -7302,8 +7285,7 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
// Read data from the network // Read data from the network
size_t len = ifp->queue.len > 0 size_t len = ifp->queue.len > 0
? q_read(&ifp->queue, (void *) ifp->rx.ptr) ? q_read(&ifp->queue, (void *) ifp->rx.ptr)
: ifp->driver->rx((void *) ifp->rx.ptr, ifp->rx.len, : ifp->driver->rx((void *) ifp->rx.ptr, ifp->rx.len, ifp);
ifp->driver_data);
qp_mark(QP_FRAMEPOPPED, (int) q_space(&ifp->queue)); qp_mark(QP_FRAMEPOPPED, (int) q_space(&ifp->queue));
mip_rx(ifp, (void *) ifp->rx.ptr, len); mip_rx(ifp, (void *) ifp->rx.ptr, len);
qp_mark(QP_FRAMEDONE, (int) q_space(&ifp->queue)); qp_mark(QP_FRAMEDONE, (int) q_space(&ifp->queue));
@ -7335,8 +7317,7 @@ static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) {
// This function executes in interrupt context, thus it should copy data // This function executes in interrupt context, thus it should copy data
// somewhere fast. Note that newlib's malloc is not thread safe, thus use // somewhere fast. Note that newlib's malloc is not thread safe, thus use
// our lock-free queue with preallocated buffer to copy data and return asap // our lock-free queue with preallocated buffer to copy data and return asap
static void on_rx(void *buf, size_t len, void *userdata) { void mip_rxcb(void *buf, size_t len, struct mip_if *ifp) {
struct mip_if *ifp = (struct mip_if *) userdata;
if (q_write(&ifp->queue, buf, len)) { if (q_write(&ifp->queue, buf, len)) {
qp_mark(QP_FRAMEPUSHED, (int) q_space(&ifp->queue)); qp_mark(QP_FRAMEPUSHED, (int) q_space(&ifp->queue));
} else { } else {
@ -7347,22 +7328,19 @@ static void on_rx(void *buf, size_t len, void *userdata) {
} }
void mip_init(struct mg_mgr *mgr, struct mip_if *ifp) { void mip_init(struct mg_mgr *mgr, struct mip_if *ifp) {
if (ifp->driver->init && !ifp->driver->init(ifp->mac, ifp->driver_data)) { if (ifp->driver->init && !ifp->driver->init(ifp)) {
MG_ERROR(("driver init failed")); MG_ERROR(("driver init failed"));
} else { } else {
size_t maxpktsize = 1540; size_t maxpktsize = 1540;
ifp->rx.ptr = (char *) calloc(1, maxpktsize), ifp->rx.len = maxpktsize; ifp->rx.ptr = (char *) calloc(1, maxpktsize), ifp->rx.len = maxpktsize;
ifp->tx.ptr = (char *) calloc(1, maxpktsize), ifp->tx.len = maxpktsize; ifp->tx.ptr = (char *) calloc(1, maxpktsize), ifp->tx.len = maxpktsize;
if (ifp->driver->setrx) { if (ifp->queue.len) ifp->queue.buf = (uint8_t *) calloc(1, ifp->queue.len);
ifp->queue.len = MIP_QSIZE;
ifp->queue.buf = (uint8_t *) calloc(1, ifp->queue.len);
ifp->driver->setrx(on_rx, ifp);
}
ifp->timer_1000ms = mg_millis(); ifp->timer_1000ms = mg_millis();
arp_cache_init(ifp->arp_cache, MIP_ARP_ENTRIES, 12); arp_cache_init(ifp->arp_cache, MIP_ARP_ENTRIES, 12);
mgr->priv = ifp; mgr->priv = ifp;
ifp->mgr = mgr; ifp->mgr = mgr;
mgr->extraconnsize = sizeof(struct connstate); mgr->extraconnsize = sizeof(struct connstate);
if (ifp->ip == 0) ifp->enable_dhcp_client = true;
#ifdef MIP_QPROFILE #ifdef MIP_QPROFILE
qp_init(); qp_init();
#endif #endif

View File

@ -893,6 +893,10 @@ uint64_t mg_millis(void);
#define mg_htons(x) mg_ntohs(x) #define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x) #define mg_htonl(x) mg_ntohl(x)
#define MG_U32(a, b, c, d) \
(((uint32_t) ((a) &255) << 24) | ((uint32_t) ((b) &255) << 16) | \
((uint32_t) ((c) &255) << 8) | (uint32_t) ((d) &255))
// Linked list management macros // Linked list management macros
#define LIST_ADD_HEAD(type_, head_, elem_) \ #define LIST_ADD_HEAD(type_, head_, elem_) \
do { \ do { \
@ -1419,13 +1423,13 @@ void mg_rpc_list(struct mg_rpc_req *r);
struct mip_if; // MIP network interface
struct mip_driver { struct mip_driver {
bool (*init)(uint8_t *mac, void *data); // Initialise driver bool (*init)(struct mip_if *); // Initialise driver
size_t (*tx)(const void *, size_t, void *data); // Transmit frame size_t (*tx)(const void *, size_t, struct mip_if *); // Transmit frame
size_t (*rx)(void *buf, size_t len, void *data); // Receive frame (polling) size_t (*rx)(void *buf, size_t len, struct mip_if *); // Receive frame (poll)
bool (*up)(void *data); // Up/down status bool (*up)(struct mip_if *); // Up/down status
// Set receive callback for interrupt-driven drivers
void (*setrx)(void (*fn)(void *buf, size_t len, void *rxdata), void *rxdata);
}; };
// Receive queue - single producer, single consumer queue. Interrupt-based // Receive queue - single producer, single consumer queue. Interrupt-based
@ -1443,13 +1447,15 @@ struct queue {
// Network interface // Network interface
struct mip_if { struct mip_if {
uint8_t mac[6]; // MAC address. Must be set to a valid MAC uint8_t mac[6]; // MAC address. Must be set to a valid MAC
uint32_t ip, mask, gw; // IP address, mask, default gateway. Can be 0 uint32_t ip, mask, gw; // IP address, mask, default gateway
struct mg_str rx; // Output (TX) buffer struct mg_str rx; // Output (TX) buffer
struct mg_str tx; // Input (RX) buffer struct mg_str tx; // Input (RX) buffer
bool use_dhcp; // Enable DCHP bool enable_dhcp_client; // Enable DCHP client
bool enable_dhcp_server; // Enable DCHP server
struct mip_driver *driver; // Low level driver struct mip_driver *driver; // Low level driver
void *driver_data; // Driver-specific data void *driver_data; // Driver-specific data
struct mg_mgr *mgr; // Mongoose event manager struct mg_mgr *mgr; // Mongoose event manager
struct queue queue; // Set queue.len for interrupt based drivers
// Internal state, user can use it but should not change it // Internal state, user can use it but should not change it
uint64_t now; // Current time uint64_t now; // Current time
@ -1462,14 +1468,13 @@ struct mip_if {
#define MIP_STATE_DOWN 0 // Interface is down #define MIP_STATE_DOWN 0 // Interface is down
#define MIP_STATE_UP 1 // Interface is up #define MIP_STATE_UP 1 // Interface is up
#define MIP_STATE_READY 2 // Interface is up and has IP #define MIP_STATE_READY 2 // Interface is up and has IP
struct queue queue; // Receive queue
}; };
void mip_init(struct mg_mgr *, struct mip_if *); void mip_init(struct mg_mgr *, struct mip_if *);
void mip_free(struct mip_if *); void mip_free(struct mip_if *);
void mip_rxcb(void *buf, size_t len, struct mip_if *ifp);
extern struct mip_driver mip_driver_stm32; extern struct mip_driver mip_driver_stm32;
extern struct mip_driver mip_driver_enc28j60;
extern struct mip_driver mip_driver_w5500; extern struct mip_driver mip_driver_w5500;
extern struct mip_driver mip_driver_tm4c; extern struct mip_driver mip_driver_tm4c;

View File

@ -14,6 +14,10 @@ uint64_t mg_millis(void);
#define mg_htons(x) mg_ntohs(x) #define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x) #define mg_htonl(x) mg_ntohl(x)
#define MG_U32(a, b, c, d) \
(((uint32_t) ((a) &255) << 24) | ((uint32_t) ((b) &255) << 16) | \
((uint32_t) ((c) &255) << 8) | (uint32_t) ((d) &255))
// Linked list management macros // Linked list management macros
#define LIST_ADD_HEAD(type_, head_, elem_) \ #define LIST_ADD_HEAD(type_, head_, elem_) \
do { \ do { \

View File

@ -1,21 +1,21 @@
static bool mock_init(uint8_t *mac, void *data) { static bool mock_init(struct mip_if *ifp) {
(void) mac, (void) data; (void) ifp;
return true; return true;
} }
static size_t mock_tx(const void *buf, size_t len, void *data) { static size_t mock_tx(const void *buf, size_t len, struct mip_if *ifp) {
(void) buf, (void) len, (void) data; (void) buf, (void) len, (void) ifp;
return len; return len;
} }
static size_t mock_rx(void *buf, size_t len, void *data) { static size_t mock_rx(void *buf, size_t len, struct mip_if *ifp) {
(void) buf, (void) len, (void) data; (void) buf, (void) len, (void) ifp;
return 0; return 0;
} }
static bool mock_up(void *data) { static bool mock_up(struct mip_if *ifp) {
(void) data; (void) ifp;
return true; return true;
} }
struct mip_driver mip_driver_mock = {mock_init, mock_tx, mock_rx, mock_up, 0}; struct mip_driver mip_driver_mock = {mock_init, mock_tx, mock_rx, mock_up};

View File

@ -29,15 +29,15 @@ static int s_num_tests = 0;
} while (0) } while (0)
// MIP TUNTAP driver // MIP TUNTAP driver
static size_t tap_rx(void *buf, size_t len, void *userdata) { static size_t tap_rx(void *buf, size_t len, struct mip_if *ifp) {
ssize_t received = read(*(int *) userdata, buf, len); ssize_t received = read(*(int *) ifp->driver_data, buf, len);
usleep(1); // This is to avoid 100% CPU usleep(1); // This is to avoid 100% CPU
if (received < 0) return 0; if (received < 0) return 0;
return (size_t) received; return (size_t) received;
} }
static size_t tap_tx(const void *buf, size_t len, void *userdata) { static size_t tap_tx(const void *buf, size_t len, struct mip_if *ifp) {
ssize_t res = write(*(int *) userdata, buf, len); ssize_t res = write(*(int *) ifp->driver_data, buf, len);
if (res < 0) { if (res < 0) {
MG_ERROR(("tap_tx failed: %d", errno)); MG_ERROR(("tap_tx failed: %d", errno));
return 0; return 0;
@ -45,8 +45,8 @@ static size_t tap_tx(const void *buf, size_t len, void *userdata) {
return (size_t) res; return (size_t) res;
} }
static bool tap_up(void *userdata) { static bool tap_up(struct mip_if *ifp) {
return userdata ? true : false; return ifp->driver_data ? true : false;
} }
// HTTP fetches IOs // HTTP fetches IOs
@ -174,9 +174,7 @@ static void test_http_fetch(void) {
mif.driver_data = &fd; mif.driver_data = &fd;
#if MG_USING_DHCP == 1 #if MG_USING_DHCP == 1
mif.use_dhcp = true; // DHCP
#else #else
mif.use_dhcp = false; // Static IP
mif.ip = 0x0220a8c0; // 192.168.32.2 // Triggering a network failure mif.ip = 0x0220a8c0; // 192.168.32.2 // Triggering a network failure
mif.mask = 0x00ffffff; // 255.255.255.0 mif.mask = 0x00ffffff; // 255.255.255.0
mif.gw = 0x0120a8c0; // 192.168.32.1 mif.gw = 0x0120a8c0; // 192.168.32.1